clone

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

feat/184-task-notice-migration
lofi / package / runtime / boot.ts
boot.ts 3.2K
1/// <reference path="./env.d.ts" />2// Package-owned browser boot orchestration.3import { getLofiApp } from "./app.ts";4import { restoreDeclaredSink } from "./data-sink.ts";5import {6 attachPwaUpdateCoordination,7 registerProductionServiceWorker,8 resolvePwaResources,9} from "./pwa.ts";10import { mountDefaultCompatBanner, schemaCompatGate } from "./schema-compat.ts";11import { mountDefaultForkNotice, storageForkGuard } from "./storage-fork.ts";12import { configureUpgradeCoordinator } from "./upgrade-coordination.ts";1314let booted = false;1516// The compatibility gate and the cross-tab upgrade coordination are wired17// before the runtime opens: the gate's verdict must be able to refuse the18// first write, and sibling tabs must hear an upgrade announcement no matter19// which module triggers it.20function startCompatibilityGate(): void {21 schemaCompatGate.start();22 mountDefaultCompatBanner();23 if (!import.meta.env.PROD) return;24 const deploymentBaseUrl = new URL(import.meta.env.BASE_URL, document.location.origin).href;25 const coordinator = configureUpgradeCoordinator(resolvePwaResources(deploymentBaseUrl).scope);26 attachPwaUpdateCoordination({27 prepareUpdateSwap: () => coordinator.announceUpgrade(),28 staleTabBehavior: () => {29 try {30 return getLofiApp().pwa?.staleTabs ?? "reload";31 } catch {32 return "reload";33 }34 },35 onStaleTab: () => {36 // The swap this tab sat out is over; resume writes long enough for the37 // gate to flip the tab read-only with its reload prompt.38 coordinator.notifyActivation();39 schemaCompatGate.markStaleTab();40 },41 });42}4344/**45 * Starts lofi's browser lifecycle once for the current document.46 *47 * Import the module that calls `defineLofiApp` first so the runtime can resolve48 * the app definition. Safe to call more than once; later calls are no-ops.49 *50 * @example51 * ```ts52 * import { bootLofi } from "@nzip/lofi";53 * // Importing app.ts registers defineLofiApp before the runtime boots.54 * import { app } from "../app.ts";55 *56 * void app;57 * void bootLofi();58 * ```59 *60 * @returns A promise that resolves once boot has been scheduled for this document.61 */62export async function bootLofi(): Promise<void> {63 if (booted || typeof document === "undefined") return;64 booted = true;65 // The fork guard snapshots container freshness from localStorage, so it66 // must run before any other module stores a key there.67 storageForkGuard.start();68 mountDefaultForkNotice();69 // The sink envelope must be open before lifecycle.ts reads the sync70 // location at import time. A restore failure degrades to local-only; it71 // must never brick boot.72 try {73 await restoreDeclaredSink();74 } catch {75 // Continue local-only; enrollment can re-declare the sink.76 }77 startCompatibilityGate();78 await import("./lifecycle.ts");79 // Arm the write ledger so journaled writes reconcile and outstanding effect80 // obligations re-run before any island mounts. A boot that cannot open the81 // runtime surfaces through its own diagnostics, never through arming.82 void import("./write-ledger.ts")83 .then((ledger) => ledger.armWriteLedger())84 .catch(() => undefined);85 registerProductionServiceWorker();86 if (import.meta.env.DEV) void import("./probe.ts");87}