@embedpdf/core 3.0.0-next.5 → 3.0.0-next.7

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.
package/dist/index.cjs CHANGED
@@ -191,7 +191,7 @@ function planPlugins(plugins) {
191
191
  providerByToken.set(plugin.token, plugin);
192
192
  scopeByToken.set(plugin.token, plugin.scope ?? "workspace");
193
193
  }
194
- for (const plugin of plugins) for (const required of plugin.requires ?? []) if (required !== DocumentsToken && !providerByToken.has(required)) throw new Error(`Plugin "${plugin.id}" requires capability "${required.name}", which no plugin provides.`);
194
+ for (const plugin of plugins) for (const required of plugin.requires ?? []) if (required !== DocumentsToken && !providerByToken.has(required)) throw new Error(`Plugin "${plugin.id}" requires capability "${required.name}", which no plugin provides` + (required.hint ? ` — ${required.hint}.` : "."));
195
195
  const ordered = [];
196
196
  const status = /* @__PURE__ */ new Map();
197
197
  const visit = (plugin) => {
@@ -805,9 +805,16 @@ function createKernel(opts) {
805
805
  }
806
806
  //#endregion
807
807
  //#region src/index.ts
808
- /** Create a typed capability token. `name` is for debugging only. */
809
- function createCapabilityToken(name) {
810
- return { name };
808
+ /**
809
+ * Create a typed capability token. `name` is the token's identity (debugging,
810
+ * error messages); `options.hint` is the authored remedy the kernel appends to
811
+ * the missing-dependency error, so the error contains its own fix.
812
+ */
813
+ function createCapabilityToken(name, options) {
814
+ return {
815
+ name,
816
+ hint: options?.hint
817
+ };
811
818
  }
812
819
  /** Identity helper that pins a plugin's generics. The real win is inference. */
813
820
  function definePlugin(def) {
@@ -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/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 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 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 /** 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 /**\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\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 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 throw new Error(\n `Plugin \"${plugin.id}\" requires capability \"${required.name}\", which no plugin provides.`,\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 };\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","export * from './types';\nexport * from './kernel';\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 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/** Create a typed capability token. `name` is for debugging only. */\nexport function createCapabilityToken<T>(name: string): CapabilityToken<T> {\n return { name };\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":["CONTINUOUS_RENDER_POLICY"],"sources":["../src/types.ts","../src/store.ts","../src/context.ts","../src/order.ts","../src/scope.ts","../src/kernel.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 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 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 /**\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\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 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 };\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","export * from './types';\nexport * from './kernel';\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 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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"}
package/dist/index.d.cts CHANGED
@@ -21,6 +21,13 @@ declare const CORE_DOCUMENT_OPEN_FAILED = "@@core/document-open-failed";
21
21
  /** A typed handle to a capability — typed resolution, no string casts. */
22
22
  interface CapabilityToken<T> {
23
23
  readonly name: string;
24
+ /**
25
+ * Authored remedy shown when a plugin `requires` this capability and no
26
+ * plugin provides it — the missing-dependency error should contain its own
27
+ * fix (e.g. "add interactionPlugin() from '@embedpdf/plugin-interaction' to
28
+ * your plugins list").
29
+ */
30
+ readonly hint?: string;
24
31
  /** phantom — never present at runtime */
25
32
  readonly __type?: T;
26
33
  }
@@ -304,8 +311,14 @@ declare class CancelledError extends Error {
304
311
  declare const isCancelled: (error: unknown) => error is CancelledError;
305
312
  //#endregion
306
313
  //#region src/index.d.ts
307
- /** Create a typed capability token. `name` is for debugging only. */
308
- declare function createCapabilityToken<T>(name: string): CapabilityToken<T>;
314
+ /**
315
+ * Create a typed capability token. `name` is the token's identity (debugging,
316
+ * error messages); `options.hint` is the authored remedy the kernel appends to
317
+ * the missing-dependency error, so the error contains its own fix.
318
+ */
319
+ declare function createCapabilityToken<T>(name: string, options?: {
320
+ hint?: string;
321
+ }): CapabilityToken<T>;
309
322
  /** Identity helper that pins a plugin's generics. The real win is inference. */
310
323
  declare function definePlugin<S = unknown, A extends Action = Action, C = unknown>(def: PluginDef<S, A, C>): PluginDef<S, A, C>;
311
324
  //#endregion
package/dist/index.d.ts CHANGED
@@ -21,6 +21,13 @@ declare const CORE_DOCUMENT_OPEN_FAILED = "@@core/document-open-failed";
21
21
  /** A typed handle to a capability — typed resolution, no string casts. */
22
22
  interface CapabilityToken<T> {
23
23
  readonly name: string;
24
+ /**
25
+ * Authored remedy shown when a plugin `requires` this capability and no
26
+ * plugin provides it — the missing-dependency error should contain its own
27
+ * fix (e.g. "add interactionPlugin() from '@embedpdf/plugin-interaction' to
28
+ * your plugins list").
29
+ */
30
+ readonly hint?: string;
24
31
  /** phantom — never present at runtime */
25
32
  readonly __type?: T;
26
33
  }
@@ -304,8 +311,14 @@ declare class CancelledError extends Error {
304
311
  declare const isCancelled: (error: unknown) => error is CancelledError;
305
312
  //#endregion
306
313
  //#region src/index.d.ts
307
- /** Create a typed capability token. `name` is for debugging only. */
308
- declare function createCapabilityToken<T>(name: string): CapabilityToken<T>;
314
+ /**
315
+ * Create a typed capability token. `name` is the token's identity (debugging,
316
+ * error messages); `options.hint` is the authored remedy the kernel appends to
317
+ * the missing-dependency error, so the error contains its own fix.
318
+ */
319
+ declare function createCapabilityToken<T>(name: string, options?: {
320
+ hint?: string;
321
+ }): CapabilityToken<T>;
309
322
  /** Identity helper that pins a plugin's generics. The real win is inference. */
310
323
  declare function definePlugin<S = unknown, A extends Action = Action, C = unknown>(def: PluginDef<S, A, C>): PluginDef<S, A, C>;
311
324
  //#endregion
package/dist/index.js CHANGED
@@ -190,7 +190,7 @@ function planPlugins(plugins) {
190
190
  providerByToken.set(plugin.token, plugin);
191
191
  scopeByToken.set(plugin.token, plugin.scope ?? "workspace");
192
192
  }
193
- for (const plugin of plugins) for (const required of plugin.requires ?? []) if (required !== DocumentsToken && !providerByToken.has(required)) throw new Error(`Plugin "${plugin.id}" requires capability "${required.name}", which no plugin provides.`);
193
+ for (const plugin of plugins) for (const required of plugin.requires ?? []) if (required !== DocumentsToken && !providerByToken.has(required)) throw new Error(`Plugin "${plugin.id}" requires capability "${required.name}", which no plugin provides` + (required.hint ? ` — ${required.hint}.` : "."));
194
194
  const ordered = [];
195
195
  const status = /* @__PURE__ */ new Map();
196
196
  const visit = (plugin) => {
@@ -804,9 +804,16 @@ function createKernel(opts) {
804
804
  }
805
805
  //#endregion
806
806
  //#region src/index.ts
807
- /** Create a typed capability token. `name` is for debugging only. */
808
- function createCapabilityToken(name) {
809
- return { name };
807
+ /**
808
+ * Create a typed capability token. `name` is the token's identity (debugging,
809
+ * error messages); `options.hint` is the authored remedy the kernel appends to
810
+ * the missing-dependency error, so the error contains its own fix.
811
+ */
812
+ function createCapabilityToken(name, options) {
813
+ return {
814
+ name,
815
+ hint: options?.hint
816
+ };
810
817
  }
811
818
  /** Identity helper that pins a plugin's generics. The real win is inference. */
812
819
  function definePlugin(def) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["CONTINUOUS_RENDER_POLICY"],"sources":["../src/types.ts","../src/store.ts","../src/context.ts","../src/order.ts","../src/scope.ts","../src/kernel.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 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 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 /** 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 /**\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\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 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 throw new Error(\n `Plugin \"${plugin.id}\" requires capability \"${required.name}\", which no plugin provides.`,\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 };\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","export * from './types';\nexport * from './kernel';\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 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/** Create a typed capability token. `name` is for debugging only. */\nexport function createCapabilityToken<T>(name: string): CapabilityToken<T> {\n return { name };\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.js","names":["CONTINUOUS_RENDER_POLICY"],"sources":["../src/types.ts","../src/store.ts","../src/context.ts","../src/order.ts","../src/scope.ts","../src/kernel.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 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 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 /**\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\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 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 };\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","export * from './types';\nexport * from './kernel';\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 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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@embedpdf/core",
3
- "version": "3.0.0-next.5",
3
+ "version": "3.0.0-next.7",
4
4
  "private": false,
5
5
  "description": "Pure, framework-free runtime for the EmbedPDF v3 viewer: store, typed capabilities, and plugin lifecycle. DOM-free and serializable.",
6
6
  "type": "module",
@@ -18,7 +18,7 @@
18
18
  "dist"
19
19
  ],
20
20
  "dependencies": {
21
- "@embedpdf/engine-core": "3.0.0-next.5"
21
+ "@embedpdf/engine-core": "3.0.0-next.7"
22
22
  },
23
23
  "devDependencies": {
24
24
  "typescript": "^5.0.0",