@embedpdf/core 3.0.0-next.13 → 3.0.0-next.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["CONTINUOUS_RENDER_POLICY"],"sources":["../src/types.ts","../src/store.ts","../src/context.ts","../src/order.ts","../src/scope.ts","../src/kernel.ts","../src/event-hook.ts","../src/serial-queue.ts","../src/index.ts"],"sourcesContent":["/**\n * @embedpdf/core — core contracts.\n *\n * Framework-free and serializable. The engine boundary is the REAL one:\n * `@embedpdf/engine-core`'s `Engine`/`DocumentHandle` — implemented identically by\n * local-wasm (`@embedpdf/engine`), cloud (`@cloudpdf/engine`), and the test fake.\n * The kernel adds *document scope*: plugins declare a scope and the kernel\n * multiplexes document-scoped plugins per document.\n */\nimport type {\n DocumentHandle,\n Engine,\n EngineFactory,\n OpenInput,\n OpenOptions,\n PageLayout,\n PageObjectNumber,\n PageRotation,\n PageRotateResult,\n PageMoveResult,\n PageDeleteResult,\n PageInsertResult,\n PageInsertBlankSpec,\n PdfSize,\n DocCapability,\n PdfSaveMode,\n DocumentMetadata,\n MetadataPatch,\n MetadataUpdateResult,\n DocumentEvent,\n EngineRenderPolicy,\n PdfDestination,\n} from '@embedpdf/engine-core/runtime';\n\n// re-export the engine contracts so consumers import them from @embedpdf/core\nexport type {\n DocumentHandle,\n Engine,\n EngineFactory,\n OpenInput,\n OpenOptions,\n PageLayout,\n PdfDestination,\n PageObjectNumber,\n PageRotation,\n PageRotateResult,\n PageMoveResult,\n PageDeleteResult,\n PageInsertResult,\n PageInsertBlankSpec,\n PdfSize,\n DocCapability,\n PdfSaveMode,\n DocumentMetadata,\n MetadataPatch,\n MetadataUpdateResult,\n DocumentEvent,\n};\n\nexport type Unsubscribe = () => void;\n\n/** Every state transition is a plain, serializable action. */\nexport interface Action {\n readonly type: string;\n}\n\n/** Kernel-emitted document-lifecycle actions; plugins may react via `onAction`. */\nexport const CORE_DOCUMENT_ADDED = '@@core/document-added';\nexport const CORE_DOCUMENT_REMOVED = '@@core/document-removed';\nexport const CORE_ACTIVE_CHANGED = '@@core/active-changed';\nexport const CORE_ORDER_CHANGED = '@@core/order-changed';\n/** A document's page registry was replaced by a mutation event (rotate/move/delete). */\nexport const CORE_DOCUMENT_PAGES_UPDATED = '@@core/document-pages-updated';\n/** A tab slot was reserved: `open()` was called; the document is loading. */\nexport const CORE_DOCUMENT_OPENING = '@@core/document-opening';\n/** The engine reports the document needs a password (`documents.unlock`). */\nexport const CORE_DOCUMENT_LOCKED = '@@core/document-locked';\n/** The open failed; the tab stays with `status: 'error'` until closed. */\nexport const CORE_DOCUMENT_OPEN_FAILED = '@@core/document-open-failed';\n\n/** A typed handle to a capability — typed resolution, no string casts. */\nexport interface CapabilityToken<T> {\n readonly name: string;\n /**\n * Authored remedy shown when a plugin `requires` this capability and no\n * plugin provides it — the missing-dependency error should contain its own\n * fix (e.g. \"add interactionPlugin() from '@embedpdf/plugin-interaction' to\n * your plugins list\").\n */\n readonly hint?: string;\n /** phantom — never present at runtime */\n readonly __type?: T;\n}\n\n/**\n * What the kernel knows about an open document — the page registry captured at open.\n * `pages` is the engine's own snapshot (`PageLayout`: index, pageObjectNumber, size,\n * rotation, label, boxes). The `pageObjectNumber` (pon) is the durable per-page\n * identity; the array index is only display order.\n */\nexport interface DocumentMeta {\n readonly id: string;\n readonly name?: string;\n readonly pageCount: number;\n readonly pages: readonly PageLayout[];\n /**\n * Bumps every time the page registry is replaced by a document mutation\n * event (rotate/move/delete). The registry's monotonic version: anything\n * caching a derivation of `pages` (e.g. the Stage's laid-out scene) keys\n * on it so a same-`pageCount` change — like a rotation — still invalidates.\n */\n readonly revision: number;\n /**\n * The deployment render policy the document's engine advertises — a\n * document FACT like `pages`, materialized by the kernel at open (before\n * publish), NOT plugin state. One lifecycle (here), one interpretation\n * (engine-core's pure `snap*` helpers); any plugin reads it. Engines\n * without a render service — and failed policy reads — resolve to\n * `continuous`, so consumers never branch on absence. Naming rule: the\n * domain appears exactly once — bare `policy()` on the render SERVICE,\n * `renderPolicy` on flat envelopes like this one and `/v1/access`.\n */\n readonly renderPolicy: EngineRenderPolicy;\n}\n\n/**\n * A tab slot whose document is not (yet) real: still opening, waiting for a\n * password, or failed. Lives beside `documents`, never inside it — plugins\n * only ever see READY documents; pending slots are pure registry/UI state.\n * Request-time lifecycle: `open()` reserves the slot (id, order position,\n * activation) synchronously; only the content arrives at completion time.\n */\nexport interface PendingMeta {\n readonly id: string;\n readonly name?: string;\n readonly status: 'loading' | 'locked' | 'error';\n /** `locked` only: a password was supplied at open and rejected — the\n * prompt should show its \"incorrect password\" copy, not \"required\". */\n readonly passwordProvided?: boolean;\n /** `error` only: what the open rejected with. Never carries a password. */\n readonly error?: unknown;\n}\n\n/** The document registry — what document scope is built on. */\nexport interface CoreState {\n readonly documents: Readonly<Record<string, DocumentMeta>>;\n readonly pending: Readonly<Record<string, PendingMeta>>;\n /** THE tab strip: spans ready docs and pending slots, in request order. */\n readonly order: readonly string[];\n /** May point at a pending slot (a loading or locked tab can be selected). */\n readonly activeId: string | null;\n}\n\nexport interface GlobalState {\n readonly core: CoreState;\n readonly plugins: Readonly<Record<string, unknown>>;\n}\n\nexport type PluginScope = 'workspace' | 'document';\n\n/**\n * The context a plugin receives. Document-scoped plugins get a context bound to a\n * single document — `documentId`, `document()` (metadata), `doc` (the engine handle),\n * and `get()` resolving document-scoped capabilities for it.\n */\nexport interface PluginContext<S, A extends Action = Action> {\n readonly id: string;\n readonly engine: Engine;\n /** The bound document (document-scoped plugins only; undefined for workspace). */\n readonly documentId?: string;\n /** The bound document's engine handle; for workspace plugins, the active doc's handle, or null. */\n readonly doc: DocumentHandle | null;\n /** Resolve a live engine handle by document id; omitted follows this context's normal active/bound rule. */\n documentHandle(documentId?: string): DocumentHandle | null;\n getState(): S;\n dispatch(action: A): void;\n subscribe(listener: () => void): Unsubscribe;\n core(): CoreState;\n /** The bound document's metadata; for workspace plugins, the active doc's, or null. */\n document(): DocumentMeta | null;\n get<T>(token: CapabilityToken<T>): T;\n forDocument<T>(token: CapabilityToken<T>, documentId: string): T;\n tryGet<T>(token: CapabilityToken<T>): T | null;\n /** `forDocument` for an OPTIONAL dependency: null when the plugin is not\n * installed or that document is not ready, never a throw. */\n tryForDocument<T>(token: CapabilityToken<T>, documentId: string): T | null;\n /**\n * Register a resource teardown owned by this plugin instance. Document-\n * scoped callbacks run when that document closes; workspace callbacks run\n * when the kernel is destroyed. Asynchronous teardowns are awaited.\n * Registering after the owner is already disposed runs the teardown\n * immediately instead of dropping it — a late registration cannot leak.\n * Safe for capability-held timers, runtimes, subscriptions, object URLs,\n * and binary caches.\n */\n cleanup(fn: () => void | Promise<void>): void;\n}\n\n/** Side-effect context: the only place async/IO/cross-plugin reactions live. */\nexport interface EffectContext<S, A extends Action = Action> extends PluginContext<S, A> {\n watch<R>(\n select: () => R,\n handler: (value: R, previous: R) => void,\n isEqual?: (a: R, b: R) => boolean,\n ): Unsubscribe;\n onAction(type: string, handler: (action: Action) => void): Unsubscribe;\n}\n\n/**\n * A plugin definition. `scope` decides multiplexing:\n * 'workspace' (default) — one instance; can see every document.\n * 'document' — one instance PER open document; authored single-document.\n */\nexport interface PluginDef<S = unknown, A extends Action = Action, C = unknown> {\n readonly id: string;\n readonly token?: CapabilityToken<C>;\n readonly scope?: PluginScope;\n readonly requires?: ReadonlyArray<CapabilityToken<unknown>>;\n readonly optional?: ReadonlyArray<CapabilityToken<unknown>>;\n readonly initialState?: S | (() => S);\n readonly reduce?: (state: S, action: A) => S;\n readonly capability?: (ctx: PluginContext<S, A>) => C;\n readonly init?: (ctx: PluginContext<S, A>) => void | Promise<void>;\n readonly effects?: (ctx: EffectContext<S, A>) => void;\n}\n\nexport type AnyPlugin = PluginDef<any, any, any>;\n\n// ── Built-in: the document registry, exposed as a capability ─────────────────\n\nexport type DocStatus = 'loading' | 'locked' | 'ready' | 'error';\n\nexport interface DocInfo {\n id: string;\n name?: string;\n /** Lifecycle state — the tab bar renders directly off this. */\n status: DocStatus;\n /** 0 until the document is `ready`. */\n pageCount: number;\n /** `locked` only: a supplied password was rejected (show \"incorrect\"). */\n passwordProvided?: boolean;\n}\n\n/** Field-wise DocInfo equality — the ONE definition every adapter's reactive\n * `docs` read keys on, so a new lifecycle field can never silently stop\n * re-rendering one framework's tab bar. */\nexport const docInfoEquals = (a: DocInfo, b: DocInfo): boolean =>\n a.id === b.id &&\n a.name === b.name &&\n a.status === b.status &&\n a.pageCount === b.pageCount &&\n a.passwordProvided === b.passwordProvided;\n\nexport const docInfoListEquals = (a: readonly DocInfo[], b: readonly DocInfo[]): boolean =>\n a === b || (a.length === b.length && a.every((d, i) => docInfoEquals(d, b[i])));\n\n/** Options for opening a document: kernel concerns (activate/name) + engine OpenOptions. */\nexport type OpenDocumentOptions = OpenOptions & { activate?: boolean; name?: string };\n\n/**\n * What `open()` accepts: an engine `OpenInput`, or a thunk producing one.\n * The thunk form makes the FETCH happen under the loading tab — the slot is\n * reserved synchronously, then the thunk runs (network), then the engine\n * opens. Prefer it for anything that isn't already in memory.\n *\n * The thunk receives an AbortSignal that fires if the tab is closed while\n * the fetch is still running — pass it to `fetch()` so a closed tab stops\n * the network work. Zero-argument thunks keep working unchanged.\n */\nexport type OpenSource = OpenInput | ((signal: AbortSignal) => OpenInput | Promise<OpenInput>);\n\n/**\n * One boot document for `documents.openAll()` — THE shared shape every\n * framework adapter's `initialDocuments` input uses (adapters re-export it,\n * never redefine it). `active` picks the selected tab (default: the first);\n * all other open options (name, password, scope, identity, …) pass straight\n * through to `open()`, so the declarative and imperative paths cannot drift.\n */\nexport type InitialDocument = { source: OpenSource; active?: boolean } & Omit<\n OpenDocumentOptions,\n 'activate'\n>;\n\nexport interface DocumentsCapability {\n open(input: OpenSource, options?: OpenDocumentOptions): Promise<string>;\n /**\n * Boot-open a batch: fires every `open()` WITHOUT awaiting, so each tab\n * slot is reserved synchronously — all tabs exist immediately, in array\n * order — and exactly ONE is selected (the `active` entry, else the\n * first), decided at request time so a slow document can never steal\n * focus. Failures surface as the tab's `error`/`locked` status, never as\n * unhandled rejections. This is kernel-owned POLICY: adapters call this\n * one line instead of each re-implementing activation and error handling.\n */\n openAll(docs: readonly InitialDocument[]): void;\n /**\n * Unlock a `locked` document with a password and promote it to `ready`.\n * Rejects with the engine's DocPasswordIncorrect on a wrong password —\n * the document stays locked and unlock can be called again. Identical\n * behavior on the local (worker loads the parked bytes) and cloud\n * (/access grant) engines.\n */\n unlock(id: string, input: { password: string }): Promise<void>;\n close(id: string): Promise<void>;\n closeAll(): Promise<void>;\n setActive(id: string): void;\n activeId(): string | null;\n list(): DocInfo[];\n get(id: string): DocInfo | null;\n has(id: string): boolean;\n count(): number;\n order(): string[];\n /** Move a document (tab) to a new position in the order. */\n move(id: string, toIndex: number): void;\n /** Swap two documents (tabs) in the order. */\n swap(a: string, b: string): void;\n /**\n * Save the COMPLETE document (base + layer) to PDF bytes. `mode` is\n * `'incremental'` (append changes, original bytes preserved) or `'rewrite'`\n * (flatten to a fresh PDF). Defaults to the active document.\n */\n download(id?: string, opts?: { mode?: PdfSaveMode }): Promise<Uint8Array>;\n /**\n * Export JUST the document's LAYER artifact (re-openable via `OpenInputLayerBytes`).\n * Rejects when the document was opened without a layer, or the engine can't\n * export one (cloud manages layers server-side — `DocumentHandle.downloadLayer`\n * is absent there). Defaults to the active document.\n */\n downloadLayer(id?: string): Promise<Uint8Array>;\n /**\n * Session authority over a document — the sanctioned surface for the ONE\n * chrome exception in permissions.md: kernel-level features with a 1:1\n * capability and no owning plugin (print via `'doc.print'`, download via\n * `'doc.download'` — the verbs live on THIS capability). Everything else\n * asks the owning plugin's twins, never a raw capability string. Defaults\n * to the active document; `false` with no (ready) document.\n */\n allows(cap: DocCapability, id?: string): boolean;\n}\n\n/** Built-in token for the document registry capability (provided by the kernel). */\nexport const DocumentsToken: CapabilityToken<DocumentsCapability> = { name: 'documents' };\n","import type { Action, CoreState, GlobalState, Unsubscribe } from './types';\n\n/**\n * The store: one state tree ({ core, plugins }), keyed plugin slices, and two\n * channels — `subscribe` (state changed, for reactivity/`watch`) and\n * `subscribeAction` (an action was dispatched, for `onAction`). Slice keys are\n * opaque strings; the kernel uses `pluginId` for workspace plugins and\n * `pluginId::docId` for document-scoped ones.\n *\n * Change notification is non-re-entrant: if a listener dispatches, we finish the\n * current pass and run another — keeping listener order deterministic.\n */\nexport interface Store {\n registerSlice(key: string, reducer: (s: unknown, a: Action) => unknown, initial: unknown): void;\n removeSlice(key: string): void;\n getSlice(key: string): unknown;\n getCore(): CoreState;\n getState(): GlobalState;\n dispatchTo(key: string, action: Action): void;\n setCore(patch: Partial<CoreState>, action: Action): void;\n subscribe(listener: () => void): Unsubscribe;\n subscribeAction(listener: (action: Action) => void): Unsubscribe;\n /** Kernel-destroy teardown: reset core, drop every slice/reducer/listener.\n * Reads stay legal afterwards (empty state); writes become no-ops. */\n destroy(): void;\n}\n\nexport function createStore(report: (error: unknown) => void = console.error): Store {\n let core: CoreState = { documents: {}, pending: {}, order: [], activeId: null };\n const slices: Record<string, unknown> = {};\n const reducers: Record<string, (s: unknown, a: Action) => unknown> = {};\n const changeListeners = new Set<() => void>();\n const actionListeners = new Set<(a: Action) => void>();\n\n // Per-listener isolation: one throwing subscriber (a plugin effect, a React\n // read) must never halt the pass for its siblings, and must never unwind a\n // kernel lifecycle transition out of setCore/dispatchTo mid-update.\n const guarded = (fn: () => void) => {\n try {\n fn();\n } catch (error) {\n report(error);\n }\n };\n\n let emitting = false;\n let pending = false;\n const emitChange = () => {\n if (emitting) {\n pending = true;\n return;\n }\n emitting = true;\n try {\n do {\n pending = false;\n changeListeners.forEach((listener) => guarded(listener));\n } while (pending);\n } finally {\n emitting = false;\n }\n };\n const emitAction = (action: Action) =>\n actionListeners.forEach((listener) => guarded(() => listener(action)));\n\n return {\n registerSlice(key, reducer, initial) {\n reducers[key] = reducer;\n slices[key] = initial;\n },\n removeSlice(key) {\n delete reducers[key];\n delete slices[key];\n emitChange();\n },\n getSlice: (key) => slices[key],\n getCore: () => core,\n getState: () => ({ core, plugins: slices }),\n dispatchTo(key, action) {\n const r = reducers[key];\n if (!r) return;\n const next = r(slices[key], action);\n if (next !== slices[key]) {\n slices[key] = next;\n emitChange();\n }\n emitAction(action);\n },\n setCore(patch, action) {\n core = { ...core, ...patch };\n emitChange();\n emitAction(action);\n },\n subscribe(listener) {\n changeListeners.add(listener);\n return () => void changeListeners.delete(listener);\n },\n subscribeAction(listener) {\n actionListeners.add(listener);\n return () => void actionListeners.delete(listener);\n },\n destroy() {\n core = { documents: {}, pending: {}, order: [], activeId: null };\n for (const key of Object.keys(slices)) delete slices[key];\n for (const key of Object.keys(reducers)) delete reducers[key];\n changeListeners.clear();\n actionListeners.clear();\n },\n };\n}\n","import type {\n Action,\n AnyPlugin,\n CapabilityToken,\n DocumentHandle,\n DocumentMeta,\n EffectContext,\n Engine,\n PluginContext,\n} from './types';\nimport type { Store } from './store';\nimport type { Scope } from './scope';\n\n/** A plugin's slice key. Workspace plugins use their id; document-scoped plugins are per-document. */\nexport const sliceKey = (pluginId: string, documentId?: string): string =>\n documentId ? `${pluginId}::${documentId}` : pluginId;\n\n/**\n * The slice of a DocumentSession a context needs — structural, so this module\n * has no cycle with the kernel. Holding the session OBJECT (not its id) is\n * what makes late teardown registration safe: a context created for a session\n * can always reach that session's scope, even after the session has been\n * closed and removed from the kernel's map.\n */\nexport interface SessionRef {\n id: string;\n handle: DocumentHandle | null;\n /** Meta staged during bring-up, before the document is published. */\n stagedMeta: DocumentMeta | null;\n readonly scope: Scope;\n}\n\n/**\n * Everything a context needs from the kernel, injected so this module has no cycles\n * with the capability resolver or the document lifecycle.\n */\nexport interface ContextServices {\n readonly engine: Engine;\n readonly store: Store;\n readonly workspaceScope: Scope;\n resolveCapability<T>(token: CapabilityToken<T>, documentId?: string): T;\n /** Total resolution (the kernel's internal rule: bring-up or ready) — what\n * `ctx.tryGet` delegates to. Never exception-driven: a throwing capability\n * constructor is a BUG and propagates. */\n tryResolveCapability<T>(token: CapabilityToken<T>, documentId?: string): T | null;\n documentHandle(documentId?: string): DocumentHandle | null;\n}\n\n/**\n * Build the context a plugin sees. When `session` is given the context is bound\n * to that document — `getState`/`dispatch` target its slice, `document()` returns\n * it, and `get()` resolves document-scoped capabilities for it.\n */\nexport function createPluginContext(\n services: ContextServices,\n plugin: AnyPlugin,\n session?: SessionRef,\n): PluginContext<unknown> {\n const { engine, store } = services;\n const documentId = session?.id;\n const key = sliceKey(plugin.id, documentId);\n const ownScope = session?.scope ?? services.workspaceScope;\n return {\n id: plugin.id,\n engine,\n documentId,\n doc: session ? session.handle : services.documentHandle(undefined),\n documentHandle: (requestedDocumentId) =>\n requestedDocumentId\n ? services.documentHandle(requestedDocumentId)\n : session\n ? session.handle\n : services.documentHandle(undefined),\n getState: () => store.getSlice(key),\n dispatch: (action: Action) => store.dispatchTo(key, action),\n subscribe: store.subscribe,\n core: store.getCore,\n document: () => {\n if (session) return store.getCore().documents[session.id] ?? session.stagedMeta;\n const id = store.getCore().activeId;\n return id ? (store.getCore().documents[id] ?? null) : null;\n },\n get: <T>(token: CapabilityToken<T>): T => services.resolveCapability(token, documentId),\n forDocument: <T>(token: CapabilityToken<T>, otherDocumentId: string): T =>\n services.resolveCapability(token, otherDocumentId),\n tryGet: <T>(token: CapabilityToken<T>): T | null =>\n services.tryResolveCapability(token, documentId),\n tryForDocument: <T>(token: CapabilityToken<T>, otherDocumentId: string): T | null =>\n services.tryResolveCapability(token, otherDocumentId),\n cleanup: (teardown) => ownScope.defer(teardown),\n };\n}\n\n/** A plugin context plus the side-effect primitives (watch / onAction / cleanup). */\nexport function createEffectContext(\n services: ContextServices,\n plugin: AnyPlugin,\n session?: SessionRef,\n): EffectContext<unknown> {\n const { store } = services;\n const ownScope = session?.scope ?? services.workspaceScope;\n return {\n ...createPluginContext(services, plugin, session),\n watch: (select, handler, isEqual = Object.is) => {\n let previous = select();\n const unsubscribe = store.subscribe(() => {\n const next = select();\n if (!isEqual(previous, next)) {\n const prior = previous;\n previous = next;\n handler(next, prior);\n }\n });\n ownScope.defer(unsubscribe);\n return unsubscribe;\n },\n onAction: (type, handler) => {\n const unsubscribe = store.subscribeAction((action) => {\n if (action.type === type) handler(action);\n });\n ownScope.defer(unsubscribe);\n return unsubscribe;\n },\n };\n}\n","import { DocumentsToken, type AnyPlugin, type CapabilityToken, type PluginScope } from './types';\n\n/**\n * The result of analysing a plugin list: a dependency-ordered list, plus lookups\n * for which plugin provides a token and what scope a token has. Pure and testable.\n */\nexport interface PluginPlan {\n readonly ordered: AnyPlugin[];\n providerOf(token: CapabilityToken<unknown>): AnyPlugin | undefined;\n scopeOf(token: CapabilityToken<unknown>): PluginScope;\n}\n\n/**\n * Validate `requires` (fail fast) and topologically sort plugins so dependencies\n * are initialised before dependents.\n */\nexport function planPlugins(plugins: readonly AnyPlugin[]): PluginPlan {\n const providerByToken = new Map<CapabilityToken<unknown>, AnyPlugin>();\n const scopeByToken = new Map<CapabilityToken<unknown>, PluginScope>([\n [DocumentsToken, 'workspace'],\n ]);\n\n for (const plugin of plugins) {\n if (plugin.token && plugin.capability) {\n providerByToken.set(plugin.token, plugin);\n scopeByToken.set(plugin.token, plugin.scope ?? 'workspace');\n }\n }\n\n // every required token must be provided (the documents token is always available)\n for (const plugin of plugins) {\n for (const required of plugin.requires ?? []) {\n if (required !== DocumentsToken && !providerByToken.has(required)) {\n // The error carries its own fix: tokens author the remedy (hint), so a\n // forgotten dependency is a ten-second paste, not an investigation.\n throw new Error(\n `Plugin \"${plugin.id}\" requires capability \"${required.name}\", which no plugin provides` +\n (required.hint ? ` — ${required.hint}.` : '.'),\n );\n }\n }\n }\n\n // depth-first topological sort (dependencies pushed before dependents)\n const ordered: AnyPlugin[] = [];\n const status = new Map<string, 'visiting' | 'done'>();\n const visit = (plugin: AnyPlugin) => {\n const state = status.get(plugin.id);\n if (state === 'done') return;\n if (state === 'visiting') throw new Error(`Dependency cycle involving plugin \"${plugin.id}\".`);\n status.set(plugin.id, 'visiting');\n for (const token of [...(plugin.requires ?? []), ...(plugin.optional ?? [])]) {\n const dependency = providerByToken.get(token);\n if (dependency && dependency !== plugin) visit(dependency);\n }\n status.set(plugin.id, 'done');\n ordered.push(plugin);\n };\n for (const plugin of plugins) visit(plugin);\n\n return {\n ordered,\n providerOf: (token) => providerByToken.get(token),\n scopeOf: (token) => scopeByToken.get(token) ?? 'workspace',\n };\n}\n","/**\n * Resource scope — the ownership half of the lifecycle model.\n *\n * Every resource acquisition registers its release into the owning scope AT\n * the acquisition site (`scope.defer(release)` on the line after acquiring),\n * so no exit path can forget it. `dispose()` unwinds the stack LIFO, awaits\n * asynchronous teardowns, and is idempotent — every call returns the same\n * promise.\n *\n * THE rule that deletes the silent-leak bug class: a disposed scope never\n * swallows a teardown. Registering against a scope that is already disposing\n * runs the teardown as soon as the in-flight disposal finishes, instead of\n * dropping it. (Producers are still expected to stop at their next\n * cancellation checkpoint; this rule is the safety net, not the primary\n * synchronization — that's the session's operation join.)\n */\n\nexport type Teardown = () => void | Promise<void>;\n\n/**\n * A lifecycle operation was cancelled — the tab was closed mid-open, or the\n * kernel was destroyed mid-start. NOT a failure: callers that race `open()`\n * against `close()` should treat this rejection as \"the close won\".\n */\nexport class CancelledError extends Error {\n constructor(what: string) {\n super(`[kernel] cancelled: ${what}`);\n this.name = 'CancelledError';\n }\n}\n\nexport const isCancelled = (error: unknown): error is CancelledError =>\n error instanceof CancelledError;\n\nexport interface Scope {\n readonly disposed: boolean;\n defer(teardown: Teardown): void;\n dispose(): Promise<void>;\n}\n\nexport function createScope(report: (error: unknown) => void): Scope {\n const teardowns: Teardown[] = [];\n let disposal: Promise<void> | null = null;\n\n return {\n get disposed() {\n return disposal !== null;\n },\n defer(teardown) {\n if (disposal) void disposal.then(() => Promise.resolve().then(teardown).catch(report));\n else teardowns.push(teardown);\n },\n dispose() {\n return (disposal ??= (async () => {\n while (teardowns.length) {\n try {\n await teardowns.pop()!();\n } catch (error) {\n report(error); // one failing teardown never strands the rest\n }\n }\n })());\n },\n };\n}\n","import { createStore } from './store';\nimport {\n createEffectContext,\n createPluginContext,\n sliceKey,\n type ContextServices,\n type SessionRef,\n} from './context';\nimport { planPlugins } from './order';\nimport { createScope, CancelledError, isCancelled, type Scope } from './scope';\nimport {\n CORE_ACTIVE_CHANGED,\n CORE_DOCUMENT_ADDED,\n CORE_DOCUMENT_LOCKED,\n CORE_DOCUMENT_OPENING,\n CORE_DOCUMENT_OPEN_FAILED,\n CORE_DOCUMENT_PAGES_UPDATED,\n CORE_DOCUMENT_REMOVED,\n CORE_ORDER_CHANGED,\n DocumentsToken,\n type Action,\n type AnyPlugin,\n type CapabilityToken,\n type CoreState,\n type DocInfo,\n type DocumentHandle,\n type DocumentMeta,\n type DocumentsCapability,\n type Engine,\n type GlobalState,\n type OpenDocumentOptions,\n type OpenInput,\n type OpenSource,\n type PendingMeta,\n type PluginScope,\n type Unsubscribe,\n} from './types';\nimport {\n CONTINUOUS_RENDER_POLICY,\n type DocumentEvent,\n type EngineRenderPolicy,\n} from '@embedpdf/engine-core/runtime';\n\n/** The new page registry a document mutation carries, or null for events that\n * don't change page structure (annotations, metadata). The snapshot is the\n * same shape `pages.list()` returns, so callers swap it in directly. */\nfunction layoutFromEvent(event: DocumentEvent) {\n switch (event.type) {\n case 'pages.moved':\n case 'pages.rotated':\n case 'pages.deleted':\n case 'pages.inserted':\n return event.layout;\n default:\n return null;\n }\n}\n\n/** Kernel lifecycle. Monotonic: created → starting → started, then destroying →\n * destroyed; `failed` is a terminal branch off starting. */\nexport type KernelStatus =\n | 'created'\n | 'starting'\n | 'started'\n | 'failed'\n | 'destroying'\n | 'destroyed';\n\nexport interface Kernel {\n readonly engine: Engine;\n readonly documents: DocumentsCapability;\n /** Resolve a capability. For document-scoped tokens, `documentId` defaults to the active doc. */\n capability<T>(token: CapabilityToken<T>, documentId?: string): T;\n /**\n * Total sibling of `capability()`: `null` instead of throwing — no\n * provider, no document, or a document that isn't `ready` yet. This is the\n * method adapters subscribe to (`useKernelValue`-style): resolution is a\n * VALUE derived from kernel state, not a pure function of its arguments —\n * a pending document's promotion changes the result while the id stays the\n * same, so caching a `capability()` call by id goes stale. The returned\n * instance is reference-stable per (plugin, document), so equality-cached\n * reads don't churn.\n */\n tryCapability<T>(token: CapabilityToken<T>, documentId?: string): T | null;\n /** A token's scope — adapters use this to decide whether to bind a document. */\n scopeOf(token: CapabilityToken<unknown>): PluginScope;\n subscribe(listener: () => void): Unsubscribe;\n getState(): GlobalState;\n status(): KernelStatus;\n /** Idempotent: a second call joins the first. Throws after destroy(). */\n start(): Promise<void>;\n /**\n * Idempotent: every call returns the same promise. Joins an in-flight\n * start(), closes every document (their engine handles included), then\n * unwinds workspace resources. Never throws.\n */\n destroy(): Promise<void>;\n}\n\nconst isDocumentScoped = (plugin: AnyPlugin) => plugin.scope === 'document';\nconst initialStateOf = (plugin: AnyPlugin): unknown =>\n typeof plugin.initialState === 'function'\n ? (plugin.initialState as () => unknown)()\n : (plugin.initialState ?? {});\nconst reducerOf = (plugin: AnyPlugin) =>\n (plugin.reduce ?? ((state: unknown) => state)) as (state: unknown, action: Action) => unknown;\nconst toDocInfo = (meta: DocumentMeta): DocInfo => ({\n id: meta.id,\n name: meta.name,\n status: 'ready',\n pageCount: meta.pageCount,\n});\nconst pendingToDocInfo = (meta: PendingMeta): DocInfo => ({\n id: meta.id,\n name: meta.name,\n status: meta.status,\n pageCount: 0,\n passwordProvided: meta.passwordProvided,\n});\n\n/** The stable id an input implies, if it carries one ('bytes'/'layerBytes'/'id'). */\nconst idOfInput = (input: OpenInput): string | null =>\n 'id' in input && typeof input.id === 'string' ? input.id : null;\nconst passwordOfInput = (input: OpenInput): string | null | undefined =>\n 'password' in input ? input.password : undefined;\n\n/**\n * Everything one open document owns, in one place: the engine handle, the\n * resource scope (event subs, slices, plugin cleanups, the handle's own\n * close), the capability instances, and the in-flight lifecycle operation.\n * The session IS the document's lifecycle; the store's `documents`/`pending`\n * entries are its UI projection.\n *\n * opening — slot reserved; source resolving / engine opening\n * locked — parked on a password; the scope already owns handle.close\n * bringup — post-security: slices, inits, effect setup; NOT yet published\n * ready — committed; the only phase adapters resolve capabilities in\n * error — open failed after the slot was reserved; resources disposed\n * closing — close() won; unpublished, joining the operation, disposing\n */\ninterface DocumentSession extends SessionRef {\n name?: string;\n phase: 'opening' | 'locked' | 'bringup' | 'ready' | 'error' | 'closing';\n /** In-flight open/unlock — close() cancels, then JOINS this before disposing,\n * so \"close resolved\" means \"no producer is still acquiring resources\". */\n operation: Promise<unknown> | null;\n /** The current engine call, retained so close() can abort real worker-side\n * work instead of waiting for it to land at a checkpoint. */\n engineOp: { abort(reason?: unknown): void } | null;\n cancel: AbortController;\n capabilities: Map<AnyPlugin, unknown>;\n close(): Promise<void>;\n}\n\n/** Engine rejections carry AbortError when close() aborts the live call;\n * match structurally so test fakes with plain promises still work. */\nconst isAbortLike = (error: unknown): boolean =>\n error instanceof Error && error.name === 'AbortError';\n\n/**\n * Assemble a kernel from an engine + plugins.\n *\n * planPlugins — validate dependencies, order them\n * resolveCapability — workspace singletons, or per-document instances built lazily\n * document lifecycle — one DocumentSession per document: transactional open\n * (publish-last), one idempotent close for every phase\n * start / destroy — explicit status machine; destroy closes everything\n *\n * The kernel closes every handle it opened; it never destroys the engine —\n * ownership follows acquisition, and the engine was handed in by the caller.\n */\nexport function createKernel(opts: {\n engine: Engine;\n plugins: AnyPlugin[];\n /** Observability seam: teardown/effect/join failures land here. Default: console.error. */\n report?: (error: unknown) => void;\n}): Kernel {\n const { engine, plugins } = opts;\n const report = opts.report ?? ((error: unknown) => console.error('[kernel]', error));\n const store = createStore(report);\n const plan = planPlugins(plugins);\n const documentScopedPlugins = plan.ordered.filter(isDocumentScoped);\n\n const workspaceCapabilities = new Map<CapabilityToken<unknown>, unknown>();\n const workspaceScope = createScope(report);\n const sessions = new Map<string, DocumentSession>();\n\n let status: KernelStatus = 'created';\n let startPromise: Promise<void> | null = null;\n let destroyPromise: Promise<void> | null = null;\n\n const guardUsable = (what: string) => {\n if (status === 'failed' || status === 'destroying' || status === 'destroyed') {\n throw new Error(`[kernel] ${what} on a ${status} kernel`);\n }\n };\n\n // ── sessions ─────────────────────────────────────────────────────────────────\n\n const checkpoint = (session: DocumentSession) => {\n if (session.cancel.signal.aborted) {\n throw new CancelledError(`closed while opening: ${session.id}`);\n }\n };\n\n /**\n * Await an engine/network call under the session's cancellation:\n * - the call is retained so close() can abort real worker-side work\n * (`AbortablePromise`), and\n * - the await RACES the cancellation, so close()'s join never blocks on a\n * call that cannot be aborted (a plain-promise engine, a stuck fetch).\n * When cancellation wins but the call later lands anyway, `onLateResult`\n * routes the result into the session scope — whose late-defer rule runs it\n * immediately after disposal — so a late-arriving resource cannot leak.\n * (Plugin inits are deliberately NOT raced: they are first-party code that\n * close() joins to completion; only unbounded external waits are raced.)\n */\n async function engineCall<T>(\n session: DocumentSession,\n call: Promise<T>,\n onLateResult?: (value: T) => void | Promise<void>,\n ): Promise<T> {\n const abortable = call as Promise<T> & { abort?: (reason?: unknown) => void };\n session.engineOp = typeof abortable.abort === 'function' ? (abortable as never) : null;\n const signal = session.cancel.signal;\n let onAbort: (() => void) | undefined;\n const cancellation = new Promise<never>((_, reject) => {\n onAbort = () => reject(new CancelledError(`closed while opening: ${session.id}`));\n if (signal.aborted) onAbort();\n else signal.addEventListener('abort', onAbort, { once: true });\n });\n try {\n return await Promise.race([call, cancellation]);\n } catch (error) {\n if (isCancelled(error)) {\n void call.then(\n (value) => {\n if (onLateResult) session.scope.defer(() => onLateResult(value));\n },\n () => {}, // the abandoned call's own rejection is not separately actionable\n );\n }\n throw error;\n } finally {\n if (onAbort) signal.removeEventListener('abort', onAbort);\n session.engineOp = null;\n }\n }\n\n function createSession(id: string, name: string | undefined): DocumentSession {\n const session: DocumentSession = {\n id,\n name,\n phase: 'opening',\n handle: null,\n stagedMeta: null,\n scope: createScope(report),\n operation: null,\n engineOp: null,\n cancel: new AbortController(),\n capabilities: new Map(),\n close: () => closeSession(session),\n };\n return session;\n }\n\n /** One transition at a time per session (open, then possibly unlock). */\n function beginOperation<T>(session: DocumentSession, run: () => Promise<T>): Promise<T> {\n if (session.operation) {\n throw new Error(`[documents] \"${session.id}\" already has an active transition`);\n }\n const operation = run().finally(() => {\n if (session.operation === operation) session.operation = null;\n });\n session.operation = operation;\n return operation;\n }\n\n /** Atomic ticket → real-id reconciliation: the sessions map, the store slot,\n * order position, and activation all move together, or not at all. */\n function rekeySession(session: DocumentSession, nextId: string): void {\n const previousId = session.id;\n if (previousId === nextId) return;\n const core = store.getCore();\n if (sessions.has(nextId) || core.documents[nextId] || core.pending[nextId]) {\n throw new Error(`[documents] duplicate document id: ${nextId}`);\n }\n sessions.delete(previousId);\n session.id = nextId;\n sessions.set(nextId, session);\n const slot = core.pending[previousId];\n if (slot) {\n const { [previousId]: _moved, ...pending } = core.pending;\n store.setCore(\n {\n pending: { ...pending, [nextId]: { ...slot, id: nextId } },\n order: core.order.map((id) => (id === previousId ? nextId : id)),\n activeId: core.activeId === previousId ? nextId : core.activeId,\n },\n { type: CORE_ORDER_CHANGED },\n );\n }\n }\n\n const closingSessions = new WeakMap<DocumentSession, Promise<void>>();\n function closeSession(session: DocumentSession): Promise<void> {\n const inFlight = closingSessions.get(session);\n if (inFlight) return inFlight;\n const closing = (async () => {\n session.phase = 'closing';\n unpublishSlot(session.id); // synchronous: the tab disappears NOW\n // Cancel, JOIN the producer, then drain its resources — in that order.\n // After the join, no known producer can register more resources; the\n // scope's late-defer rule covers anything unknowable.\n const reason = new CancelledError(`closed while opening: ${session.id}`);\n session.cancel.abort(reason);\n session.engineOp?.abort(reason);\n await session.operation?.catch((error) => {\n if (!isCancelled(error) && !isAbortLike(error)) report(error);\n });\n await session.scope.dispose();\n if (sessions.get(session.id) === session) sessions.delete(session.id);\n })();\n closingSessions.set(session, closing);\n return closing;\n }\n\n // ── store projection (publish/unpublish) ─────────────────────────────────────\n\n function nextActiveDocument(core: CoreState, removedId: string): string | null {\n if (core.activeId !== removedId) return core.activeId;\n const index = core.order.indexOf(removedId);\n const remaining = core.order.filter((id) => id !== removedId);\n return remaining.length === 0 ? null : (remaining[Math.max(0, index - 1)] ?? remaining[0]);\n }\n\n function publishPendingSlot(session: DocumentSession, activate: boolean): void {\n const core = store.getCore();\n store.setCore(\n {\n pending: {\n ...core.pending,\n [session.id]: { id: session.id, name: session.name, status: 'loading' },\n },\n order: [...core.order, session.id],\n activeId: activate || core.activeId === null ? session.id : core.activeId,\n },\n { type: CORE_DOCUMENT_OPENING },\n );\n }\n\n function publishLocked(session: DocumentSession, passwordProvided: boolean): void {\n const core = store.getCore();\n store.setCore(\n {\n pending: {\n ...core.pending,\n [session.id]: { id: session.id, name: session.name, status: 'locked', passwordProvided },\n },\n },\n { type: CORE_DOCUMENT_LOCKED },\n );\n }\n\n function publishError(session: DocumentSession, error: unknown): void {\n const core = store.getCore();\n store.setCore(\n {\n pending: {\n ...core.pending,\n [session.id]: { id: session.id, name: session.name, status: 'error', error },\n },\n },\n { type: CORE_DOCUMENT_OPEN_FAILED },\n );\n }\n\n function unpublishSlot(id: string): void {\n const core = store.getCore();\n if (!core.pending[id] && !core.documents[id]) return;\n const { [id]: _pending, ...pending } = core.pending;\n const { [id]: _document, ...documents } = core.documents;\n store.setCore(\n {\n pending,\n documents,\n order: core.order.filter((other) => other !== id),\n activeId: nextActiveDocument(core, id),\n },\n { type: CORE_DOCUMENT_REMOVED },\n );\n }\n\n /** The ONE ready transition: swap the pending slot for the staged meta and\n * fire CORE_DOCUMENT_ADDED. Everything before this is unpublished and rolls\n * back by disposing the session scope; nothing after this can fail. */\n function commitReady(session: DocumentSession): void {\n session.phase = 'ready';\n const meta = session.stagedMeta!;\n const core = store.getCore();\n const { [session.id]: _resolved, ...pending } = core.pending;\n store.setCore(\n { documents: { ...core.documents, [session.id]: meta }, pending },\n { type: CORE_DOCUMENT_ADDED },\n );\n }\n\n // ── capability resolution ────────────────────────────────────────────────────\n\n const sessionOf = (documentId?: string): DocumentSession | null => {\n const id = documentId ?? store.getCore().activeId;\n return id ? (sessions.get(id) ?? null) : null;\n };\n\n /** The handle plugins may touch: bring-up or ready — never locked, never\n * a handle whose close already won. */\n const documentHandle = (documentId?: string): DocumentHandle | null => {\n const session = sessionOf(documentId);\n if (!session) return null;\n return session.phase === 'bringup' || session.phase === 'ready' ? session.handle : null;\n };\n\n function buildDocumentCapability(plugin: AnyPlugin, session: DocumentSession): unknown {\n let capability = session.capabilities.get(plugin);\n if (!capability) {\n capability = plugin.capability!(createPluginContext(services, plugin, session));\n session.capabilities.set(plugin, capability);\n }\n return capability;\n }\n\n function resolveCapability<T>(token: CapabilityToken<T>, documentId?: string): T {\n guardUsable(`capability(\"${token.name}\")`);\n const workspaceCapability = workspaceCapabilities.get(token);\n if (workspaceCapability) return workspaceCapability as T;\n const provider = plan.providerOf(token);\n if (!provider) throw new Error(`No capability \"${token.name}\".`);\n const id = documentId ?? store.getCore().activeId;\n if (!id) throw new Error(`Capability \"${token.name}\" requires an active document.`);\n const session = sessions.get(id);\n if (!session) throw new Error(`Capability \"${token.name}\" unavailable: no document \"${id}\".`);\n if (session.phase === 'bringup' || session.phase === 'ready') {\n return buildDocumentCapability(provider, session) as T;\n }\n // Fail fast and truthfully: a loading/locked/error document has no\n // plugin instances yet. `useOptional*` adapters turn this into their\n // fallback; strict resolution surfaces the real state.\n const shown = session.phase === 'opening' ? 'loading' : session.phase;\n throw new Error(`Capability \"${token.name}\" unavailable: document \"${id}\" is ${shown}.`);\n }\n\n /** Internal total resolver: bring-up counts, so a plugin's `ctx.tryGet`\n * works during its own document's init. */\n function tryResolveInternal<T>(token: CapabilityToken<T>, documentId?: string): T | null {\n if (status === 'destroying' || status === 'destroyed') return null;\n const workspaceCapability = workspaceCapabilities.get(token);\n if (workspaceCapability) return workspaceCapability as T;\n const provider = plan.providerOf(token);\n if (!provider) return null;\n const session = sessionOf(documentId);\n if (!session || (session.phase !== 'bringup' && session.phase !== 'ready')) return null;\n return buildDocumentCapability(provider, session) as T;\n }\n\n /** Public total resolver — see `Kernel.tryCapability`. `ready` only: the\n * null→instance flip at commit time IS the adapters' re-render signal. */\n function tryResolveCapability<T>(token: CapabilityToken<T>, documentId?: string): T | null {\n if (status === 'destroying' || status === 'destroyed') return null;\n const workspaceCapability = workspaceCapabilities.get(token);\n if (workspaceCapability) return workspaceCapability as T;\n const provider = plan.providerOf(token);\n if (!provider) return null;\n const session = sessionOf(documentId);\n if (!session || session.phase !== 'ready') return null;\n return buildDocumentCapability(provider, session) as T;\n }\n\n const services: ContextServices = {\n engine,\n store,\n workspaceScope,\n resolveCapability,\n tryResolveCapability: tryResolveInternal,\n documentHandle,\n };\n\n // ── document lifecycle ───────────────────────────────────────────────────────\n\n // Tickets for slots whose real id isn't known yet (thunk sources).\n let ticketCounter = 0;\n const nextTicket = () => `pending:${++ticketCounter}`;\n\n /** Slices + event subscription + plugin inits + effect SETUP — every step's\n * release deferred into the session scope, every await followed by a\n * checkpoint. Runs entirely pre-commit: a failure anywhere rolls the whole\n * session back and the document was never `ready`. */\n async function bringUp(\n session: DocumentSession,\n snapshot: { pageCount: number; pages: DocumentMeta['pages'] },\n ): Promise<void> {\n session.phase = 'bringup';\n // The render policy is a document FACT (Pattern A, like the page\n // registry): async on the engine contract, materialized ONCE here —\n // pre-publish — so every consumer reads it synchronously off the meta\n // and no \"policy still resolving\" state exists anywhere downstream.\n // Best-effort by design: no render service, or a failed read, means\n // `continuous` — a policy hiccup must never block a document open.\n let renderPolicy: EngineRenderPolicy = CONTINUOUS_RENDER_POLICY;\n try {\n renderPolicy = (await session.handle!.render?.policy()) ?? CONTINUOUS_RENDER_POLICY;\n } catch {\n /* unreachable policy = continuous */\n }\n checkpoint(session);\n session.stagedMeta = {\n id: session.id,\n name: session.name,\n pageCount: snapshot.pageCount,\n pages: snapshot.pages,\n revision: 0,\n renderPolicy,\n };\n\n // Document mutation events (rotate/move/delete) replace the page\n // registry in place — the snapshot they carry is byte-identical to\n // pages.list(), so this is a direct swap, no merge. Own mutations and\n // remote (collaborator) mutations arrive identically; the handler is\n // origin-agnostic, as the event model intends.\n const unsubscribeEvents = session.handle!.events.subscribe((event) => {\n const layout = layoutFromEvent(event);\n if (!layout) return;\n const now = store.getCore();\n const existing = now.documents[session.id];\n if (!existing) return; // pre-commit or closed — registry not published\n const updated: DocumentMeta = {\n ...existing,\n pageCount: layout.pageCount,\n pages: layout.pages,\n revision: existing.revision + 1,\n };\n store.setCore(\n { documents: { ...now.documents, [session.id]: updated } },\n { type: CORE_DOCUMENT_PAGES_UPDATED },\n );\n });\n session.scope.defer(unsubscribeEvents);\n\n for (const plugin of documentScopedPlugins) {\n const key = sliceKey(plugin.id, session.id);\n store.registerSlice(key, reducerOf(plugin), initialStateOf(plugin));\n session.scope.defer(() => store.removeSlice(key)); // LIFO ⇒ reverse dependency order\n }\n for (const plugin of documentScopedPlugins) {\n await plugin.init?.(createPluginContext(services, plugin, session));\n checkpoint(session);\n }\n // Effect SETUP is part of the transaction (it can throw); the callbacks\n // it registers fire post-commit and are isolated by the store instead.\n for (const plugin of documentScopedPlugins) {\n plugin.effects?.(createEffectContext(services, plugin, session));\n }\n checkpoint(session);\n }\n\n async function openDocument(input: OpenSource, options?: OpenDocumentOptions): Promise<string> {\n guardUsable('documents.open()');\n const { activate, name, ...engineOptions } = options ?? {};\n\n // 1. Reserve the tab slot SYNCHRONOUSLY (before the first await): id,\n // order position, and activation are decided at request time; only the\n // content arrives at completion time. Fire-and-forget concurrent opens\n // therefore keep call order as tab order.\n const requestedId =\n typeof input === 'function' ? nextTicket() : (idOfInput(input) ?? nextTicket());\n if (sessions.has(requestedId)) {\n throw new Error(`[documents] document already open: ${requestedId}`);\n }\n const session = createSession(requestedId, name);\n sessions.set(session.id, session);\n publishPendingSlot(session, activate ?? true);\n\n return beginOperation(session, async () => {\n try {\n const source =\n typeof input === 'function'\n ? await engineCall(session, Promise.resolve(input(session.cancel.signal)))\n : input;\n checkpoint(session);\n const sourceId = idOfInput(source);\n if (sourceId) rekeySession(session, sourceId);\n\n // A handle that lands after close() won still gets closed — see engineCall.\n const handle = await engineCall(session, engine.open(source, engineOptions), (late) =>\n late.close(),\n );\n session.handle = handle;\n session.scope.defer(() => handle.close()); // paired at acquisition — no exit path leaks it\n checkpoint(session);\n if (handle.id !== session.id) rekeySession(session, handle.id);\n\n // 2. A password-locked handle parks here — BEFORE pages.list(), which\n // would reject on a locked document. `documents.unlock()` finishes\n // the job later. `passwordProvided` records that a supplied password\n // was already tried and rejected (drives the \"incorrect\" copy).\n if (handle.security?.passwordPrompt?.state === 'required') {\n const passwordProvided =\n ('password' in engineOptions && engineOptions.password != null) ||\n passwordOfInput(source) != null;\n session.phase = 'locked';\n publishLocked(session, passwordProvided);\n return session.id;\n }\n\n const snapshot = await engineCall(session, handle.pages.list());\n checkpoint(session);\n await bringUp(session, snapshot);\n commitReady(session);\n return session.id;\n } catch (error) {\n // Close won the race: close() owns unpublish + disposal; just get out\n // of its way, rejecting with the typed cancellation either way.\n if (isCancelled(error)) throw error;\n if (session.phase === 'closing' || isAbortLike(error)) {\n throw new CancelledError(`closed while opening: ${session.id}`);\n }\n // 3. Real failure: ROLLBACK (scope releases exactly what was acquired,\n // however far we got), then park the tab as `error` — closable, and\n // reopenable after close. The document was never `ready`.\n await session.scope.dispose();\n session.phase = 'error';\n publishError(session, error);\n throw error;\n }\n });\n }\n\n async function unlockDocument(id: string, input: { password: string }): Promise<void> {\n guardUsable('documents.unlock()');\n const session = sessions.get(id);\n if (!session || session.phase !== 'locked' || !session.handle) {\n throw new Error(`[documents] document is not locked: ${id}`);\n }\n const handle = session.handle;\n return beginOperation(session, async () => {\n // Engine-agnostic by design: local loads the parked worker bytes, cloud\n // POSTs /access — same call, same result. A WRONG PASSWORD rejects here\n // and nothing changes: the document stays locked, unlock is retryable.\n try {\n await engineCall(session, handle.security.unlock({ password: input.password }));\n } catch (error) {\n if (session.phase === 'closing' || isAbortLike(error)) {\n throw new CancelledError(`closed while opening: ${session.id}`);\n }\n throw error; // still locked — deliberately no state change\n }\n checkpoint(session);\n // Past the password: failures from here are REAL open failures — the\n // same rollback + `error` policy as the open path.\n try {\n const snapshot = await engineCall(session, handle.pages.list());\n checkpoint(session);\n await bringUp(session, snapshot);\n commitReady(session);\n } catch (error) {\n if (isCancelled(error)) throw error;\n if (session.phase === 'closing' || isAbortLike(error)) {\n throw new CancelledError(`closed while opening: ${session.id}`);\n }\n await session.scope.dispose();\n session.phase = 'error';\n publishError(session, error);\n throw error;\n }\n });\n }\n\n async function closeDocument(documentId: string): Promise<void> {\n const session = sessions.get(documentId);\n if (!session) return; // idempotent — unknown or already closed\n await session.close();\n }\n\n function reorder(next: string[]) {\n store.setCore({ order: next }, { type: CORE_ORDER_CHANGED });\n }\n\n const documents: DocumentsCapability = {\n open: openDocument,\n openAll: (docs) => {\n guardUsable('documents.openAll()');\n // Fire-and-forget on purpose: each open() reserves its tab slot\n // synchronously, so tabs exist immediately in array order; exactly one\n // activation, decided here at request time. Failures are tab state\n // (`error`/`locked`), never unhandled rejections.\n const activeIndex = Math.max(\n 0,\n docs.findIndex((d) => d.active),\n );\n docs.forEach(({ source, active: _active, ...options }, index) => {\n void openDocument(source, { ...options, activate: index === activeIndex }).catch(() => {});\n });\n },\n unlock: unlockDocument,\n close: closeDocument,\n closeAll: async () => {\n for (const id of [...store.getCore().order]) await closeDocument(id);\n },\n setActive: (id) => {\n const core = store.getCore();\n // Pending tabs are selectable — a loading or locked tab is a real tab.\n if (core.documents[id] || core.pending[id])\n store.setCore({ activeId: id }, { type: CORE_ACTIVE_CHANGED });\n },\n activeId: () => store.getCore().activeId,\n list: (): DocInfo[] =>\n store.getCore().order.map((id) => {\n const core = store.getCore();\n const meta = core.documents[id];\n return meta ? toDocInfo(meta) : pendingToDocInfo(core.pending[id]);\n }),\n get: (id) => {\n const core = store.getCore();\n const meta = core.documents[id];\n if (meta) return toDocInfo(meta);\n const pending = core.pending[id];\n return pending ? pendingToDocInfo(pending) : null;\n },\n has: (id) => {\n const core = store.getCore();\n return core.documents[id] !== undefined || core.pending[id] !== undefined;\n },\n count: () => store.getCore().order.length,\n order: () => [...store.getCore().order],\n move: (id, toIndex) => {\n const core = store.getCore();\n if (!core.documents[id] && !core.pending[id]) return;\n const without = core.order.filter((x) => x !== id);\n const clamped = Math.max(0, Math.min(toIndex, without.length));\n without.splice(clamped, 0, id);\n reorder(without);\n },\n swap: (a, b) => {\n const core = store.getCore();\n const indexA = core.order.indexOf(a);\n const indexB = core.order.indexOf(b);\n if (indexA < 0 || indexB < 0) return;\n const next = [...core.order];\n next[indexA] = b;\n next[indexB] = a;\n reorder(next);\n },\n // Document IO — siblings of open/close, straight to the live engine handle.\n download: (id, opts) => {\n const handle = documentHandle(id);\n if (!handle) return Promise.reject(new Error('[documents] no document to download'));\n return handle.download(opts);\n },\n downloadLayer: (id) => {\n const handle = documentHandle(id);\n if (!handle) return Promise.reject(new Error('[documents] no document to download'));\n if (!handle.downloadLayer) {\n return Promise.reject(\n new Error(\n '[documents] this engine cannot export a layer (open with a layer on the local engine)',\n ),\n );\n }\n return handle.downloadLayer();\n },\n // The permissions.md chrome exception: print/download are kernel verbs\n // with 1:1 capabilities, so their authority question is answered here.\n allows: (cap, id) => documentHandle(id)?.security.allows(cap) ?? false,\n };\n workspaceCapabilities.set(DocumentsToken, documents);\n\n // ── workspace plugins: seed slices, then build their capabilities ────────────\n for (const plugin of plan.ordered) {\n if (!isDocumentScoped(plugin))\n store.registerSlice(plugin.id, reducerOf(plugin), initialStateOf(plugin));\n }\n for (const plugin of plan.ordered) {\n if (!isDocumentScoped(plugin) && plugin.token && plugin.capability) {\n workspaceCapabilities.set(\n plugin.token,\n plugin.capability(createPluginContext(services, plugin)),\n );\n }\n }\n\n return {\n engine,\n documents,\n capability: resolveCapability,\n tryCapability: tryResolveCapability,\n scopeOf: plan.scopeOf,\n subscribe: store.subscribe,\n getState: store.getState,\n status: () => status,\n start: () => {\n if (status === 'destroying' || status === 'destroyed' || status === 'failed') {\n return Promise.reject(new Error(`[kernel] start() on a ${status} kernel`));\n }\n return (startPromise ??= (async () => {\n status = 'starting';\n try {\n for (const plugin of plan.ordered) {\n if (status !== 'starting') return; // destroy() raced us — stop within one init\n if (!isDocumentScoped(plugin))\n await plugin.init?.(createPluginContext(services, plugin));\n }\n if (status !== 'starting') return;\n for (const plugin of plan.ordered) {\n if (!isDocumentScoped(plugin)) plugin.effects?.(createEffectContext(services, plugin));\n }\n status = 'started';\n } catch (error) {\n // Startup failure unwinds everything construction + start registered;\n // the kernel is terminally failed (destroy() remains legal, and idle).\n status = 'failed';\n await workspaceScope.dispose();\n workspaceCapabilities.clear();\n throw error;\n }\n })());\n },\n destroy: () => {\n return (destroyPromise ??= (async () => {\n const wasFailed = status === 'failed';\n status = 'destroying'; // an in-flight start exits at its next check\n await startPromise?.catch((error) => {\n if (!isCancelled(error) && !wasFailed) report(error);\n });\n // Close every session — the RESOURCE-owning map, not store.order: a\n // session mid-close is already unpublished but still needs joining.\n await Promise.allSettled([...sessions.values()].map((session) => session.close()));\n sessions.clear();\n await workspaceScope.dispose();\n workspaceCapabilities.clear();\n store.destroy();\n status = 'destroyed';\n })());\n },\n };\n}\n","import type { Unsubscribe } from './types';\n\n/**\n * Subscribe-only face of a plugin event — the ONE event shape a capability\n * exposes (`onX: EventHook<XEvent>`). Events carry occurrences, never state:\n * a late subscriber that needs the current value uses a query + selector\n * instead. Payloads are plain serializable objects.\n */\nexport type EventHook<T> = (listener: (event: T) => void) => Unsubscribe;\n\n/**\n * The plugin-private pair behind one capability event. Expose `.on` on the\n * capability; keep `emit`/`dispose` inside the plugin. Because the capability\n * member is the bare function type, the emitting half is unreachable through\n * the public contract by construction.\n */\nexport interface EventHookSource<T> {\n readonly on: EventHook<T>;\n emit(event: T): void;\n dispose(): void;\n}\n\n/**\n * Create one capability event.\n *\n * Delivery contract: synchronous fan-out over a snapshot of the listener set\n * (listeners added or removed during an emit don't affect that emit); a\n * throwing listener is isolated — reported through `onListenerError`, never\n * allowed to break the emitting operation or its sibling listeners. After\n * `dispose()`, `on` returns an inert unsubscribe (teardown races never throw).\n * No value cache, no replay, no equality — state belongs to the store.\n */\nexport function createEventHook<T = void>(\n onListenerError?: (error: unknown) => void,\n): EventHookSource<T> {\n let listeners: Set<(event: T) => void> | null = new Set();\n return {\n on(listener) {\n if (!listeners) return () => {};\n listeners.add(listener);\n return () => void listeners?.delete(listener);\n },\n emit(event) {\n if (!listeners) return;\n for (const listener of [...listeners]) {\n try {\n listener(event);\n } catch (error) {\n onListenerError?.(error);\n }\n }\n },\n dispose() {\n listeners = null;\n },\n };\n}\n","/**\n * Small promise-tail queue: operations run strictly one at a time, in\n * submission order, and failures never poison later operations. The shared\n * serializer for per-document mutation/dispatch pipelines.\n *\n * Direction law for nested queues: an operation running on queue A may\n * enqueue into queue B and await it, but never the reverse on the same pair —\n * a B-operation enqueueing back into A self-deadlocks behind its caller.\n */\nexport function createSerialQueue(): <T>(operation: () => Promise<T>) => Promise<T> {\n let tail: Promise<void> = Promise.resolve();\n return <T>(operation: () => Promise<T>): Promise<T> => {\n const result = tail.then(operation, operation);\n tail = result.then(\n () => undefined,\n () => undefined,\n );\n return result;\n };\n}\n","export * from './types';\nexport * from './kernel';\nexport * from './event-hook';\nexport * from './serial-queue';\nexport { CancelledError, isCancelled } from './scope';\n\n// Re-export the engine contracts so plugins/adapters import them from @embedpdf/core.\nexport {\n AbortablePromise,\n CONTINUOUS_RENDER_POLICY,\n // The refusal shape of the permissions convention (permissions.md): a\n // plugin's optimistic gate rejects with the SAME error the engine throws.\n PermissionDenied,\n snapAppearanceScale,\n snapFullPageViewport,\n snapTileScale,\n} from '@embedpdf/engine-core/runtime';\nexport type {\n EngineRenderPolicy,\n PageHandle,\n PageRaster,\n PageRenderOptions,\n PageRenderTarget,\n PageRenderViewport,\n PageImageHandle,\n PageImageOptions,\n PageImageObjectUrl,\n PdfRect,\n} from '@embedpdf/engine-core/runtime';\n\nimport type { Action, CapabilityToken, PluginDef } from './types';\n\n/**\n * Create a typed capability token. `name` is the token's identity (debugging,\n * error messages); `options.hint` is the authored remedy the kernel appends to\n * the missing-dependency error, so the error contains its own fix.\n */\nexport function createCapabilityToken<T>(\n name: string,\n options?: { hint?: string },\n): CapabilityToken<T> {\n return { name, hint: options?.hint };\n}\n\n/** Identity helper that pins a plugin's generics. The real win is inference. */\nexport function definePlugin<S = unknown, A extends Action = Action, C = unknown>(\n def: PluginDef<S, A, C>,\n): PluginDef<S, A, C> {\n return def;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["DocumentsToken","toPluginErrorInfo","toPluginError","PluginError","createStore","createScope","DocumentsToken","createEventHook","CancelledError","isCancelled","createDocumentChanges","sessionAllows","permissionDenied","settle","cancellable","readWrapped","createPluginContext","createSettingsStore","timerClock","CONTINUOUS_RENDER_POLICY","createBurstGate","sliceKey","findPage","DocumentsToken","PluginError"],"sources":["../src/order.ts","../src/viewer-settings.ts","../src/kernel.ts","../src/compose.ts","../src/memo.ts","../src/state.ts","../src/documents-state.ts","../src/capability-handle.ts","../src/index.ts"],"sourcesContent":["import { DocumentsToken, type AnyPlugin, type CapabilityToken, type PluginScope } from './types';\n\n/**\n * The result of analysing a plugin list: a dependency-ordered list, plus lookups\n * for which plugin provides a token and what scope a token has. Pure and testable.\n */\nexport interface PluginPlan {\n readonly ordered: AnyPlugin[];\n providerOf(token: CapabilityToken<unknown>): AnyPlugin | undefined;\n scopeOf(token: CapabilityToken<unknown>): PluginScope;\n}\n\n/**\n * Validate the composition (fail fast, before anything is constructed) and\n * topologically sort plugins so dependencies are initialised before dependents.\n *\n * Rejected compositions, each with both names in the error:\n * - the same plugin definition installed twice, or two definitions sharing an id;\n * - two plugins providing the same capability token (no implicit last-wins —\n * an intentional replacement is a composition-time choice, made by\n * removing the old definition from the list);\n * - a workspace plugin that `requires` a document-scoped token (it has no\n * document to resolve it for; `ctx.forDocument()` is the explicit path);\n * - a required token no plugin provides;\n * - a dependency cycle.\n */\nexport function planPlugins(plugins: readonly AnyPlugin[]): PluginPlan {\n const byId = new Map<string, AnyPlugin>();\n const providerByToken = new Map<CapabilityToken<unknown>, AnyPlugin>();\n const scopeByToken = new Map<CapabilityToken<unknown>, PluginScope>([\n [DocumentsToken, 'workspace'],\n ]);\n\n for (const plugin of plugins) {\n const prior = byId.get(plugin.id);\n if (prior) {\n throw new Error(\n prior === plugin\n ? `[kernel] plugin \"${plugin.id}\" is installed twice.`\n : `[kernel] two different plugins use the id \"${plugin.id}\".`,\n );\n }\n byId.set(plugin.id, plugin);\n if (plugin.token) {\n const other = providerByToken.get(plugin.token);\n if (other) {\n throw new Error(\n `[kernel] capability \"${plugin.token.name}\" is provided by both \"${other.id}\" and \"${plugin.id}\".`,\n );\n }\n providerByToken.set(plugin.token, plugin);\n scopeByToken.set(plugin.token, plugin.scope ?? 'workspace');\n }\n }\n\n for (const plugin of plugins) {\n for (const required of plugin.requires ?? []) {\n // every required token must be provided (the documents token is always available)\n if (required !== DocumentsToken && !providerByToken.has(required)) {\n // The error carries its own fix: tokens author the remedy (hint), so a\n // forgotten dependency is a ten-second paste, not an investigation.\n throw new Error(\n `Plugin \"${plugin.id}\" requires capability \"${required.name}\", which no plugin provides` +\n (required.hint ? ` — ${required.hint}.` : '.'),\n );\n }\n if (plugin.scope !== 'document' && providerByToken.get(required)?.scope === 'document') {\n throw new Error(\n `[kernel] workspace plugin \"${plugin.id}\" requires document-scoped capability ` +\n `\"${required.name}\"; resolve it per document with ctx.forDocument() and declare it as optional.`,\n );\n }\n }\n }\n\n // depth-first topological sort (dependencies pushed before dependents)\n const ordered: AnyPlugin[] = [];\n const status = new Map<string, 'visiting' | 'done'>();\n const visit = (plugin: AnyPlugin) => {\n const state = status.get(plugin.id);\n if (state === 'done') return;\n if (state === 'visiting') throw new Error(`Dependency cycle involving plugin \"${plugin.id}\".`);\n status.set(plugin.id, 'visiting');\n for (const token of [...(plugin.requires ?? []), ...(plugin.optional ?? [])]) {\n const dependency = providerByToken.get(token);\n if (dependency && dependency !== plugin) visit(dependency);\n }\n status.set(plugin.id, 'done');\n ordered.push(plugin);\n };\n for (const plugin of plugins) visit(plugin);\n\n return {\n ordered,\n providerOf: (token) => providerByToken.get(token),\n scopeOf: (token) => scopeByToken.get(token) ?? 'workspace',\n };\n}\n","/**\n * The viewer's own settings: what every document it opens gets unless the document says\n * otherwise (who the user is, what they may do), and what every plugin's colors fall back to\n * (the accent, the page). The kernel keeps one store of them (`kernel.getSettings()`), so every\n * framework adapter reads and changes the same values: React's `<Viewer>` from its props.\n *\n * The colors are the last step of the lookup `@embedpdf/web`'s `paint()` builds: a part's CSS\n * variable, the variables of what it follows, then its setting, and the viewer's accent last.\n */\nimport type { Identity } from '@embedpdf/engine-core/runtime';\n\n/** How each page looks before its picture arrives, and around it. */\nexport interface ViewerPageSettings {\n /** The page before its picture arrives. */\n readonly background: string;\n /** The shadow under each page, as CSS `box-shadow`; `'none'` for none. */\n readonly shadow: string;\n}\n\nexport interface ViewerSettings {\n /**\n * Who the user is, for every document opened without an `identity` of its own: the author of\n * the annotations they make, and whom `:self` and `:group` permissions are checked against.\n * Null for nobody in particular. The local engine only; the cloud reads it from the token.\n */\n readonly identity: Identity | null;\n /**\n * What the user may do, as permissions, in every document opened without a `scope` of its\n * own. Null for everything. The local engine only; the cloud reads it from the token.\n */\n readonly scope: readonly string[] | null;\n /** The color every part without a color of its own follows. */\n readonly accent: string;\n readonly page: ViewerPageSettings;\n}\n\n/** The viewer's settings when the app gives none; the colors are the theming page's defaults. */\nexport const VIEWER_DEFAULTS: ViewerSettings = Object.freeze({\n identity: null,\n scope: null,\n accent: '#3858e9',\n page: Object.freeze({ background: '#ffffff', shadow: '0 6px 18px rgb(0 0 0 / 0.18)' }),\n});\n\n/** An identity is one person: a new one replaces the last instead of merging into it. */\nexport const VIEWER_WHOLE_SETTINGS = ['identity'] as const;\n\n/** The viewer settings a framework's viewer takes as its props or config; each one is optional. */\nexport interface ViewerSettingsInput {\n readonly identity?: Identity | null;\n readonly scope?: readonly string[] | null;\n readonly accent?: string;\n readonly page?: Partial<ViewerPageSettings>;\n}\n\n/**\n * The viewer's settings as a framework's viewer component gives them: a setting left out is its\n * default, and `page` fills in the side it leaves out. Every adapter's viewer starts the kernel\n * with this and passes it again when its props change.\n */\nexport function viewerSettingsOf({\n identity,\n scope,\n accent,\n page,\n}: ViewerSettingsInput): ViewerSettings {\n return {\n identity: identity ?? null,\n scope: scope ?? null,\n accent: accent ?? VIEWER_DEFAULTS.accent,\n page: { ...VIEWER_DEFAULTS.page, ...page },\n };\n}\n","import { createBurstGate } from './bursts';\nimport { createDocumentChanges } from './changes';\nimport { timerClock, type HostClock } from './clock';\nimport { createStore } from './store';\nimport {\n createPluginContext,\n markConnected,\n readWrapped,\n sliceKey,\n type ContextServices,\n type SessionRef,\n} from './context';\nimport type { SliceLease } from './store';\nimport { cancellable } from './cancellable';\nimport { createEventHook } from './event-hook';\nimport { PluginError, toPluginError, toPluginErrorInfo } from './errors';\nimport { findPage } from './page-of';\nimport { permissionDenied, sessionAllows } from './permissions';\nimport { settle } from './settle';\nimport {\n createSettingsStore,\n type DeepPartial,\n type SettingsApi,\n type SettingsOf,\n type SettingsStore,\n} from './settings';\nimport { planPlugins } from './order';\nimport { createScope, CancelledError, isCancelled, type Scope } from './scope';\nimport {\n DocumentsToken,\n type AnyPlugin,\n type CapabilityToken,\n type CoreState,\n type DocumentHandle,\n type DocumentInfo,\n type DocumentMeta,\n type DocumentsCapability,\n type Engine,\n type GlobalState,\n type OpenDocumentOptions,\n type OpenDocumentResult,\n type OpenInput,\n type OpenSource,\n type DocumentOpenedEvent,\n type DocumentOpenFailedEvent,\n type DocumentLockedEvent,\n type DocumentClosedEvent,\n type DocumentActiveChangedEvent,\n type DocumentPagesChangedEvent,\n type DocumentUnsavedChangesChangedEvent,\n type PendingMeta,\n type Permission,\n type PluginScope,\n type Unsubscribe,\n type UnlockOptions,\n type UrlSource,\n} from './types';\nimport { VIEWER_DEFAULTS, VIEWER_WHOLE_SETTINGS, type ViewerSettings } from './viewer-settings';\nimport {\n CONTINUOUS_RENDER_POLICY,\n isLocalDocument,\n pageRefsEqual,\n type DocumentEvent,\n type EngineRenderPolicy,\n type PageLayout,\n} from '@embedpdf/engine-core/runtime';\n\nconst EMPTY_PAGES: readonly PageLayout[] = Object.freeze([]);\n\n/** The new page registry a document mutation carries, or null for events that\n * don't change page structure (annotations, metadata). The snapshot is the\n * same shape `pages.list()` returns, so callers swap it in directly. */\nfunction layoutFromEvent(event: DocumentEvent) {\n switch (event.type) {\n case 'pages.reordered':\n case 'pages.rotated':\n case 'pages.deleted':\n case 'pages.inserted':\n return event.layout;\n default:\n return null;\n }\n}\n\n/** Kernel lifecycle. Monotonic: created → starting → started, then destroying →\n * destroyed; `failed` is a terminal branch off starting. */\nexport type KernelStatus =\n | 'created'\n | 'starting'\n | 'started'\n | 'failed'\n | 'destroying'\n | 'destroyed';\n\n/**\n * The viewer: the engine, the plugins and the open documents. Its own settings\n * (`getSettings()`, `updateSettings()`, …) are what every document gets unless it says\n * otherwise, and what every plugin's colors fall back to (`viewer-settings.ts`).\n */\nexport interface Kernel extends SettingsApi<ViewerSettings> {\n readonly engine: Engine;\n readonly documents: DocumentsCapability;\n /**\n * Resolve a capability. For document-scoped tokens, `documentId` defaults to the active doc.\n * For a workspace token, a `documentId` gives the capability as seen from that document's\n * scope (`PluginDef.inScope`; the documents capability too): the same object on every call\n * while the document is open.\n */\n capability<T>(token: CapabilityToken<T>, documentId?: string): T;\n /**\n * Total sibling of `capability()`: `null` instead of throwing — no\n * provider, no document, or a document that isn't `ready` yet. This is the\n * method adapters subscribe to (`useKernelValue`-style): resolution is a\n * value derived from kernel state, not a pure function of its arguments —\n * a pending document's promotion changes the result while the id stays the\n * same, so caching a `capability()` call by id goes stale. The returned\n * instance is reference-stable per (plugin, document), so equality-cached\n * reads don't churn.\n */\n tryCapability<T>(token: CapabilityToken<T>, documentId?: string): T | null;\n /** A token's scope — adapters use this to decide whether to bind a document. */\n scopeOf(token: CapabilityToken<unknown>): PluginScope;\n /**\n * A plugin's settings calls, with or without a document open: settings belong to the plugin\n * as the app registered it, not to a document. For adapters (a settings hook renders before\n * the first document opens) and for an app that changes them before opening one. Throws\n * when no installed plugin provides `token`, or its plugin declares no settings.\n */\n settingsOf<C>(token: CapabilityToken<C>): SettingsApi<SettingsOf<C>>;\n subscribe(listener: () => void): Unsubscribe;\n getState(): GlobalState;\n status(): KernelStatus;\n /** Idempotent: a second call joins the first. Throws after destroy(). */\n start(): Promise<void>;\n /**\n * Idempotent: every call returns the same promise. Joins an in-flight\n * start(), closes every document (their engine handles included), then\n * unwinds workspace resources. Never throws.\n */\n destroy(): Promise<void>;\n}\n\nconst isDocumentScoped = (plugin: AnyPlugin) => plugin.scope === 'document';\nconst initialStateOf = (plugin: AnyPlugin): unknown => plugin.state?.();\n/**\n * A document's public fields, one object per registry entry: the entries are replaced, never\n * changed, so the same entry gives the same object, and readers re-render only for their own.\n */\nconst infoByEntry = new WeakMap<DocumentMeta | PendingMeta, DocumentInfo>();\nfunction documentInfoOf(entry: DocumentMeta | PendingMeta): DocumentInfo {\n let info = infoByEntry.get(entry);\n if (!info) {\n info = 'pages' in entry ? readyInfo(entry) : pendingInfo(entry);\n infoByEntry.set(entry, info);\n }\n return info;\n}\nconst readyInfo = (meta: DocumentMeta): DocumentInfo =>\n Object.freeze({\n id: meta.id,\n name: meta.name,\n status: 'ready',\n pageCount: meta.pageCount,\n hasUnsavedChanges: meta.hasUnsavedChanges,\n });\nconst pendingInfo = (meta: PendingMeta): DocumentInfo =>\n Object.freeze({\n id: meta.id,\n name: meta.name,\n status: meta.status,\n pageCount: 0,\n hasUnsavedChanges: false,\n ...(meta.status === 'locked' ? { passwordProvided: meta.passwordProvided ?? false } : {}),\n ...(meta.status === 'error' && meta.error !== undefined\n ? { error: toPluginErrorInfo(toPluginError('documents', meta.error)) }\n : {}),\n });\n\n/** Whether an event is a change to the document, one a download would carry. */\nconst changesDocument = (event: DocumentEvent): boolean =>\n event.type !== 'stream.desynced' &&\n event.type !== 'document.versioned' &&\n event.type !== 'signatures.prepared' &&\n event.type !== 'signatures.cancelled';\n\n/** The part of `fetch` a `{ kind: 'url' }` source needs. */\ntype FetchFile = (\n url: string,\n init: { readonly signal: AbortSignal },\n) => Promise<{\n readonly ok: boolean;\n readonly status: number;\n arrayBuffer(): Promise<ArrayBuffer>;\n}>;\n\n/** Download a URL source's file; its tab shows the failure when the server says no. */\nasync function fetchUrlSource(source: UrlSource, signal: AbortSignal): Promise<OpenInput> {\n const fetchFile = (globalThis as { fetch?: FetchFile }).fetch;\n if (!fetchFile) {\n throw new PluginError('unsupported', 'documents', `can't download ${source.url}: no fetch()`);\n }\n const response = await fetchFile(source.url, { signal });\n if (!response.ok) {\n throw new PluginError(\n response.status === 404 ? 'not-found' : 'operation-failed',\n 'documents',\n `couldn't download ${source.url} (HTTP ${response.status})`,\n );\n }\n return { kind: 'bytes', bytes: new Uint8Array(await response.arrayBuffer()) };\n}\n\nconst isUrlSource = (source: OpenInput | UrlSource): source is UrlSource => source.kind === 'url';\n\nconst noLayer = () =>\n new PluginError('unsupported', 'documents', 'only the local engine gives a layer to download');\n\n/**\n * The documents capability as seen from inside one document's scope: every call whose document\n * is optional uses that document instead of the active one. The rest is the registry itself.\n */\nconst documentsInScope = (\n documents: DocumentsCapability,\n documentId: string,\n): DocumentsCapability => ({\n ...documents,\n get: (id = documentId) => documents.get(id),\n download: (id = documentId, options) => documents.download(id, options),\n downloadLayer: (id = documentId, options) => documents.downloadLayer(id, options),\n canDownload: (id = documentId) => documents.canDownload(id),\n canPrint: (id = documentId) => documents.canPrint(id),\n listPages: (id = documentId) => documents.listPages(id),\n getPage: (page, id = documentId) => documents.getPage(page, id),\n getPageIndex: (ref, id = documentId) => documents.getPageIndex(ref, id),\n getRevision: (id = documentId) => documents.getRevision(id),\n});\n\n/** The stable id an input implies, if it carries one ('bytes'/'layerBytes'/'id'). */\nconst idOfInput = (input: OpenInput | UrlSource): string | null =>\n 'id' in input && typeof input.id === 'string' ? input.id : null;\n\n/**\n * Everything one open document owns, in one place: the engine handle, the\n * resource scope (event subs, slices, plugin cleanups, the handle's own\n * close), the capability instances, and the in-flight lifecycle operation.\n * The session is the document's lifecycle; the store's `documents`/`pending`\n * entries are its UI projection.\n *\n * opening — slot reserved; source resolving / engine opening\n * locked — parked on a password; the scope already owns handle.close\n * bringup — post-security: slices, construction, connection; not yet published\n * ready — committed; the only phase adapters resolve capabilities in\n * error — open failed after the slot was reserved; resources disposed\n * closing — close() won; unpublished, joining the operation, disposing\n */\ninterface DocumentSession extends SessionRef {\n name?: string;\n /** What `open()` was called with, so a failed tab can `retry()`. */\n source: OpenSource | null;\n openOptions: OpenDocumentOptions | undefined;\n phase: 'opening' | 'locked' | 'bringup' | 'ready' | 'error' | 'closing';\n /** In-flight open/unlock — close() cancels, then joins this before disposing,\n * so \"close resolved\" means \"no producer is still acquiring resources\". */\n operation: Promise<unknown> | null;\n /** The current engine call, retained so close() can abort real worker-side\n * work instead of waiting for it to land at a checkpoint. */\n engineOp: { abort(reason?: unknown): void } | null;\n cancel: AbortController;\n capabilities: Map<AnyPlugin, unknown>;\n /**\n * The plugins whose `create()` is running. A read that reaches one of them meanwhile finds\n * no capability yet, instead of building it a second time. (Listeners can't cause one: the\n * store holds its notifications while a document's plugins are built.)\n */\n building: Set<AnyPlugin>;\n /** Workspace capabilities as seen from this document's scope (`PluginDef.inScope`), by token. */\n views: Map<CapabilityToken<unknown>, unknown>;\n /** `connect` halves of `create()`, run once every instance of the document is built. */\n connectors: Map<AnyPlugin, () => void>;\n /** How many changes the document has had since it opened: a download that read them all clears `hasUnsavedChanges`. */\n changeCount: number;\n close(): Promise<void>;\n}\n\nlet instanceCounter = 0;\n\n/** Engine rejections carry AbortError when close() aborts the live call;\n * match structurally so test fakes with plain promises still work. */\nconst isAbortLike = (error: unknown): boolean =>\n error instanceof Error && error.name === 'AbortError';\n\n/**\n * Assemble a kernel from an engine + plugins.\n *\n * planPlugins — validate dependencies, order them\n * resolveCapability — workspace singletons (or their view from a document's scope),\n * or per-document instances built lazily\n * settings — one store per plugin registration, built up front, shared\n * by its instances and readable with no document\n * document lifecycle — one DocumentSession per document: transactional open\n * (publish-last), one idempotent close for every phase\n * start / destroy — explicit status machine; destroy closes everything\n *\n * The kernel closes every handle it opened; it never destroys the engine —\n * ownership follows acquisition, and the engine was handed in by the caller.\n */\nexport function createKernel(config: {\n engine: Engine;\n plugins: AnyPlugin[];\n /** The viewer's own settings, over `VIEWER_DEFAULTS`: what `resetSettings()` goes back to. */\n settings?: DeepPartial<ViewerSettings>;\n /** Observability seam: teardown, listener and join failures land here. Default: console.error. */\n report?: (error: unknown) => void;\n /**\n * The host's time: what plugins' `ctx.clock` schedules on. A browser passes\n * `browserClock()` from `@embedpdf/web`, for frames. Default: `timerClock`,\n * timers and no frames.\n */\n clock?: HostClock;\n}): Kernel {\n const { engine, plugins } = config;\n const report = config.report ?? ((error: unknown) => console.error('[kernel]', error));\n const store = createStore(report);\n const plan = planPlugins(plugins);\n const documentScopedPlugins = plan.ordered.filter(isDocumentScoped);\n\n const workspaceCapabilities = new Map<CapabilityToken<unknown>, unknown>();\n const workspaceLeases = new Map<AnyPlugin, SliceLease<unknown>>();\n const workspaceConnectors = new Map<AnyPlugin, () => void>();\n const workspaceCancel = new AbortController();\n const workspaceScope = createScope(report);\n /** How each workspace capability looks from inside a document's scope, by token. The\n * documents capability is built in, so its view is declared here; plugins declare theirs. */\n const inScopeByToken = new Map<CapabilityToken<unknown>, NonNullable<AnyPlugin['inScope']>>([\n [DocumentsToken, documentsInScope],\n ]);\n for (const plugin of plan.ordered) {\n if (plugin.token && plugin.inScope && !isDocumentScoped(plugin)) {\n inScopeByToken.set(plugin.token, plugin.inScope);\n }\n }\n\n // ── lifecycle events (the kernel primitive; disposed at destroy) ──────────\n const opened = createEventHook<DocumentOpenedEvent>(report);\n const openFailed = createEventHook<DocumentOpenFailedEvent>(report);\n const locked = createEventHook<DocumentLockedEvent>(report);\n const closed = createEventHook<DocumentClosedEvent>(report);\n const activeChanged = createEventHook<DocumentActiveChangedEvent>(report);\n const pagesChanged = createEventHook<DocumentPagesChangedEvent>(report);\n const unsavedChangesChanged = createEventHook<DocumentUnsavedChangesChangedEvent>(report);\n workspaceScope.defer(() => {\n for (const hook of [\n opened,\n openFailed,\n locked,\n closed,\n activeChanged,\n pagesChanged,\n unsavedChangesChanged,\n ])\n hook.dispose();\n });\n /** Every core write goes through here so an active-tab change is observed exactly once. */\n const setCore = (patch: Partial<CoreState>): void => {\n const before = store.getCore().activeId;\n store.setCore(patch);\n const after = store.getCore().activeId;\n if (before !== after) activeChanged.emit({ documentId: after, previousDocumentId: before });\n };\n const sessions = new Map<string, DocumentSession>();\n\n let status: KernelStatus = 'created';\n let startPromise: Promise<void> | null = null;\n let destroyPromise: Promise<void> | null = null;\n\n const guardUsable = (what: string) => {\n if (status === 'failed' || status === 'destroying' || status === 'destroyed') {\n throw new Error(`[kernel] ${what} on a ${status} kernel`);\n }\n };\n\n // ── sessions ─────────────────────────────────────────────────────────────────\n\n const checkpoint = (session: DocumentSession) => {\n if (session.cancel.signal.aborted) {\n throw new CancelledError(`closed while opening: ${session.id}`);\n }\n };\n\n /**\n * Await an engine/network call under the session's cancellation:\n * - the call is retained so close() can abort real worker-side work\n * (`AbortablePromise`), and\n * - the await races the cancellation, so close()'s join never blocks on a\n * call that cannot be aborted (a plain-promise engine, a stuck fetch).\n * When cancellation wins but the call later lands anyway, `onLateResult`\n * routes the result into the session scope — whose late-defer rule runs it\n * immediately after disposal — so a late-arriving resource cannot leak.\n * (Plugin inits are deliberately not raced: they are first-party code that\n * close() joins to completion; only unbounded external waits are raced.)\n */\n async function engineCall<T>(\n session: DocumentSession,\n call: Promise<T>,\n onLateResult?: (value: T) => void | Promise<void>,\n ): Promise<T> {\n const abortable = call as Promise<T> & { abort?: (reason?: unknown) => void };\n session.engineOp = typeof abortable.abort === 'function' ? (abortable as never) : null;\n const signal = session.cancel.signal;\n let onAbort: (() => void) | undefined;\n const cancellation = new Promise<never>((_, reject) => {\n onAbort = () => reject(new CancelledError(`closed while opening: ${session.id}`));\n if (signal.aborted) onAbort();\n else signal.addEventListener('abort', onAbort, { once: true });\n });\n try {\n return await Promise.race([call, cancellation]);\n } catch (error) {\n if (isCancelled(error)) {\n void call.then(\n (value) => {\n if (onLateResult) session.scope.defer(() => onLateResult(value));\n },\n () => {}, // the abandoned call's own rejection is not separately actionable\n );\n }\n throw error;\n } finally {\n if (onAbort) signal.removeEventListener('abort', onAbort);\n session.engineOp = null;\n }\n }\n\n function createSession(id: string, name: string | undefined): DocumentSession {\n const cancel = new AbortController();\n const session: DocumentSession = {\n id,\n instanceId: `${id}#${++instanceCounter}`,\n name,\n source: null,\n openOptions: undefined,\n phase: 'opening',\n handle: null,\n stagedMeta: null,\n scope: createScope(report),\n operation: null,\n engineOp: null,\n cancel,\n signal: cancel.signal,\n capabilities: new Map(),\n building: new Set(),\n views: new Map(),\n connectors: new Map(),\n leases: new Map(),\n settleFlushes: new Set(),\n downloadWraps: [],\n changes: createDocumentChanges({\n handle: () => session.handle,\n notify: () => store.notify(),\n report,\n }),\n changeCount: 0,\n close: () => closeSession(session),\n };\n // A download waits for every change: open holds are sent, and the answers awaited.\n session.settleFlushes.add(() => session.changes.settle());\n session.scope.defer(() => session.changes.close());\n return session;\n }\n\n /** One transition at a time per session (open, then possibly unlock). */\n function beginOperation<T>(session: DocumentSession, run: () => Promise<T>): Promise<T> {\n if (session.operation) {\n throw new Error(`[documents] \"${session.id}\" already has an active transition`);\n }\n const operation = run().finally(() => {\n if (session.operation === operation) session.operation = null;\n });\n session.operation = operation;\n return operation;\n }\n\n /** Atomic ticket → real-id reconciliation: the sessions map, the store slot,\n * order position, and activation all move together, or not at all. */\n function rekeySession(session: DocumentSession, nextId: string): void {\n const previousId = session.id;\n if (previousId === nextId) return;\n const core = store.getCore();\n if (sessions.has(nextId) || core.documents[nextId] || core.pending[nextId]) {\n throw new PluginError('conflict', 'documents', `duplicate document id: ${nextId}`);\n }\n sessions.delete(previousId);\n session.id = nextId;\n sessions.set(nextId, session);\n session.views.clear(); // a view is bound to the id it was built for\n const slot = core.pending[previousId];\n if (slot) {\n const { [previousId]: _moved, ...pending } = core.pending;\n setCore({\n pending: { ...pending, [nextId]: { ...slot, id: nextId } },\n order: core.order.map((id) => (id === previousId ? nextId : id)),\n activeId: core.activeId === previousId ? nextId : core.activeId,\n });\n }\n }\n\n const closingSessions = new WeakMap<DocumentSession, Promise<void>>();\n function closeSession(session: DocumentSession): Promise<void> {\n const inFlight = closingSessions.get(session);\n if (inFlight) return inFlight;\n const closing = (async () => {\n session.phase = 'closing';\n unpublishSlot(session.id); // synchronous: the tab disappears now\n for (const lease of session.leases.values()) lease.revoke(); // write authority ends now\n // Cancel, join the producer, then drain its resources — in that order.\n // After the join, no known producer can register more resources; the\n // scope's late-defer rule covers anything unknowable.\n const reason = new CancelledError(`closed while opening: ${session.id}`);\n session.cancel.abort(reason);\n session.engineOp?.abort(reason);\n await session.operation?.catch((error) => {\n if (!isCancelled(error) && !isAbortLike(error)) report(error);\n });\n await session.scope.dispose();\n if (sessions.get(session.id) === session) sessions.delete(session.id);\n session.views.clear();\n closed.emit({ documentId: session.id });\n })();\n closingSessions.set(session, closing);\n return closing;\n }\n\n // ── store projection (publish/unpublish) ─────────────────────────────────────\n\n function nextActiveDocument(core: CoreState, removedId: string): string | null {\n if (core.activeId !== removedId) return core.activeId;\n const index = core.order.indexOf(removedId);\n const remaining = core.order.filter((id) => id !== removedId);\n return remaining.length === 0 ? null : (remaining[Math.max(0, index - 1)] ?? remaining[0]);\n }\n\n function publishPendingSlot(session: DocumentSession, activate: boolean): void {\n const core = store.getCore();\n setCore({\n pending: {\n ...core.pending,\n [session.id]: { id: session.id, name: session.name, status: 'loading' },\n },\n // A failed document opened again (`retry()`) keeps its place.\n order: core.order.includes(session.id) ? core.order : [...core.order, session.id],\n activeId: activate || core.activeId === null ? session.id : core.activeId,\n });\n }\n\n function publishLocked(session: DocumentSession, passwordProvided: boolean): void {\n const core = store.getCore();\n setCore({\n pending: {\n ...core.pending,\n [session.id]: { id: session.id, name: session.name, status: 'locked', passwordProvided },\n },\n });\n locked.emit({ documentId: session.id, passwordProvided });\n }\n\n function publishError(session: DocumentSession, error: unknown): void {\n const core = store.getCore();\n setCore({\n pending: {\n ...core.pending,\n [session.id]: { id: session.id, name: session.name, status: 'error', error },\n },\n });\n openFailed.emit({\n documentId: session.id,\n error: toPluginErrorInfo(toPluginError('documents', error)),\n });\n }\n\n function unpublishSlot(id: string): void {\n const core = store.getCore();\n if (!core.pending[id] && !core.documents[id]) return;\n const { [id]: _pending, ...pending } = core.pending;\n const { [id]: _document, ...documents } = core.documents;\n setCore({\n pending,\n documents,\n order: core.order.filter((other) => other !== id),\n activeId: nextActiveDocument(core, id),\n });\n }\n\n /** The one ready transition: swap the pending slot for the staged meta and\n * announce `onOpened`. Everything before this is unpublished and rolls\n * back by disposing the session scope; nothing after this can fail. */\n function commitReady(session: DocumentSession): void {\n session.phase = 'ready';\n const meta = session.stagedMeta!;\n const core = store.getCore();\n const { [session.id]: _resolved, ...pending } = core.pending;\n setCore({ documents: { ...core.documents, [session.id]: meta }, pending });\n opened.emit({ documentId: session.id, info: documentInfoOf(meta) });\n }\n\n /**\n * The document changed, or a download read every change it had: set `hasUnsavedChanges` and\n * announce a flip. Before the document is published, its staged meta carries the flag.\n */\n function setUnsavedChanges(session: DocumentSession, hasUnsavedChanges: boolean): void {\n const core = store.getCore();\n const published = core.documents[session.id];\n if (!published) {\n if (session.stagedMeta && session.stagedMeta.hasUnsavedChanges !== hasUnsavedChanges) {\n session.stagedMeta = { ...session.stagedMeta, hasUnsavedChanges };\n }\n return;\n }\n if (published.hasUnsavedChanges === hasUnsavedChanges) return;\n setCore({\n documents: { ...core.documents, [session.id]: { ...published, hasUnsavedChanges } },\n });\n unsavedChangesChanged.emit({ documentId: session.id, hasUnsavedChanges });\n }\n\n // ── capability resolution ────────────────────────────────────────────────────\n\n const sessionOf = (documentId?: string): DocumentSession | null => {\n const id = documentId ?? store.getCore().activeId;\n return id ? (sessions.get(id) ?? null) : null;\n };\n\n /** The handle plugins may touch: bring-up or ready — never locked, never\n * a handle whose close already won. */\n const documentHandle = (documentId?: string): DocumentHandle | null => {\n const session = sessionOf(documentId);\n if (!session) return null;\n return session.phase === 'bringup' || session.phase === 'ready' ? session.handle : null;\n };\n\n /** The session a per-document verb acts on: `documentId`, or the active document. */\n function readySession(documentId: string | undefined, verb: string): DocumentSession {\n const session = sessionOf(documentId);\n if (!session || !documentHandle(session.id)) {\n throw new PluginError('not-ready', 'documents', `no open document to ${verb}`);\n }\n return session;\n }\n\n const allowsOn = (documentId: string | undefined, permission: Permission): boolean => {\n const handle = documentHandle(documentId);\n return handle ? sessionAllows(handle.security, permission) : false;\n };\n\n /**\n * Read the document's file, with everything the user sees: refuse without `doc.download`, let\n * it settle (what its plugins held back is sent and answered, settle.ts), then read it inside\n * every `aroundDownload` wrap (the document's own save actions). A read that saw every change\n * the document had clears `hasUnsavedChanges`; a change during the read, or after it (a\n * did-save script), keeps it. Closing the document, or `signal`, cancels it.\n */\n async function downloadFile(\n documentId: string | undefined,\n signal: AbortSignal | undefined,\n verb: string,\n read: (handle: DocumentHandle) => Promise<Uint8Array>,\n refuse?: (handle: DocumentHandle) => PluginError | null,\n ): Promise<Uint8Array> {\n const session = readySession(documentId, verb);\n if (!allowsOn(session.id, 'doc.download')) {\n throw permissionDenied('documents', 'doc.download', `documents.${verb}`);\n }\n const refusal = refuse?.(session.handle!);\n if (refusal) throw refusal;\n try {\n await settle(session.settleFlushes, [signal, session.signal], report);\n // The same document, even if another became active while it settled.\n const handle = documentHandle(session.id);\n if (!handle) throw new PluginError('not-ready', 'documents', `no open document to ${verb}`);\n let readAt = -1;\n const readFile = () => {\n readAt = session.changeCount;\n return cancellable('documents', signal, read(handle));\n };\n const bytes = await readWrapped(session.downloadWraps, readFile);\n if (readAt === session.changeCount) setUnsavedChanges(session, false);\n return bytes;\n } catch (error) {\n throw toPluginError('documents', error);\n }\n }\n\n /** Whether the plugin's `create()` is running for this document: it has no capability yet. */\n const isBuilding = (plugin: AnyPlugin, session: DocumentSession): boolean =>\n session.building.has(plugin) && !session.capabilities.has(plugin);\n\n function buildDocumentCapability(plugin: AnyPlugin, session: DocumentSession): unknown {\n let capability = session.capabilities.get(plugin);\n if (!capability) {\n // A second instance would take the first one's state slice and mirror cells, leaving the\n // connected one with cells it can no longer write.\n if (session.building.has(plugin)) {\n throw new PluginError(\n 'not-ready',\n plugin.id,\n `\"${plugin.id}\" was asked for while it is being created`,\n );\n }\n const ctx = createPluginContext(services, plugin, session, session.signal, session.scope);\n session.building.add(plugin);\n let created: ReturnType<AnyPlugin['create']>;\n try {\n created = plugin.create(ctx);\n } finally {\n session.building.delete(plugin);\n }\n const { api, connect } = created;\n capability = api;\n session.connectors.set(plugin, () => {\n connect?.();\n markConnected(ctx);\n });\n session.capabilities.set(plugin, capability);\n }\n return capability;\n }\n\n /**\n * A workspace capability as a document's scope sees it: the view its plugin declares\n * (`inScope`), built once per document and kept on the session, so it is the same object on\n * every call and goes when the document closes. Without a document, or for a document the\n * kernel doesn't know, it is the capability itself.\n */\n function workspaceViewOf<T>(\n token: CapabilityToken<T>,\n capability: T,\n documentId: string | undefined,\n ): T {\n const inScope = inScopeByToken.get(token);\n const session = documentId === undefined ? undefined : sessions.get(documentId);\n if (!inScope || !session) return capability;\n let view = session.views.get(token);\n if (view === undefined) {\n view = inScope(capability, session.id);\n session.views.set(token, view);\n }\n return view as T;\n }\n\n function resolveCapability<T>(token: CapabilityToken<T>, documentId?: string): T {\n guardUsable(`capability(\"${token.name}\")`);\n const workspaceCapability = workspaceCapabilities.get(token);\n if (workspaceCapability) return workspaceViewOf(token, workspaceCapability as T, documentId);\n const provider = plan.providerOf(token);\n if (!provider) throw new Error(`No capability \"${token.name}\".`);\n const id = documentId ?? store.getCore().activeId;\n if (!id) throw new Error(`Capability \"${token.name}\" requires an active document.`);\n const session = sessions.get(id);\n if (!session) throw new Error(`Capability \"${token.name}\" unavailable: no document \"${id}\".`);\n if (session.phase === 'bringup' || session.phase === 'ready') {\n return buildDocumentCapability(provider, session) as T;\n }\n // Fail fast and truthfully: a loading/locked/error document has no\n // plugin instances yet. `useOptional*` adapters turn this into their\n // fallback; strict resolution surfaces the real state.\n const shown = session.phase === 'opening' ? 'loading' : session.phase;\n throw new Error(`Capability \"${token.name}\" unavailable: document \"${id}\" is ${shown}.`);\n }\n\n /** Internal total resolver: bring-up counts, so a plugin's `ctx.tryGet`\n * works during its own document's init. */\n function tryResolveInternal<T>(token: CapabilityToken<T>, documentId?: string): T | null {\n if (status === 'destroying' || status === 'destroyed') return null;\n const workspaceCapability = workspaceCapabilities.get(token);\n if (workspaceCapability) return workspaceViewOf(token, workspaceCapability as T, documentId);\n const provider = plan.providerOf(token);\n if (!provider) return null;\n const session = sessionOf(documentId);\n if (!session || (session.phase !== 'bringup' && session.phase !== 'ready')) return null;\n if (isBuilding(provider, session)) return null;\n return buildDocumentCapability(provider, session) as T;\n }\n\n /** Public total resolver — see `Kernel.tryCapability`. `ready` only: the\n * null→instance flip at commit time is the adapters' re-render signal. */\n function tryResolveCapability<T>(token: CapabilityToken<T>, documentId?: string): T | null {\n if (status === 'destroying' || status === 'destroyed') return null;\n const workspaceCapability = workspaceCapabilities.get(token);\n if (workspaceCapability) return workspaceViewOf(token, workspaceCapability as T, documentId);\n const provider = plan.providerOf(token);\n if (!provider) return null;\n const session = sessionOf(documentId);\n if (!session || session.phase !== 'ready') return null;\n if (isBuilding(provider, session)) return null;\n return buildDocumentCapability(provider, session) as T;\n }\n\n // ── settings ─────────────────────────────────────────────────────────────────\n // The viewer's own settings: one store, read when a document opens (its scope and identity)\n // and by every adapter that paints (the accent, the page).\n const viewerSettings = createSettingsStore<ViewerSettings>(\n { defaults: VIEWER_DEFAULTS, registered: config.settings, whole: VIEWER_WHOLE_SETTINGS },\n store.notify,\n report,\n );\n workspaceScope.defer(() => viewerSettings.dispose());\n\n // A plugin's settings belong to the plugin as the app registered it, not to one document.\n // Each registration's store is built here, before any plugin is created, so the settings\n // can be read and changed before any document opens. Every instance of the plugin then\n // shares it, so a change reaches every open document and the ones opened later, and wakes\n // every reader through the store's one change stream.\n const settingsStores = new Map<AnyPlugin, SettingsStore<object>>();\n for (const plugin of plan.ordered) {\n if (plugin.settings) {\n settingsStores.set(plugin, createSettingsStore(plugin.settings, store.notify, report));\n }\n }\n // Destroy ends the listeners; the values stay readable, so a reader unmounting late never throws.\n workspaceScope.defer(() => {\n for (const settings of settingsStores.values()) settings.dispose();\n });\n\n function settingsOf<C>(token: CapabilityToken<C>): SettingsApi<SettingsOf<C>> {\n const provider = plan.providerOf(token);\n if (!provider) throw new Error(`No capability \"${token.name}\".`);\n const settings = settingsStores.get(provider);\n if (!settings) throw new Error(`Plugin \"${provider.id}\" has no settings.`);\n // The token's capability type names the settings type; the store was built untyped.\n return settings.api as unknown as SettingsApi<SettingsOf<C>>;\n }\n\n const services: ContextServices = {\n engine,\n store,\n workspaceScope,\n workspaceSignal: workspaceCancel.signal,\n workspaceLeases,\n report,\n clock: config.clock ?? timerClock,\n resolveCapability,\n tryResolveCapability: tryResolveInternal,\n documentHandle,\n settingsStoreOf: (plugin) => settingsStores.get(plugin),\n };\n\n // ── document lifecycle ───────────────────────────────────────────────────────\n\n // Tickets for slots whose real id isn't known yet (thunk sources).\n let ticketCounter = 0;\n const nextTicket = () => `pending:${++ticketCounter}`;\n\n /** Slices, the registry's event subscription, and every plugin's\n * construction and connection — every step's release deferred into the\n * session scope, every await followed by a checkpoint. Runs entirely\n * pre-commit: a failure anywhere rolls the whole session back and the\n * document was never `ready`. */\n async function bringUp(\n session: DocumentSession,\n snapshot: { pageCount: number; pages: DocumentMeta['pages'] },\n ): Promise<void> {\n session.phase = 'bringup';\n // The render policy is a document fact (Pattern A, like the page\n // registry): async on the engine contract, materialized once here —\n // pre-publish — so every consumer reads it synchronously off the meta\n // and no \"policy still resolving\" state exists anywhere downstream.\n // Best-effort by design: no render service, or a failed read, means\n // `continuous` — a policy hiccup must never block a document open.\n let renderPolicy: EngineRenderPolicy = CONTINUOUS_RENDER_POLICY;\n try {\n renderPolicy = (await session.handle!.render?.getPolicy()) ?? CONTINUOUS_RENDER_POLICY;\n } catch {\n /* unreachable policy = continuous */\n }\n checkpoint(session);\n session.stagedMeta = {\n id: session.id,\n instanceId: session.instanceId,\n name: session.name,\n pageCount: snapshot.pageCount,\n pages: snapshot.pages,\n revision: 0,\n renderPolicy,\n hasUnsavedChanges: false,\n };\n\n // Changes are unsaved only where they live in this tab until a download (the local\n // engine); the cloud engine stores each one as it's made.\n const tracksChanges = isLocalDocument(session.handle!);\n // Document mutation events (rotate/move/delete) replace the page\n // registry in place — the snapshot they carry is byte-identical to\n // pages.list(), so this is a direct swap, no merge. Own mutations and\n // remote (collaborator) mutations arrive identically; the handler is\n // origin-agnostic, as the event model intends.\n // The kernel listens first and last, so a change's events land as one store update.\n const bursts = createBurstGate(store);\n session.scope.defer(bursts.close);\n const unsubscribeEvents = session.handle!.events.subscribe((event) => {\n bursts.first(event);\n if (tracksChanges && changesDocument(event)) {\n session.changeCount += 1;\n setUnsavedChanges(session, true);\n }\n const layout = layoutFromEvent(event);\n if (!layout) return;\n const now = store.getCore();\n const existing = now.documents[session.id];\n if (!existing) return; // pre-commit or closed — registry not published\n const updated: DocumentMeta = {\n ...existing,\n pageCount: layout.pageCount,\n pages: layout.pages,\n revision: existing.revision + 1,\n };\n setCore({ documents: { ...now.documents, [session.id]: updated } });\n pagesChanged.emit({\n documentId: session.id,\n revision: updated.revision,\n pages: updated.pages,\n });\n });\n session.scope.defer(unsubscribeEvents);\n\n for (const plugin of documentScopedPlugins) {\n // The lease is the write authority for this instance's slice. Revoking it\n // is the first teardown to run at close (LIFO), synchronously, so nothing\n // retained by this instance can reach a reopened document's state.\n const lease = store.lease(\n sliceKey(plugin.id, session.id),\n initialStateOf(plugin),\n session.instanceId,\n );\n session.leases.set(plugin, lease);\n session.scope.defer(() => lease.revoke()); // LIFO ⇒ reverse dependency order\n }\n // Construction and connection are part of the transaction (either can\n // throw); the callbacks they register fire post-commit and are isolated\n // by the store instead. Eager, in dependency order, with the store's change\n // stream held: a `create()` writes state, and a listener told of it at once\n // could read a plugin that isn't built yet and build it out of order,\n // before the plugins it finds while it's created. Listeners hear of the\n // whole bring-up in one pass, once every plugin is there.\n const release = store.hold();\n try {\n for (const plugin of documentScopedPlugins) {\n buildDocumentCapability(plugin, session);\n session.connectors.get(plugin)?.();\n }\n } finally {\n release();\n }\n session.scope.defer(session.handle!.events.subscribe((event) => bursts.last(event)));\n checkpoint(session);\n }\n\n /** What `open()` and `retry()` resolve: the document as its tab shows it, ready or locked. */\n function openedResult(session: DocumentSession): OpenDocumentResult {\n const core = store.getCore();\n const entry = core.documents[session.id] ?? core.pending[session.id];\n if (!entry) throw new PluginError('instance-closed', 'documents', `${session.id} was closed`);\n return { document: documentInfoOf(entry) };\n }\n\n // `async` on purpose: a refused reservation (duplicate id, destroyed kernel)\n // is a rejection, never a synchronous throw, like every other open failure.\n async function openDocument(\n input: OpenSource,\n options?: OpenDocumentOptions,\n slotId?: string,\n ): Promise<OpenDocumentResult> {\n const signal = options?.signal;\n if (signal?.aborted) {\n throw new PluginError('operation-cancelled', 'documents', 'the open was cancelled');\n }\n try {\n const { session, done } = reserveAndOpen(input, options, slotId);\n // Cancelling closes the document while it opens; once it's open, the signal is spent.\n const cancel = () => void session.close();\n signal?.addEventListener('abort', cancel, { once: true });\n try {\n return openedResult(await done);\n } finally {\n signal?.removeEventListener('abort', cancel);\n }\n } catch (error) {\n throw toPluginError('documents', error);\n }\n }\n\n /**\n * The engine's open options for a document: its own, and the viewer's `scope` and\n * `identity` where it has none. A document's own replace the viewer's, never merge.\n */\n function engineOptionsOf(options: OpenDocumentOptions | undefined) {\n const { activate: _activate, name: _name, signal: _signal, ...own } = options ?? {};\n const viewer = viewerSettings.api.getSettings();\n const scope = own.scope ?? viewer.scope ?? undefined;\n const identity = own.identity ?? viewer.identity ?? undefined;\n return {\n ...own,\n ...(scope !== undefined ? { scope } : {}),\n ...(identity !== undefined ? { identity } : {}),\n };\n }\n\n /**\n * Reserve the tab slot synchronously and start the open; returns the slot id at once.\n * `slotId` is a failed document's tab, which `retry()` opens again in place.\n */\n function reserveAndOpen(\n input: OpenSource,\n options?: OpenDocumentOptions,\n slotId?: string,\n ): { session: DocumentSession; done: Promise<DocumentSession> } {\n guardUsable('documents.open()');\n const engineOptions = engineOptionsOf(options);\n\n // 1. Reserve the tab slot synchronously (before the first await): id,\n // order position, and activation are decided at request time; only the\n // content arrives at completion time. Fire-and-forget concurrent opens\n // therefore keep call order as tab order.\n const requestedId =\n slotId ?? (typeof input === 'function' ? nextTicket() : (idOfInput(input) ?? nextTicket()));\n if (sessions.has(requestedId)) {\n throw new PluginError('conflict', 'documents', `document already open: ${requestedId}`);\n }\n const session = createSession(requestedId, options?.name);\n session.source = input;\n session.openOptions = options;\n sessions.set(session.id, session);\n publishPendingSlot(session, options?.activate ?? true);\n\n const done = beginOperation(session, async () => {\n try {\n const given =\n typeof input === 'function'\n ? await engineCall(session, Promise.resolve(input(session.cancel.signal)))\n : input;\n checkpoint(session);\n // A URL is downloaded here, under the loading tab; closing the tab stops the download.\n const source = isUrlSource(given)\n ? await engineCall(session, fetchUrlSource(given, session.cancel.signal))\n : given;\n checkpoint(session);\n const sourceId = idOfInput(source);\n if (sourceId) rekeySession(session, sourceId);\n\n // A handle that lands after close() won still gets closed — see engineCall.\n const handle = await engineCall(session, engine.open(source, engineOptions), (late) =>\n late.close(),\n );\n session.handle = handle;\n session.scope.defer(() => handle.close()); // paired at acquisition — no exit path leaks it\n checkpoint(session);\n if (handle.id !== session.id) rekeySession(session, handle.id);\n\n // 2. A password-locked handle parks here — before pages.list(), which\n // would reject on a locked document. `documents.unlock()` finishes\n // the job later. The prompt's `incorrect` says a supplied password\n // was tried and rejected (drives the \"incorrect\" copy).\n const prompt = handle.security?.passwordPrompt;\n if (prompt?.state === 'required') {\n session.phase = 'locked';\n publishLocked(session, prompt.incorrect);\n return session;\n }\n\n const snapshot = await engineCall(session, handle.pages.list());\n checkpoint(session);\n await bringUp(session, snapshot);\n commitReady(session);\n return session;\n } catch (error) {\n // Close won the race: close() owns unpublish + disposal; just get out\n // of its way, rejecting with the typed cancellation either way.\n if (isCancelled(error)) throw error;\n if (session.phase === 'closing' || isAbortLike(error)) {\n throw new CancelledError(`closed while opening: ${session.id}`);\n }\n // 3. Real failure: Rollback (scope releases exactly what was acquired,\n // however far we got), then park the tab as `error` — closable, and\n // reopenable after close. The document was never `ready`.\n await session.scope.dispose();\n session.phase = 'error';\n publishError(session, error);\n throw error;\n }\n });\n return { session, done };\n }\n\n async function unlockDocument(id: string, options: UnlockOptions): Promise<void> {\n guardUsable('documents.unlock()');\n const session = sessions.get(id);\n if (!session || session.phase !== 'locked' || !session.handle) {\n throw new PluginError('invalid-input', 'documents', `document is not locked: ${id}`);\n }\n const handle = session.handle;\n return beginOperation(session, async () => {\n // Engine-agnostic by design: local loads the parked worker bytes, cloud\n // POSTs /access — same call, same result. A wrong password rejects here\n // and nothing changes: the document stays locked, unlock is retryable,\n // and so does a password check the caller's signal stopped.\n try {\n await cancellable(\n 'documents',\n options.signal,\n engineCall(session, handle.security.unlock({ password: options.password })),\n );\n } catch (error) {\n if (session.phase === 'closing' || isAbortLike(error)) {\n throw new CancelledError(`closed while opening: ${session.id}`);\n }\n throw error; // still locked — deliberately no state change\n }\n checkpoint(session);\n // Past the password: failures from here are real open failures — the\n // same rollback + `error` policy as the open path.\n try {\n const snapshot = await engineCall(session, handle.pages.list());\n checkpoint(session);\n await bringUp(session, snapshot);\n commitReady(session);\n } catch (error) {\n if (isCancelled(error)) throw error;\n if (session.phase === 'closing' || isAbortLike(error)) {\n throw new CancelledError(`closed while opening: ${session.id}`);\n }\n await session.scope.dispose();\n session.phase = 'error';\n publishError(session, error);\n throw error;\n }\n }).catch((error: unknown) => {\n throw toPluginError('documents', error);\n });\n }\n\n async function closeDocument(documentId: string): Promise<void> {\n const session = sessions.get(documentId);\n if (!session) return; // idempotent — unknown or already closed\n await session.close();\n }\n\n function reorder(next: string[]) {\n setCore({ order: next });\n }\n\n const metaOf = (documentId?: string): DocumentMeta | null => {\n const core = store.getCore();\n const id = documentId ?? core.activeId;\n return id ? (core.documents[id] ?? null) : null;\n };\n\n /** The tab list: the same array until one of its documents changes, or the order does. */\n let listMemo: { core: CoreState; value: readonly DocumentInfo[] } | null = null;\n const listDocuments = (): readonly DocumentInfo[] => {\n const core = store.getCore();\n if (listMemo?.core === core) return listMemo.value;\n const next = core.order.map((id) => documentInfoOf(core.documents[id] ?? core.pending[id]));\n const previous = listMemo?.value;\n const same =\n previous !== undefined &&\n previous.length === next.length &&\n previous.every((info, i) => info === next[i]);\n const value = same ? previous : Object.freeze(next);\n listMemo = { core, value };\n return value;\n };\n\n /** A document's fields: `documentId`, or the active document's. */\n const getDocument = (documentId?: string): DocumentInfo | null => {\n const core = store.getCore();\n const id = documentId ?? core.activeId;\n const entry = id ? (core.documents[id] ?? core.pending[id]) : undefined;\n return entry ? documentInfoOf(entry) : null;\n };\n\n const documents: DocumentsCapability = {\n open: openDocument,\n // A file's bytes are the engine's to take or refuse: it says which.\n canOpenFiles: () => engine.canOpenFiles,\n retry: async (id, options) => {\n const session = sessions.get(id);\n if (!session || session.phase !== 'error' || session.source === null) {\n throw new PluginError(\n 'invalid-input',\n 'documents',\n `${id} didn't fail to open; nothing to retry`,\n );\n }\n // In its own tab, which keeps its id and place; the active document stays the active one.\n // The failed session holds nothing (its scope was released when it failed), so the new\n // one takes its id without closing it: the tab never closes.\n const { source, openOptions } = session;\n sessions.delete(id);\n return openDocument(source, { ...openOptions, activate: false, signal: options?.signal }, id);\n },\n rename: (id, name) => {\n const core = store.getCore();\n if (core.documents[id]) {\n setCore({ documents: { ...core.documents, [id]: { ...core.documents[id], name } } });\n } else if (core.pending[id]) {\n setCore({ pending: { ...core.pending, [id]: { ...core.pending[id], name } } });\n }\n const session = sessions.get(id);\n if (session) session.name = name;\n },\n openAll: (initialDocuments) => {\n guardUsable('documents.openAll()');\n // Fire-and-forget on purpose: each open() reserves its tab slot\n // synchronously, so tabs exist immediately in array order; exactly one\n // activation, decided here at request time. Failures are tab state\n // (`error`/`locked`), never unhandled rejections.\n const activeIndex = Math.max(\n 0,\n initialDocuments.findIndex((initialDocument) => initialDocument.active),\n );\n // The returned ids are the reserved slots: a thunk source's ticket is\n // rekeyed to the real id on resolve (`onOpened` carries the final id).\n return initialDocuments.map(({ source, active: _active, ...options }, index) => {\n try {\n const { session, done } = reserveAndOpen(source, {\n ...options,\n activate: index === activeIndex,\n });\n void done.catch(() => {});\n return session.id;\n } catch {\n // A refused reservation (an id already open) is not a boot failure:\n // the existing tab stands; report the id the caller asked for.\n return typeof source === 'function' ? '' : (idOfInput(source) ?? '');\n }\n });\n },\n unlock: unlockDocument,\n close: closeDocument,\n closeAll: async () => {\n for (const id of [...store.getCore().order]) await closeDocument(id);\n },\n setActive: (id) => {\n const core = store.getCore();\n // Pending tabs are selectable — a loading or locked tab is a real tab.\n if (core.documents[id] || core.pending[id]) setCore({ activeId: id });\n },\n getActiveId: () => store.getCore().activeId,\n getActive: () => getDocument(),\n list: listDocuments,\n get: getDocument,\n has: (id) => {\n const core = store.getCore();\n return core.documents[id] !== undefined || core.pending[id] !== undefined;\n },\n getCount: () => store.getCore().order.length,\n getOrder: () => store.getCore().order,\n setOrder: (ids) => {\n const current = store.getCore().order;\n const same =\n ids.length === current.length &&\n [...ids].sort().join('\\0') === [...current].sort().join('\\0');\n if (!same) {\n throw new PluginError(\n 'invalid-input',\n 'documents',\n 'setOrder() needs a permutation of the current order',\n );\n }\n reorder([...ids]);\n },\n move: (id, toIndex) => {\n const core = store.getCore();\n if (!core.documents[id] && !core.pending[id]) return;\n const without = core.order.filter((documentId) => documentId !== id);\n const clamped = Math.max(0, Math.min(toIndex, without.length));\n without.splice(clamped, 0, id);\n reorder(without);\n },\n swap: (id, otherId) => {\n const core = store.getCore();\n const index = core.order.indexOf(id);\n const otherIndex = core.order.indexOf(otherId);\n if (index < 0 || otherIndex < 0) return;\n const next = [...core.order];\n next[index] = otherId;\n next[otherIndex] = id;\n reorder(next);\n },\n // Reading the file: download() and downloadLayer() (downloadFile above).\n download: (id, options) =>\n downloadFile(id, options?.signal, 'download', (handle) =>\n handle.download(options?.mode !== undefined ? { mode: options.mode } : undefined),\n ),\n downloadLayer: (id, options) =>\n downloadFile(\n id,\n options?.signal,\n 'downloadLayer',\n (handle) => (isLocalDocument(handle) ? handle.downloadLayer() : Promise.reject(noLayer())),\n // Refused before anything runs: the cloud engine keeps layers itself.\n (handle) => (isLocalDocument(handle) ? null : noLayer()),\n ),\n // The verbs that need them live here, so their checks do too (permissions.md).\n canDownload: (id) => allowsOn(id, 'doc.download'),\n canPrint: (id) => allowsOn(id, 'doc.print'),\n // The page registry, addressed by PageRef (durable) or display index.\n listPages: (id) => metaOf(id)?.pages ?? EMPTY_PAGES,\n getPage: (page, id) => findPage(metaOf(id)?.pages ?? EMPTY_PAGES, page),\n getPageIndex: (ref, id) =>\n metaOf(id)?.pages.findIndex((pageInfo) => pageRefsEqual(pageInfo.ref, ref)) ?? -1,\n getRevision: (id) => metaOf(id)?.revision ?? -1,\n onOpened: opened.on,\n onOpenFailed: openFailed.on,\n onLocked: locked.on,\n onClosed: closed.on,\n onActiveChanged: activeChanged.on,\n onPagesChanged: pagesChanged.on,\n onUnsavedChangesChanged: unsavedChangesChanged.on,\n };\n workspaceCapabilities.set(DocumentsToken, documents);\n\n // ── workspace plugins: seed slices, then build their capabilities ────────────\n for (const plugin of plan.ordered) {\n if (!isDocumentScoped(plugin)) {\n workspaceLeases.set(plugin, store.lease(plugin.id, initialStateOf(plugin)));\n }\n }\n for (const plugin of plan.ordered) {\n if (isDocumentScoped(plugin)) continue;\n const ctx = createPluginContext(\n services,\n plugin,\n undefined,\n workspaceCancel.signal,\n workspaceScope,\n );\n const { api, connect } = plugin.create(ctx);\n if (plugin.token) workspaceCapabilities.set(plugin.token, api);\n workspaceConnectors.set(plugin, () => {\n connect?.();\n markConnected(ctx);\n });\n }\n\n return {\n ...viewerSettings.api,\n engine,\n documents,\n capability: resolveCapability,\n tryCapability: tryResolveCapability,\n scopeOf: plan.scopeOf,\n settingsOf,\n subscribe: store.subscribe,\n getState: store.getState,\n status: () => status,\n start: () => {\n if (status === 'destroying' || status === 'destroyed' || status === 'failed') {\n return Promise.reject(new Error(`[kernel] start() on a ${status} kernel`));\n }\n return (startPromise ??= (async () => {\n status = 'starting';\n try {\n for (const plugin of plan.ordered) {\n if (status !== 'starting') return; // destroy() raced us\n if (!isDocumentScoped(plugin)) workspaceConnectors.get(plugin)?.();\n }\n status = 'started';\n } catch (error) {\n // Startup failure unwinds everything construction + start registered;\n // the kernel is terminally failed (destroy() remains legal, and idle).\n status = 'failed';\n await workspaceScope.dispose();\n workspaceCapabilities.clear();\n throw error;\n }\n })());\n },\n destroy: () => {\n return (destroyPromise ??= (async () => {\n const wasFailed = status === 'failed';\n status = 'destroying'; // an in-flight start exits at its next check\n workspaceCancel.abort(new CancelledError('kernel destroyed'));\n await startPromise?.catch((error) => {\n if (!isCancelled(error) && !wasFailed) report(error);\n });\n // Close every session — the resource-owning map, not store.order: a\n // session mid-close is already unpublished but still needs joining.\n await Promise.allSettled([...sessions.values()].map((session) => session.close()));\n sessions.clear();\n await workspaceScope.dispose();\n workspaceCapabilities.clear();\n store.destroy();\n status = 'destroyed';\n })());\n },\n };\n}\n","/**\n * `composeApi`: the composition root's one primitive. A controller builds\n * its capability from area slices (each `satisfies Partial<Capability>`)\n * plus the service-owned members (authority twins, event hooks); this spreads\n * them into one object and refuses a key that two slices both define, so a\n * new verb can never silently shadow an old one.\n *\n * const api: FormHostCapability = composeApi('form', [\n * fields.api,\n * values.api,\n * { canFill: () => authority.can('doc.forms.fill'), onValueChanged: events.valueChanged.on },\n * ]);\n */\n\ntype UnionToIntersection<U> = (U extends unknown ? (value: U) => void : never) extends (\n value: infer I,\n) => void\n ? I\n : never;\n\nexport function composeApi<const T extends readonly object[]>(\n capability: string,\n slices: T,\n): UnionToIntersection<T[number]> {\n const seen = new Set<string>();\n const api: Record<string, unknown> = {};\n for (const slice of slices) {\n for (const key of Object.keys(slice)) {\n if (seen.has(key)) {\n throw new Error(`[${capability}] api member '${key}' is defined twice`);\n }\n seen.add(key);\n api[key] = (slice as Record<string, unknown>)[key];\n }\n }\n return api as UnionToIntersection<T[number]>;\n}\n","/**\n * Identity-stable reads. A capability read returns the same object until its\n * inputs change, so adapter selectors compare with `Object.is` and re-render\n * only when something really changed. These two helpers are how a plugin\n * builds such reads: name the inputs, and the result is recomputed only when\n * one of them is a different value.\n */\n\n/** The inputs as `compute` receives them; inferred from `inputs()` only, never from `compute`. */\ntype Computed<Inputs extends readonly unknown[]> = { -readonly [K in keyof Inputs]: NoInfer<Inputs[K]> };\n\nconst sameInputs = (left: readonly unknown[], right: readonly unknown[]): boolean =>\n left.length === right.length && left.every((value, index) => Object.is(value, right[index]));\n\n/**\n * Recompute `compute(...inputs())` only when an input changes (compared with\n * `Object.is`, element by element); otherwise return the previous result.\n */\nexport function memo<const Inputs extends readonly unknown[], Result>(\n inputs: () => Inputs,\n compute: (...inputs: Computed<Inputs>) => Result,\n): () => Result {\n let last: { inputs: Inputs; result: Result } | null = null;\n return () => {\n const next = inputs();\n if (last && sameInputs(last.inputs, next)) return last.result;\n const result = compute(...(next as unknown as Computed<Inputs>));\n last = { inputs: next, result };\n return result;\n };\n}\n\n/**\n * `memo` per key: each key keeps its own last inputs and result. Keys are\n * compared with `Map` semantics, so use primitives (a page object number, an\n * encoded key), not freshly built objects. `maxEntries` bounds the cache;\n * the oldest key is evicted first.\n */\nexport function memoByKey<Key, const Inputs extends readonly unknown[], Result>(\n inputs: (key: Key) => Inputs,\n compute: (key: Key, ...inputs: Computed<Inputs>) => Result,\n options: { maxEntries?: number } = {},\n): (key: Key) => Result {\n const cache = new Map<Key, { inputs: Inputs; result: Result }>();\n return (key) => {\n const next = inputs(key);\n const cached = cache.get(key);\n if (cached && sameInputs(cached.inputs, next)) return cached.result;\n const result = compute(key, ...(next as unknown as Computed<Inputs>));\n if (cached) cache.delete(key);\n else if (options.maxEntries !== undefined && cache.size >= options.maxEntries) {\n const oldest = cache.keys().next();\n if (!oldest.done) cache.delete(oldest.value);\n }\n cache.set(key, { inputs: next, result });\n return result;\n };\n}\n","/**\n * A plugin's State table as code: how to read it from the capability, and what\n * it is with no document. The declaration names no framework, so each adapter\n * turns the same declaration into its own reactive form (a React hook, a signal\n * per field, refs, a store) without listing the fields again.\n *\n * export const searchState = defineState(SearchToken, {\n * read: (search) => ({ query: search.getQuery(), hitCount: search.getHitCount() }),\n * empty: { query: null, hitCount: 0 },\n * });\n *\n * State a plugin loads only when asked for names the verb that loads it, `ensure`: an adapter\n * calls it once a document's capability is in scope, so reading the state is what loads it,\n * while the read itself stays pure.\n */\nimport type { CapabilityToken } from './types';\n\nexport interface StateDeclaration<Capability, State extends object> {\n /** The capability the state is read from; a document plugin's resolves per document. */\n readonly token: CapabilityToken<Capability>;\n /**\n * Read every field through the capability's getters. A getter returns the\n * same value while nothing changed, so adapters compare the result field by\n * field ({@link shallowEqual}) and wake readers only when a field changed.\n */\n readonly read: (capability: Capability) => State;\n /**\n * The state while the capability can't resolve: no document is open, it\n * isn't ready yet, or none is in scope. It has exactly `read`'s fields, so\n * an adapter can list the fields without a capability. Frozen, because every\n * reader shares it.\n */\n readonly empty: State;\n /**\n * Start loading what `read` shows, for state the plugin loads on demand. The adapters call it\n * whenever a hook or service starts reading a document's capability, so it must do nothing\n * once loaded or loading (an `ensureLoaded()` verb); a failure shows in the state (its\n * `status`), so they ignore the promise's rejection. Absent for state that is always loaded.\n */\n readonly ensure?: (capability: Capability) => Promise<void>;\n}\n\n/**\n * Declare a plugin's state. The fields come from `read`, and `empty` is checked\n * against them (`NoInfer` keeps it out of the inference): a missing field or a\n * value of the wrong type is a type error. Write `empty` inline, because\n * TypeScript refuses an extra field only in an object literal.\n */\nexport function defineState<Capability, State extends object>(\n token: CapabilityToken<Capability>,\n declaration: {\n readonly read: (capability: Capability) => State;\n readonly empty: NoInfer<State>;\n readonly ensure?: (capability: NoInfer<Capability>) => Promise<void>;\n },\n): StateDeclaration<Capability, State> {\n return {\n token,\n read: declaration.read,\n empty: Object.freeze(declaration.empty),\n ...(declaration.ensure ? { ensure: declaration.ensure } : {}),\n };\n}\n\n/**\n * One level of identity: equal when the two values are the same, or are\n * objects (or arrays) holding the same values under the same keys. Declared\n * state compares this way, and so does a value a selector picks from it.\n */\nexport function shallowEqual(left: unknown, right: unknown): boolean {\n if (Object.is(left, right)) return true;\n if (typeof left !== 'object' || typeof right !== 'object' || left === null || right === null) {\n return false;\n }\n if (Array.isArray(left) !== Array.isArray(right)) return false;\n const leftFields = left as Record<string, unknown>;\n const rightFields = right as Record<string, unknown>;\n const keys = Object.keys(leftFields);\n return (\n keys.length === Object.keys(rightFields).length &&\n keys.every((key) => key in rightFields && Object.is(leftFields[key], rightFields[key]))\n );\n}\n","/**\n * The documents' State tables as code: the document a component talks to (React's\n * `useDocument()`), and every open document with the active one (`useDocumentsState()`). Each\n * adapter turns these into its own reactive form, as it does a plugin's.\n */\nimport { defineState } from './state';\nimport { DocumentsToken, type DocumentInfo } from './types';\n\n/** No document: none is open, or none is in scope. It reads as one still on its way. */\nconst NO_DOCUMENT: DocumentInfo = Object.freeze({\n id: '',\n name: undefined,\n status: 'loading',\n pageCount: 0,\n hasUnsavedChanges: false,\n passwordProvided: undefined,\n error: undefined,\n});\n\n/**\n * The document in scope (the capability resolved for a document, or else the active one): its\n * id, name, status, page count, unsaved changes, and why it's locked or failed.\n */\nexport const documentState = defineState(DocumentsToken, {\n read: (documents): DocumentInfo => documents.get() ?? NO_DOCUMENT,\n empty: NO_DOCUMENT,\n});\n\n/** Every open document, in tab order, and the active one's id. */\nexport interface DocumentsState {\n /** Every open document, loading, locked and failed ones included. */\n readonly documents: readonly DocumentInfo[];\n /** The active document's id, or null with none open. */\n readonly activeId: string | null;\n}\n\nexport const documentsState = defineState(DocumentsToken, {\n read: (documents): DocumentsState => ({\n documents: documents.list(),\n activeId: documents.getActiveId(),\n }),\n empty: { documents: [], activeId: null },\n});\n","/**\n * What every framework adapter's plugin API shares (`useStage()`, `inject(EpdfSearch)`): which\n * members are reads, and what a document plugin's API reaches while its part of the app has no\n * ready document.\n *\n * Reads follow the capability vocabulary (docs/conventions/naming.md): a member named `get*`,\n * `list*`, `is*`, `has*` or `can*` reads state, so an adapter makes a call to it in a template or\n * a derived value update when its answer changes, and every other member tracks nothing.\n *\n * Without a document there is no capability to call, so the API reaches a stand-in: reading a\n * member never throws, so chrome renders before the first document opens, and calling one refuses\n * with `not-ready`. A member the token lists in `promises` returns the refusal as a rejected\n * promise, as the capability itself would, so a `.catch()` sees it; every other member throws.\n * The settings calls work: a plugin's settings belong to the plugin, not to a document.\n */\nimport { PluginError } from './errors';\nimport type { Kernel } from './kernel';\nimport type { CapabilityToken } from './types';\n\nconst READ = /^(?:get|list|is|has|can)(?:[A-Z]|$)/;\n\n/** Whether a capability member named `name` is a read: `get*`, `list*`, `is*`, `has*` or `can*`. */\nexport function isReadMember(name: string): boolean {\n return READ.test(name);\n}\n\n/** Whether the member at `path` (`'setValue'`, `'comments.reply'`) returns a promise, as `token` declares. */\nexport function returnsPromise(token: CapabilityToken<unknown>, path: string): boolean {\n return token.promises?.[path] === true;\n}\n\n/** The capability members that work without a document: a plugin's settings are the plugin's own. */\nconst SETTINGS_CALLS: ReadonlySet<string> = new Set([\n 'getSettings',\n 'updateSettings',\n 'resetSettings',\n 'onSettingsChanged',\n]);\n\nconst standIns = new WeakMap<Kernel, WeakMap<CapabilityToken<unknown>, object>>();\n\n/**\n * What a framework adapter's API reaches for a document plugin while its part of the app has no\n * ready document. One per kernel and token, so it is the same object on every read.\n */\nexport function standInFor<T>(kernel: Kernel, token: CapabilityToken<T>): T {\n let byToken = standIns.get(kernel);\n if (!byToken) {\n byToken = new WeakMap();\n standIns.set(kernel, byToken);\n }\n let standIn = byToken.get(token);\n if (!standIn) {\n standIn = createStandIn(token, () => kernel.settingsOf(token));\n byToken.set(token, standIn);\n }\n return standIn as T;\n}\n\n/**\n * A Proxy whose every member is a function that refuses with `not-ready`. A member is itself such\n * a Proxy, so namespaces work too (`annotation.comments.reply`). Members are cached by path, so\n * `stage.zoomIn` is the same function on every read. `then`, symbol keys and keys that start with\n * `__` (a framework's own flags, such as Vue's `__v_isRef`) read as undefined: the stand-in is no\n * thenable, and inspecting it doesn't throw. The four settings calls forward to `settings()` when\n * called; for a plugin without settings, they refuse too.\n */\nfunction createStandIn(token: CapabilityToken<unknown>, settings: () => object): object {\n const refusal = () => new PluginError('not-ready', token.name, 'no document is open');\n const refuse = (path: string) => (): unknown => {\n if (returnsPromise(token, path)) return Promise.reject(refusal());\n throw refusal();\n };\n const forward =\n (name: string) =>\n (...args: unknown[]): unknown => {\n // A plugin whose definition declares no settings (the Stage keeps its own, per view) has\n // none to reach without a document either.\n let api: Record<string, (...args: unknown[]) => unknown>;\n try {\n api = settings() as typeof api;\n } catch {\n throw refusal();\n }\n return api[name]!(...args);\n };\n const members = new Map<string, unknown>();\n const memberOf = (parentPath: string, key: string | symbol): unknown => {\n if (typeof key === 'symbol' || key === 'then' || key.startsWith('__')) return undefined;\n const path = parentPath ? `${parentPath}.${key}` : key;\n let member = members.get(path);\n if (!member) {\n member =\n !parentPath && SETTINGS_CALLS.has(key)\n ? forward(key)\n : new Proxy(refuse(path), { get: (_refuse, next) => memberOf(path, next) });\n members.set(path, member);\n }\n return member;\n };\n return new Proxy({}, { get: (_capability, key) => memberOf('', key) });\n}\n","export * from './types';\nexport * from './kernel';\nexport * from './event-hook';\nexport * from './serial-queue';\nexport * from './errors';\nexport { timerClock } from './clock';\nexport type { Cancel, HostClock, PluginClock } from './clock';\nexport { composeApi } from './compose';\nexport type { LatestCancellation, LatestLane, LatestRun } from './lanes';\nexport { createLatestLane } from './lanes';\nexport { guardHandle } from './guarded-handle';\nexport { memo, memoByKey } from './memo';\nexport { reload } from './mirror';\nexport type { Mirror, MirrorChange, MirrorReload, MirrorSpec } from './mirror';\nexport type { PageMirror, PageMirrorChange, PageMirrorSpec } from './page-mirror';\nexport type {\n ChangeLabel,\n ChangeQueue,\n HeldChange,\n HoldOptions,\n PendingChange,\n PredictedOp,\n SettledChange,\n StagedChange,\n} from './changes';\nexport type {\n DeepPartial,\n NoSettings,\n Settings,\n SettingsApi,\n SettingsChangedEvent,\n SettingsDeclaration,\n SettingsOf,\n} from './settings';\nexport { mergeSettings } from './settings';\nexport { defineState, shallowEqual } from './state';\nexport type { StateDeclaration } from './state';\nexport { documentState, documentsState } from './documents-state';\nexport type { DocumentsState } from './documents-state';\nexport { VIEWER_DEFAULTS, VIEWER_WHOLE_SETTINGS, viewerSettingsOf } from './viewer-settings';\nexport type { ViewerPageSettings, ViewerSettings, ViewerSettingsInput } from './viewer-settings';\nexport { isReadMember, returnsPromise, standInFor } from './capability-handle';\nexport type { SliceChange } from './store';\nexport type { SliceLease } from './store';\nexport { CancelledError, isCancelled } from './scope';\n\n// Re-export the engine contracts so plugins/adapters import them from @embedpdf/core.\nexport {\n AbortablePromise,\n CONTINUOUS_RENDER_POLICY,\n // The refusal shape of the permissions convention (permissions.md): a\n // plugin's optimistic gate rejects with the same error the engine throws.\n PermissionDenied,\n // What a page picture draws, as the engine decides it (left-out options\n // draw what the user may read).\n resolvePageLayers,\n snapAppearanceScale,\n snapFullPageViewport,\n snapTileScale,\n toPageRef,\n pageRefsEqual,\n encodePageKey,\n decodePageKey,\n annotationKey,\n // Local-only members (warmup, fonts) are reached through this check.\n isLocalEngine,\n} from '@embedpdf/engine-core/runtime';\nexport type {\n AnnotationAppearanceImage,\n AnnotationRef,\n EngineRenderPolicy,\n PageLayerRights,\n PageRenderLayers,\n PageRef,\n PageHandle,\n PageRaster,\n PageRenderOptions,\n PageRenderTarget,\n PageRenderViewport,\n PageImageHandle,\n PageImageOptions,\n PageImageObjectUrl,\n PdfRect,\n CallFacts,\n CallPriority,\n WorkingSetPage,\n} from '@embedpdf/engine-core/runtime';\n\nimport type { NoSettings } from './settings';\nimport type { CapabilityToken, PluginDef, PromiseMembers } from './types';\n\n/**\n * Create a typed capability token. `name` is the token's identity (debugging,\n * error messages); `options.hint` is the authored remedy the kernel appends to\n * the missing-dependency error, so the error contains its own fix.\n * `options.promises` lists the members that return a promise, so a framework\n * adapter's stand-in rejects them, rather than throwing, while no document is\n * open; a document plugin whose capability has such members gives it.\n */\nexport function createCapabilityToken<T>(\n name: string,\n options?: { hint?: string; promises?: PromiseMembers<T> },\n): CapabilityToken<T> {\n return {\n name,\n hint: options?.hint,\n ...(options?.promises ? { promises: options.promises as Readonly<Record<string, true>> } : {}),\n };\n}\n\n/**\n * The host lens over a plugin's token: the same runtime object, typed with\n * the wider capability sibling plugins use. The one place that cast lives.\n */\nexport function createHostToken<Host>(token: CapabilityToken<unknown>): CapabilityToken<Host> {\n return token as CapabilityToken<Host>;\n}\n\n/**\n * Define a plugin. It returns the definition as it is, and reads the types from it, so a\n * plugin passes no type arguments: the capability from `token`, the state from `state`, the\n * settings from `settings.defaults`. `create` receives a `PluginContext<State, Settings>`\n * and must return the capability. A plugin without `state` is stateless (`void`); one\n * without `settings` has `NoSettings`, and `ctx.settings()` throws there.\n */\nexport function definePlugin<\n State = void,\n Capability = unknown,\n Settings extends object = NoSettings,\n>(definition: PluginDef<State, Capability, Settings>): PluginDef<State, Capability, Settings> {\n return definition;\n}\n"],"mappings":"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"}