clone

a deno framework for local-first web apps that open instantly, work offline and sync when users choose

feat/prf-provision-custody
lofi / package / runtime / app.ts
app.ts 2.8K
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 */15export type LofiAppConfig<Schema = unknown> = {16 name: string;17 databaseName: string;18 schema: Schema;19 storage: "durable";20 credentialOrigins?: readonly string[];21 /**22 * Recoverable passkeys are scoped to this relying-party ID. Pin the canonical23 * production hostname before users create backups. When omitted, the current24 * hostname is used, which is convenient locally but preview-host specific.25 */26 passkey?: { rpId?: string };27 sync: { adapter: "jazz" };28 repositoryUrl?: string;29};3031type AppSlot = { config?: LofiAppConfig };3233const slotName = "__LOFI_APP_DEFINITION__";34const browserGlobal = globalThis as typeof globalThis & { [slotName]?: AppSlot };3536function slot(): AppSlot {37 browserGlobal[slotName] ??= {};38 return browserGlobal[slotName];39}4041/** Raised when package runtime startup cannot resolve valid author-owned app configuration. */42export class LofiConfigurationError extends Error {43 /** Stable error class name for diagnostics and error boundaries. */44 override readonly name = "LofiConfigurationError";45 /** Stable category for runtime startup classification. */46 readonly code = "configuration-error";47}4849/**50 * Defines the application-facing values the versioned runtime needs.51 *52 * The returned value retains the exact schema type so author code can select53 * tables without importing or editing framework internals.54 *55 * @example56 * ```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 * @param config The author-owned app configuration, including the raw Jazz schema.70 * @returns The same configuration, typed to the exact schema for table selection.71 */72export function defineLofiApp<const Schema>(73 config: LofiAppConfig<Schema>,74): LofiAppConfig<Schema> {75 if (!config.name.trim()) throw new LofiConfigurationError("lofi app name must not be empty");76 if (!config.databaseName.trim()) {77 throw new LofiConfigurationError("lofi database name must not be empty");78 }79 slot().config = config;80 return config;81}8283/** Package-internal lookup used when browser resources are opened lazily. */84export function getLofiApp(): LofiAppConfig {85 const config = slot().config;86 if (!config) {87 throw new LofiConfigurationError(88 "lofi app is not defined; export defineLofiApp({...}) from src/app.ts before booting the runtime",89 );90 }91 return config;92}