Fifteen high-traffic exports gain @example, @param, and @returns tags: defineLofiApp and LofiAppConfig, bootLofi, the Preact hooks and components, the two-client testing fixture and readiness wait, and the access templates. Examples are adapted from the reference app and the user guides so they match real usage.
- commit
- fa6930656f8cab8250a20ec86e658814707fb5c0
- parent
- 0041cc696ef65b4a718d417659590118fb12e196
- author
- Dami <dami.etoile@gmail.com>
| M | package/ | 82 | +++++++++++++++++++++++++++++- |
| M | package/ | 15 | +++++- |
| M | package/ | 29 | ++++++++++- |
| M | package/ | 14 | +++++- |
| M | package/ | 47 | ++++++++++++++++- |
| M | package/ | 32 | +++++++++++- |
| M | package/ | 19 | +++++++- |
| M | package/ | 18 | +++++++ |
| M | package/ | 15 | +++++ |
@@ -27,12 +27,43 @@ export type GroupAccessTemplate = {
2727 /** Any built-in access policy template accepted by {@link defineAccessPolicies}. */2828 export type AccessTemplate = PrivateAccessTemplate | SharedAccessTemplate | GroupAccessTemplate;2929 30-/** Declares owner-only read and mutation policy for one resource table. */30+/**31+ * Declares owner-only read and mutation policy for one resource table.32+ *33+ * @example34+ * ```ts35+ * import { defineAccessPolicies, privateAccess } from "@nzip/lofi/access";36+ * import { app } from "./schema.ts";37+ *38+ * export default defineAccessPolicies(app, [privateAccess({ resource: app.notes })]);39+ * ```40+ *41+ * @param config The resource table whose rows are visible and mutable only to their creator.42+ * @returns A template for {@link defineAccessPolicies}.43+ */3144 export function privateAccess(config: { resource: AccessTable }): PrivateAccessTemplate {3245 return { kind: "private", ...config };3346 }3447 35-/** Declares owner plus explicit read/edit grants for one resource table. */48+/**49+ * Declares owner plus explicit read/edit grants for one resource table.50+ *51+ * The grant table must be declared with the `sharedGrantTable` helper (or match52+ * its column shape) and reference the resource table.53+ *54+ * @example55+ * ```ts56+ * import { defineAccessPolicies, sharedAccess } from "@nzip/lofi/access";57+ * import { app } from "./schema.ts";58+ *59+ * export default defineAccessPolicies(app, [60+ * sharedAccess({ resource: app.notes, grants: app.noteGrants }),61+ * ]);62+ * ```63+ *64+ * @param config The shared resource table and its grant table.65+ * @returns A template for {@link defineAccessPolicies}.66+ */3667 export function sharedAccess(config: {3768 resource: AccessTable;3869 grants: AccessTable;@@ -40,7 +71,30 @@ export function sharedAccess(config: {
4071 return { kind: "shared", ...config };4172 }4273 43-/** Declares fixed-role membership policy for one or more group-owned resources. */74+/**75+ * Declares fixed-role membership policy for one or more group-owned resources.76+ *77+ * The membership table must be declared with the `groupMembershipTable` helper78+ * (or match its column shape), and each resource table must carry the `groupId`79+ * column referencing the group table.80+ *81+ * @example82+ * ```ts83+ * import { defineAccessPolicies, groupAccess } from "@nzip/lofi/access";84+ * import { app } from "./schema.ts";85+ *86+ * export default defineAccessPolicies(app, [groupAccess({87+ * groups: app.workspaces,88+ * members: app.workspaceMembers,89+ * resources: app.documents,90+ * groupId: "workspaceId",91+ * })]);92+ * ```93+ *94+ * @param config The group table, membership table, group-owned resource table(s), and the95+ * resource column that references the group.96+ * @returns A template for {@link defineAccessPolicies}.97+ */4498 export function groupAccess(config: {4599 groups: AccessTable;46100 members: AccessTable;@@ -133,6 +187,28 @@ export type RawAccessPolicyExtension = (context: RawAccessPolicyContext) => void
133187 /**134188 * Compiles the three narrow templates through Jazz's own policy builder. The135189 * optional callback is the raw Jazz-policy escape hatch for app-specific rules.190+ *191+ * @example192+ * ```ts193+ * import { defineAccessPolicies, groupAccess, sharedAccess } from "@nzip/lofi/access";194+ * import { app } from "./schema.ts";195+ *196+ * export default defineAccessPolicies(app, [197+ * sharedAccess({ resource: app.notes, grants: app.noteGrants }),198+ * groupAccess({199+ * groups: app.workspaces,200+ * members: app.workspaceMembers,201+ * resources: app.documents,202+ * groupId: "workspaceId",203+ * }),204+ * ]);205+ * ```206+ *207+ * @param app The raw Jazz app returned by `s.defineApp`.208+ * @param templates At least one {@link privateAccess}, {@link sharedAccess}, or209+ * {@link groupAccess} template; every table needs a policy.210+ * @param raw Optional raw-policy callback for app-specific rules beyond the templates.211+ * @returns Compiled permissions suitable as the app's default policy export.136212 */137213 export function defineAccessPolicies<TApp extends object>(138214 app: TApp,@@ -33,7 +33,20 @@ function Row({ label, value }: { label: string; value: string }): JSX.Element {
3333 3434 const available = (present: boolean) => (present ? "available" : "missing");3535 36-/** Renders live storage, sync, auth, and PWA capability diagnostics. */36+/**37+ * Renders live storage, sync, auth, and PWA capability diagnostics.38+ *39+ * @example40+ * ```tsx41+ * import { DeviceStatus } from "@nzip/lofi/preact";42+ *43+ * export function SettingsPage() {44+ * return <DeviceStatus />;45+ * }46+ * ```47+ *48+ * @returns The device diagnostics panel, grouped by owning subsystem.49+ */3750 export default function DeviceStatus(): VNode {3851 const { report, requestPersistence } = useDeviceCapabilities();3952 const [pwa, setPwa] = useState<PwaState>(getPwaState());@@ -10,7 +10,20 @@ import {
1010 export { pwaFailureMessage } from "../runtime/pwa.ts";1111 export type { PwaController, PwaFailureCode, PwaState, PwaUpdateState } from "../runtime/pwa.ts";1212 13-/** Subscribes a Preact component to an isolated or shared PWA controller. */13+/**14+ * Subscribes a Preact component to an isolated or shared PWA controller.15+ *16+ * @example17+ * ```tsx18+ * import { usePwaState } from "@nzip/lofi/preact";19+ *20+ * const pwa = usePwaState();21+ * const updateReady = pwa.update === "ready";22+ * ```23+ *24+ * @param controller The controller to observe; defaults to the package-wide PWA controller.25+ * @returns The current install/update state, kept live via subscription.26+ */1427 export function usePwaState(controller: PwaController = pwaController): PwaState {1528 const [state, setState] = useState<PwaState>(controller.getState());1629 useEffect(() => controller.subscribe(setState), [controller]);@@ -25,7 +38,19 @@ export interface PwaActionsProps {
2538 readonly title?: string;2639 }2740 28-/** A composable install/update surface that keeps browser event handling package-owned. */41+/**42+ * A composable install/update surface that keeps browser event handling package-owned.43+ *44+ * @example45+ * ```tsx46+ * import { PwaActions } from "@nzip/lofi/preact";47+ *48+ * <PwaActions title="Install this app" />;49+ * ```50+ *51+ * @param props An optional controller override and heading text.52+ * @returns The install/update section, or `null` when no action or status is relevant.53+ */2954 export function PwaActions({3055 controller = pwaController,3156 title = "Install & updates",@@ -10,7 +10,19 @@ export type RuntimeRecoveryProps = {
1010 reload?: () => void;1111 };1212 13-/** Renders recovery only when another tab is running an incompatible broker version. */13+/**14+ * Renders recovery only when another tab is running an incompatible broker version.15+ *16+ * @example17+ * ```tsx18+ * import { RuntimeRecovery } from "@nzip/lofi/preact";19+ *20+ * <RuntimeRecovery failure={startupFailure} />;21+ * ```22+ *23+ * @param props The startup failure to inspect and an optional reload override.24+ * @returns The recovery prompt, or `null` when no incompatible-broker failure is present.25+ */1426 export function RuntimeRecovery({ failure, reload }: RuntimeRecoveryProps): JSX.Element | null {1527 if (failure?.code !== "broker-incompatible") return null;1628 return (@@ -18,7 +18,30 @@ const loading = <T extends TableRow>(): LiveQuerySnapshot<T> => ({
1818 error: null,1919 });2020 21-/** Subscribes a Preact component to any typed Jazz query. */21+/**22+ * Subscribes a Preact component to any typed Jazz query.23+ *24+ * Equivalent mounted queries share one Jazz subscription. The snapshot preserves25+ * the exact row type produced by the builder, including `select` and `include`26+ * projections. An empty `rows` array with `status: "ready"` is an empty result,27+ * not a loading signal.28+ *29+ * @example30+ * ```ts31+ * import { useLiveQuery } from "@nzip/lofi/preact";32+ * import { app } from "../app.ts";33+ *34+ * const records = useLiveQuery(35+ * () => app.schema.records.where({ workspaceId, archived: false }),36+ * [workspaceId],37+ * );38+ * // records.status is "loading" | "ready" | "error"; records.rows is typed.39+ * ```40+ *41+ * @param createQuery Builds the typed Jazz query; re-invoked when dependencies change.42+ * @param dependencies Values that, when changed, release the query and open a replacement.43+ * @returns The live snapshot: `status`, exact typed `rows`, and `error`.44+ */2245 export function useLiveQuery<T extends TableRow>(2346 createQuery: () => QueryBuilder<T>,2447 dependencies: readonly unknown[],@@ -53,7 +76,27 @@ export type TableMutations<T extends TableRow, Init> = TableMutationSnapshot & {
5376 remove(id: string): Promise<void>;5477 };5578 56-/** Binds one typed Jazz table to stable insert, update, and remove methods. */79+/**80+ * Binds one typed Jazz table to stable insert, update, and remove methods.81+ *82+ * Mutation promises resolve after local durability; when managed sync is active,83+ * global confirmation continues in the background and updates the shared84+ * table-scoped `pending`, `durability`, and `error` state.85+ *86+ * @example87+ * ```ts88+ * import { useTableMutations } from "@nzip/lofi/preact";89+ * import { app } from "../app.ts";90+ *91+ * const records = useTableMutations(app.schema.records);92+ * const created = await records.insert({ title: "Release notes", archived: false });93+ * await records.update(created.id, { archived: true });94+ * await records.remove(created.id);95+ * ```96+ *97+ * @param table The typed schema table to mutate, e.g. `app.schema.records`.98+ * @returns Stable `insert`, `update`, and `remove` methods plus the shared mutation state.99+ */57100 export function useTableMutations<T extends TableRow, Init>(58101 table: TableProxy<T, Init>,59102 ): TableMutations<T, Init> {@@ -1,4 +1,17 @@
1-/** Author-owned configuration consumed by the package runtime. */1+/**2+ * Author-owned configuration consumed by the package runtime.3+ *4+ * @example5+ * ```ts6+ * const config: LofiAppConfig<typeof schema> = {7+ * name: "my-app",8+ * databaseName: "my-app",9+ * schema,10+ * storage: "durable",11+ * sync: { adapter: "jazz" },12+ * };13+ * ```14+ */215 export type LofiAppConfig<Schema = unknown> = {316 name: string;417 databaseName: string;@@ -38,6 +51,23 @@ export class LofiConfigurationError extends Error {
3851 *3952 * The returned value retains the exact schema type so author code can select4053 * tables without importing or editing framework internals.54+ *55+ * 56+ * ```ts57+ * import { defineLofiApp } from "@nzip/lofi";58+ * import { app as schema } from "./schema.ts";59+ *60+ * export const app = defineLofiApp({61+ * name: "my-app",62+ * databaseName: "my-app",63+ * schema,64+ * storage: "durable",65+ * sync: { adapter: "jazz" },66+ * });67+ * ```68+ *69+ * config The author-owned app configuration, including the raw Jazz schema.70+ * The same configuration, typed to the exact schema for table selection.4171 */4272 export function defineLofiApp<const Schema>(4373 config: LofiAppConfig<Schema>,@@ -3,7 +3,24 @@ import { registerProductionServiceWorker } from "./pwa.ts";
33 44 let booted = false;55 6-/** Starts lofi's browser lifecycle once for the current document. */6+/**7+ * Starts lofi's browser lifecycle once for the current document.8+ *9+ * Import the module that calls `defineLofiApp` first so the runtime can resolve10+ * the app definition. Safe to call more than once; later calls are no-ops.11+ *12+ * @example13+ * ```ts14+ * import { bootLofi } from "@nzip/lofi";15+ * // Importing app.ts registers defineLofiApp before the runtime boots.16+ * import { app } from "../app.ts";17+ *18+ * void app;19+ * void bootLofi();20+ * ```21+ *22+ * @returns A promise that resolves once boot has been scheduled for this document.23+ */724 export async function bootLofi(): Promise<void> {825 if (booted || typeof document === "undefined") return;926 booted = true;@@ -380,6 +380,24 @@ export class TwoClientFixture {
380380 * Launch (or reuse) a browser and build a { TwoClientFixture}: opens both381381 * clients at `baseURL`, applies the chosen identity mode with state kept in382382 * memory, and starts recording. Cleans up on any setup failure.383+ *384+ * 385+ * ```ts386+ * const fixture = await createTwoClientFixture({387+ * baseURL,388+ * identity: { mode: "shared", preparePrimary: (client) => ready(client) },389+ * artifacts: { directory: "test-results" },390+ * });391+ * try {392+ * await fixture.goOffline();393+ * // ...apply edits on fixture.first and fixture.second, then converge...394+ * } finally {395+ * await fixture.close();396+ * }397+ * ```398+ *399+ * options The base URL, identity mode, and optional browser, context, and artifact settings.400+ * A ready fixture whose two clients are open at `baseURL` with identity applied.383401 */384402 export async function createTwoClientFixture(385403 options: TwoClientFixtureOptions,@@ -28,6 +28,21 @@ export class ReadinessError extends Error {
2828 /**2929 * Wait for an app-owned browser predicate. The predicate runs in the page and3030 * Playwright owns the timeout/polling, so tests do not need arbitrary sleeps.31+ *32+ * @example33+ * ```ts34+ * import { waitForReady } from "@nzip/lofi/testing";35+ *36+ * await waitForReady(client.page, () => document.querySelector(".task-list") !== null, undefined, {37+ * description: "task list rendered",38+ * });39+ * ```40+ *41+ * @param page The Playwright page to poll.42+ * @param predicate An app-owned readiness check evaluated inside the page.43+ * @param argument A serializable value passed to the predicate in the page.44+ * @param options Optional description, timeout, and polling configuration.45+ * @returns Resolves when the predicate becomes true; rejects with {@link ReadinessError} on timeout.3146 */3247 export async function waitForReady<Argument>(3348 page: Page,