/** * One-call Service Worker bootstrap for Weft. * * Wires up storage, engine, scheduler, and the four event listeners * (`install`, `activate`, `fetch`, `periodicsync`) in a single async call. * Use this when your Service Worker file calls `register` from inside the * helper. Use the lower-level `createFetchHandler` / `createPeriodicSyncHandler` * / `createLifecycleHandlers` factories when you've already registered * workflows synchronously and want explicit listener attachment. * * @module service-worker/setup */ import { Engine, type RegistryAgnosticEngine } from '../core/engine'; import type { HandlerOptions } from '../server/handler'; import type { Storage as WeftStorage } from '../storage/interface'; import { ServiceWorkerScheduler } from './scheduler'; /** * The subset of {@link HandlerOptions} that can be supplied by a Service * Worker. Service Workers can inject request authorization and event-stream * plumbing, but cannot provide server-owned registries or Bun runtime * integrations. * * @example * ```ts * import type { ServiceWorkerHandlerOptions } from '@lostgradient/weft/service-worker'; * * const handlerOptions: ServiceWorkerHandlerOptions = { * authContext: { method: 'public' }, * }; * void handlerOptions; * ``` */ export type ServiceWorkerHandlerOptions = Pick; /** * Options for {@link setupServiceWorker}. All fields are optional; the * helper supplies sensible defaults (`/weft/` path prefix, `'weft'` * IndexedDB database name, `'weft-timers'` periodic-sync tag). * * @example * ```ts * import { workflow } from '@lostgradient/weft'; * import { setupServiceWorker, type SetupServiceWorkerOptions } from '@lostgradient/weft/service-worker'; * * const checkout = workflow({ name: 'checkout' }).execute(async function* () { * yield; * return 'done'; * }); * * const options: SetupServiceWorkerOptions = { * pathPrefix: '/weft/', * register(engine) { * engine.register(checkout); * }, * }; * void setupServiceWorker(options); * ``` */ export interface SetupServiceWorkerOptions { /** Path prefix for engine HTTP routing. Default: `'/weft/'`. */ pathPrefix?: string; /** IndexedDB database name. Default: `'weft'`. */ databaseName?: string; /** Periodic-sync tag the scheduler ticks on. Default: `'weft-timers'`. */ periodicSyncTag?: string; /** * Pre-built engine. If provided, must use the same storage instance as * `options.storage` (or its own storage if `storage` is omitted). * * Typed as {@link RegistryAgnosticEngine} (see its JSDoc) rather than the * plain default `Engine`, so both `new Engine({ storage })` and * `Engine.create({ workflows })` type-check here directly. */ engine?: RegistryAgnosticEngine; /** * Pre-built storage instance. Must be the same `===` reference as the * engine's storage when both are provided. */ storage?: WeftStorage; /** Handler options supported by the Service Worker runtime. */ handlerOptions?: ServiceWorkerHandlerOptions; /** * Register workflows on the engine before listeners do real work. * Resolves before the helper returns. Rejection causes subsequent * fetch/periodic-sync handlers to fail-fast with explicit errors. */ register?: (engine: Engine) => void | Promise; /** * When `true`, calls `engine.recoverAll()` (with no arguments) after * `options.register` completes and before the `ready` promise settles. * Fetch and periodic-sync handlers therefore block on both workflow * registration AND recovery before serving any traffic. * * This is the zero-boilerplate replacement for the common pattern of * calling `await engine.recoverAll()` at the end of your `register` * callback. It does NOT forward `RecoverAllOptions`: if you need * `acknowledgeUnknownWorkflowTypes` or any other recovery option, call * `engine.recoverAll(opts)` yourself inside `register` and leave `recover` * unset — do not set both, or recovery runs twice (the helper has no guard * against a second, no-argument pass). * * Defaults to `false` — no behavior change for callers that omit this option. */ recover?: boolean; } /** * Result returned by {@link setupServiceWorker} once registration completes. * * @example * ```ts * import { workflow } from '@lostgradient/weft'; * import { setupServiceWorker, type SetupServiceWorkerResult } from '@lostgradient/weft/service-worker'; * * const hello = workflow({ name: 'hello' }).execute(async function* () { * yield; * return 'world'; * }); * * const setup: SetupServiceWorkerResult = await setupServiceWorker(); * await setup.ready; * setup.engine.register(hello); * ``` */ export interface SetupServiceWorkerResult { engine: Engine; storage: WeftStorage; scheduler: ServiceWorkerScheduler; /** Resolves when registration (and recovery, if `recover: true`) completes. Rejects if either threw. */ ready: Promise; } /** * Bootstrap a Weft engine inside a Service Worker scope. Attaches all four * event listeners synchronously, then awaits `register` before any handler * does real work. Safe to call once per worker evaluation; concurrent calls * during initialization converge to the same {@link SetupServiceWorkerResult}. * * @example * ```ts * /// * import { workflow } from '@lostgradient/weft'; * import { setupServiceWorker } from '@lostgradient/weft/service-worker'; * * const checkout = workflow({ name: 'checkout' }).execute(async function* () { * yield; * return 'done'; * }); * * const setup = await setupServiceWorker({ * register(engine) { * engine.register(checkout); * }, * }); * void setup; * ``` */ export declare function setupServiceWorker(options?: SetupServiceWorkerOptions): Promise; /** * Test helper: clear the per-scope setup registry. Production code does not * call this. Exposed so tests can simulate fresh worker evaluations without * tearing down the scope itself. * * @example * ```ts * import { resetSetupServiceWorkerRegistry } from '@lostgradient/weft/service-worker'; * declare const fakeScope: object; * resetSetupServiceWorkerRegistry(fakeScope); * ``` */ export declare function resetSetupServiceWorkerRegistry(scope?: object): void;