@hoardodile/sdk-web 0.1.2 → 0.1.4

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.d.ts CHANGED
@@ -356,6 +356,11 @@ declare const invalidatePushKeys: {
356
356
  readonly messages: "messages:invalidate";
357
357
  readonly danmaku: "danmaku:invalidate";
358
358
  };
359
+ /** Per-method bridge timeouts (ms), mirroring the entries above. */
360
+ declare const pluginRequestTimeouts: {
361
+ readonly readFile: 120000;
362
+ readonly download: 300000;
363
+ };
359
364
  /**
360
365
  * Type-safe host bridge. The runtime still serialises messages as plain
361
366
  * postMessage objects; this contract gives compile-time guarantees to callers.
@@ -734,4 +739,4 @@ declare function subscribeToPrefChanges(key: string, cb: () => void): () => void
734
739
  /** Notify all subscribers of `key` about a value change. */
735
740
  declare function broadcastPrefChange(key: string): void;
736
741
 
737
- export { type Codec, type DeepPartial, type FileUrlVariant, type Host, type HostMessage, type HostPush, type HostPushes, type HostResponse, type InvalidateTarget, type MutationState, PROTOCOL_VERSION, type PluginContextPainted, type PluginFonts, type PluginIframeContext, type PluginMessage, type PluginRequest, type PluginRequests, type PluginResolvedTheme, type PluginResource, type PluginSubscribe, type PluginThemePalette, type QueryState, type ReactivePluginAPI, type RequestInput, type RequestOutput, type StubbedPluginAPI, type Theme, type WebPluginAPI, applyFonts, applyTheme, booleanCodec, broadcastPrefChange, createIframeHostAPI, createWebPluginAPI, ensureHostBridge, extractFontsPayload, extractPrefPayload, extractThemePayload, getPluginCacheStore, getPluginContext, getPluginPrefStore, getVisibilitySnapshot, hostPushKeys, invalidatePushKeys, isRecord, jsonCodec, mountPlugin, numberCodec, pluginMethods, pluginThemePalettes, seedPluginStores, setPluginCache, setPluginPref, snapshotCacheEntries, subscribeToPrefChanges, subscribeToVisibility };
742
+ export { type Codec, type DeepPartial, type FileUrlVariant, type Host, type HostMessage, type HostPush, type HostPushes, type HostResponse, type InvalidateTarget, type MutationState, PROTOCOL_VERSION, type PluginContextPainted, type PluginFonts, type PluginIframeContext, type PluginMessage, type PluginRequest, type PluginRequests, type PluginResolvedTheme, type PluginResource, type PluginSubscribe, type PluginThemePalette, type QueryState, type ReactivePluginAPI, type RequestInput, type RequestOutput, type StubbedPluginAPI, type Theme, type WebPluginAPI, applyFonts, applyTheme, booleanCodec, broadcastPrefChange, createIframeHostAPI, createWebPluginAPI, ensureHostBridge, extractFontsPayload, extractPrefPayload, extractThemePayload, getPluginCacheStore, getPluginContext, getPluginPrefStore, getVisibilitySnapshot, hostPushKeys, invalidatePushKeys, isRecord, jsonCodec, mountPlugin, numberCodec, pluginMethods, pluginRequestTimeouts, pluginThemePalettes, seedPluginStores, setPluginCache, setPluginPref, snapshotCacheEntries, subscribeToPrefChanges, subscribeToVisibility };
package/dist/index.js CHANGED
@@ -406,6 +406,7 @@ function applyTheme(resolvedTheme, palette, iconStyle) {
406
406
  root.classList.add(`theme-${palette}`);
407
407
  }
408
408
  root.dataset.iconStyle = iconStyle;
409
+ root.style.colorScheme = resolvedTheme;
409
410
  }
410
411
  var FONT_STYLE_ID = "plugin-host-font";
411
412
  function applyFonts(family, cssPaths) {
@@ -683,6 +684,6 @@ function createIframeHostAPI(ctx) {
683
684
  };
684
685
  }
685
686
 
686
- export { PROTOCOL_VERSION, applyFonts, applyTheme, booleanCodec, broadcastPrefChange, createIframeHostAPI, createWebPluginAPI, ensureHostBridge, extractFontsPayload, extractPrefPayload, extractThemePayload, getPluginCacheStore, getPluginContext, getPluginPrefStore, getVisibilitySnapshot, hostPushKeys, invalidatePushKeys, isRecord, jsonCodec, mountPlugin, numberCodec, pluginMethods, pluginThemePalettes, seedPluginStores, setPluginCache, setPluginPref, snapshotCacheEntries, subscribeToPrefChanges, subscribeToVisibility };
687
+ export { PROTOCOL_VERSION, applyFonts, applyTheme, booleanCodec, broadcastPrefChange, createIframeHostAPI, createWebPluginAPI, ensureHostBridge, extractFontsPayload, extractPrefPayload, extractThemePayload, getPluginCacheStore, getPluginContext, getPluginPrefStore, getVisibilitySnapshot, hostPushKeys, invalidatePushKeys, isRecord, jsonCodec, mountPlugin, numberCodec, pluginMethods, pluginRequestTimeouts, pluginThemePalettes, seedPluginStores, setPluginCache, setPluginPref, snapshotCacheEntries, subscribeToPrefChanges, subscribeToVisibility };
687
688
  //# sourceMappingURL=index.js.map
688
689
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/protocol.ts","../src/bridge.ts","../src/codecs.ts","../src/fixtures.ts","../src/lifecycle.ts","../src/stores.ts","../src/urls.ts","../src/runtime.ts"],"names":["isRecord","imageVariantQuery"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n\tFileStats,\n\tPluginAssetDeleteResult,\n\tPluginDownloadRequest,\n\tPluginDownloadResult,\n\tReadFileRange,\n\tSearchMeta,\n\tSerializedFileList,\n} from \"@hoardodile/sdk-types\"\n\n/**\n * Wire protocol version shared between the plugin SDK and the browser\n * host. Bumped only on incompatible protocol changes. Plugins stamp every\n * outbound message with it; the host warns loudly when a plugin was built\n * against a different version.\n */\nexport const PROTOCOL_VERSION = 1 as const\n\n/** Shared read-range contract; defined once in `@hoardodile/sdk-types`. */\nexport type { ReadFileRange } from \"@hoardodile/sdk-types\"\n\nexport type PluginResolvedTheme = \"light\" | \"dark\"\n\n/**\n * Canonical list of theme palette ids — the single source of truth shared\n * by the host app and every plugin. `mono` is the default and has no CSS\n * class (it lives in `:root` / `.dark`); every other id maps to a\n * `.theme-<id>` block in `@hoardodile/ui/theme.css` and a\n * `theme.palette.<id>` i18n label.\n */\nexport const pluginThemePalettes = [\n\t\"mono\",\n\t\"sage\",\n\t\"parchment\",\n\t\"azure\",\n\t\"hoardodile\",\n] as const\n\nexport type PluginThemePalette = (typeof pluginThemePalettes)[number]\n\n/** Icon rendering style as chosen in host Settings → Icons. */\nexport type PluginIconStyle = \"duotone\" | \"grayscale\" | \"linear\"\n\n/** Host app font as observed by the plugin. */\nexport type PluginFonts = {\n\t/** CSS `font-family` stack; empty when the plugin opted out of inheritance. */\n\treadonly family: string\n\t/** Absolute paths (`/fonts/...`) of the preset stylesheets backing the stack. */\n\treadonly cssPaths: readonly string[]\n}\n\n/**\n * Context injected into the iframe as `window.__context__` and pushed via\n * the `context` host push. Not a one-shot: the host may push a replacement\n * context at any time (a pooled iframe is rebound across resources without\n * a reload), and every push re-invokes the `mountPlugin` mount callback.\n */\nexport type PluginIframeContext = {\n\treadonly pluginId: string\n\treadonly resId: string\n\treadonly resName: string\n\treadonly sourceMeta: unknown\n\treadonly searchMeta: SearchMeta | undefined\n\treadonly fileStats: FileStats | undefined\n\treadonly contentPluginId: string\n\t/** Current UI language code. The iframe uses this to select its own locale bundle. */\n\treadonly language: string\n\t/** Current resolved theme (light or dark). */\n\treadonly resolvedTheme: PluginResolvedTheme\n\t/** Current theme palette. */\n\treadonly palette: PluginThemePalette\n\t/** Current icon rendering style (Settings → Icons). */\n\treadonly iconStyle: PluginIconStyle\n\t/**\n\t * Host app font to apply inside the iframe: a CSS `font-family` stack\n\t * plus the preset stylesheets that back it. An empty family means the\n\t * plugin opted out (`ui.inheritFont: false`) and keeps its own fonts.\n\t */\n\treadonly fonts: PluginFonts\n\t/** Initial plugin-scoped prefs (unprefixed keys) loaded from server. */\n\treadonly initialPrefs: Record<string, string>\n\t/** Initial plugin+resId cache entries (unprefixed keys) loaded from server. */\n\treadonly initialCache: Record<string, string>\n\t/**\n\t * Short-lived token that lets the sandboxed iframe fetch resource files\n\t * without a session cookie (null-origin iframe cannot send SameSite cookies).\n\t */\n\treadonly fileToken: string\n\t/**\n\t * Short-lived token for the plugin's own asset vault URLs\n\t * (`/api/plugin-assets/<pluginId>/<token>/<path>`), issued only when\n\t * the plugin's manifest declares the `download` permission. Empty\n\t * string otherwise — `resolveAssetUrl` throws on the empty token\n\t * (never builds a malformed `/…//path` URL); check the permission\n\t * before relying on vault URLs.\n\t */\n\treadonly assetToken: string\n}\n\n/** Wire request from plugin (iframe) to host. */\nexport type PluginRequest = {\n\treadonly type: \"request\"\n\treadonly id: number\n\treadonly method: string\n\treadonly params?: unknown\n\t/** Wire protocol version the plugin was built against (see {@link PROTOCOL_VERSION}). */\n\treadonly proto?: number\n\t/**\n\t * SDK-internal scope stamp: the resource the request was issued for,\n\t * captured by the runtime when the plugin called the API. The host\n\t * drops the request as stale when the stamp no longer matches the\n\t * iframe's binding (e.g. an unmount flush racing a rebind), so late\n\t * requests never leak into the wrong resource. Plugin code never\n\t * sets this.\n\t */\n\treadonly resId?: string\n}\n\n/** Wire response from host to plugin for a prior request. */\nexport type HostResponse = {\n\treadonly type: \"response\"\n\treadonly id: number\n\treadonly ok: boolean\n\treadonly data?: unknown\n\treadonly error?: string\n\t/**\n\t * Optional machine-readable plugin error code (e.g. the asset\n\t * `DENIED` / `UNAVAILABLE` / `POLICY` vocabulary) so the bridge can\n\t * reject with an Error carrying the code — plugin code branches on\n\t * `err.name` across the postMessage boundary.\n\t */\n\treadonly errorCode?: string\n\t/**\n\t * Legacy alias of {@link errorCode} (same value) kept for host\n\t * builds predating the unified field. The bridge reads\n\t * `errorCode ?? errorName`.\n\t */\n\treadonly errorName?: string\n}\n\n/** Wire push event from host to plugin. */\nexport type HostPush = {\n\treadonly type: \"push\"\n\treadonly key: string\n\treadonly data?: unknown\n}\n\n/** Wire subscription request from plugin to host. */\nexport type PluginSubscribe = {\n\treadonly type: \"subscribe\"\n\treadonly key: string\n\t/** Wire protocol version the plugin was built against (see {@link PROTOCOL_VERSION}). */\n\treadonly proto?: number\n}\n\n/**\n * Wire acknowledgement from plugin to host: the pushed context has been\n * applied *and painted* — the mount callback returned (for React plugins\n * the new tree is already committed via flushSync) and a frame with the\n * new content has reached the compositor. The host keeps a freshly\n * claimed pooled iframe transparent until this arrives, so the previous\n * resource's content never shows under a new claim.\n */\nexport type PluginContextPainted = {\n\treadonly type: \"contextPainted\"\n\treadonly resId: string\n\t/** Wire protocol version the plugin was built against (see {@link PROTOCOL_VERSION}). */\n\treadonly proto?: number\n}\n\n/** Union of all messages a plugin can send to the host. */\nexport type PluginMessage =\n\t| PluginRequest\n\t| PluginSubscribe\n\t| PluginContextPainted\n\n/** Union of all messages the host can send to a plugin. */\nexport type HostMessage = HostResponse | HostPush\n\n/** Type-safe request protocol table. Each entry declares input and output. */\nexport type PluginRequests = {\n\tlogInfo: {\n\t\treadonly input: {\n\t\t\treadonly message: string\n\t\t\treadonly data?: Record<string, unknown>\n\t\t}\n\t\treadonly output: undefined\n\t}\n\tlogWarn: {\n\t\treadonly input: {\n\t\t\treadonly message: string\n\t\t\treadonly data?: Record<string, unknown>\n\t\t}\n\t\treadonly output: undefined\n\t}\n\tlogError: {\n\t\treadonly input: {\n\t\t\treadonly message: string\n\t\t\treadonly data?: Record<string, unknown>\n\t\t}\n\t\treadonly output: undefined\n\t}\n\tlistFiles: {\n\t\treadonly input: undefined\n\t\treadonly output: SerializedFileList\n\t}\n\treadFile: {\n\t\treadonly input: {\n\t\t\treadonly path: string\n\t\t\t/** Byte range (see {@link ReadFileRange}); omitted = whole file. */\n\t\t\treadonly range?: ReadFileRange\n\t\t}\n\t\treadonly output: ArrayBuffer\n\t\t// Timeout: see {@link pluginRequestTimeouts.readFile} (large files\n\t\t// stream through the host process before the bytes arrive).\n\t}\n\tlistMessages: {\n\t\treadonly input: undefined\n\t\treadonly output: readonly import(\"@hoardodile/sdk-types\").Message[]\n\t}\n\tcreateMessage: {\n\t\treadonly input: {\n\t\t\treadonly body: string\n\t\t\t/** Wire anchor envelope; plugins pass raw data, the SDK wraps it. */\n\t\t\treadonly anchor?: import(\"@hoardodile/sdk-types\").AnchorData\n\t\t}\n\t\treadonly output: import(\"@hoardodile/sdk-types\").Message\n\t}\n\tlistDanmaku: {\n\t\treadonly input: {\n\t\t\treadonly filter?: import(\"@hoardodile/sdk-types\").DanmakuListFilter\n\t\t}\n\t\treadonly output: readonly import(\"@hoardodile/sdk-types\").Danmaku[]\n\t}\n\tcreateDanmaku: {\n\t\treadonly input: {\n\t\t\treadonly text: string\n\t\t\t/** Wire anchor envelope (see {@link PluginRequests.createMessage}). */\n\t\t\treadonly anchor: import(\"@hoardodile/sdk-types\").AnchorData\n\t\t\treadonly mode?: import(\"@hoardodile/sdk-types\").DanmakuMode\n\t\t}\n\t\treadonly output: import(\"@hoardodile/sdk-types\").Danmaku\n\t}\n\tsetPref: {\n\t\t/** Persist a plugin-wide preference; host broadcasts `prefsChanged`. */\n\t\treadonly input: { readonly key: string; readonly value: string }\n\t\treadonly output: undefined\n\t}\n\tsetCache: {\n\t\t/**\n\t\t * Persist a per-resource cache entry; host broadcasts\n\t\t * `cacheChanged`.\n\t\t */\n\t\treadonly input: {\n\t\t\treadonly key: string\n\t\t\treadonly value: string\n\t\t}\n\t\treadonly output: undefined\n\t}\n\tinvalidate: {\n\t\t/** Request the host to invalidate cached data for a target. */\n\t\treadonly input: { readonly target: InvalidateTarget }\n\t\treadonly output: undefined\n\t}\n\t/**\n\t * User-consented download into the plugin's own asset vault (see\n\t * `@hoardodile/sdk-types/plugin-asset`). The host asks the user with\n\t * the shared consent dialog — an array of requests is ONE dialog\n\t * listing every item (all-or-nothing); cached destinations resolve\n\t * without any dialog. Rejections carry a machine-readable `err.name`\n\t * (`DENIED` / `UNAVAILABLE` / `POLICY`).\n\t *\n\t * The wire mirrors the call: single in → single result out, array in\n\t * → array of results out, in request order.\n\t *\n\t * Timeout: {@link pluginRequestTimeouts.download} — the client-side\n\t * ceiling for the whole flow (consent dialog + transfer), declared in\n\t * the protocol meta rather than ad hoc at the call site.\n\t */\n\tdownload: {\n\t\treadonly input: PluginDownloadRequest | readonly PluginDownloadRequest[]\n\t\treadonly output: PluginDownloadResult | readonly PluginDownloadResult[]\n\t}\n\t/**\n\t * Remove a vault file (idempotent); the plugin decides its own vault\n\t * lifecycle — no user consent, nothing leaves the host.\n\t */\n\tdeleteAsset: {\n\t\treadonly input: { readonly path: string }\n\t\treadonly output: PluginAssetDeleteResult\n\t}\n}\n\n/** Type-safe push protocol table. */\nexport type HostPushes = {\n\tcontext: PluginIframeContext\n\tvisibility: { readonly visible: boolean }\n\tthemeChanged: {\n\t\treadonly resolvedTheme: string\n\t\treadonly palette: string\n\t\t/** Active icon rendering style — host applies it as `data-icon-style`. */\n\t\treadonly iconStyle: PluginIconStyle\n\t}\n\tfontsChanged: PluginFonts\n\t/**\n\t * The wire payload is a bare language-code string: it predates the\n\t * typed protocol table and must stay stable for already-installed\n\t * plugin builds (see `pushLanguageChanged` in apps/web — do not wrap\n\t * it in an object).\n\t */\n\tlanguageChanged: string\n\tprefsChanged: { readonly key: string; readonly value?: string }\n\t/**\n\t * A plugin+resource cache entry changed. With data, carries the single\n\t * changed entry; without data (undefined), all entries were cleared and\n\t * the plugin should drop its whole cache store.\n\t */\n\tcacheChanged:\n\t\t| { readonly resId: string; readonly key: string; readonly value?: string }\n\t\t| undefined\n\t/**\n\t * Host-initiated request to jump to an anchor (e.g. the user clicked a\n\t * comment anchor in the host UI). Carries the plugin-defined anchor data\n\t * only — the resource is always the iframe's own.\n\t */\n\tanchorJump: import(\"@hoardodile/sdk-types\").AnchorData\n\t\"res:invalidate\": undefined\n\t\"resources:invalidate\": undefined\n\t\"messages:invalidate\": undefined\n\t\"danmaku:invalidate\": undefined\n}\n\n/** Extract the input type for a request key. */\nexport type RequestInput<K extends keyof PluginRequests> =\n\tPluginRequests[K][\"input\"]\n\n/** Extract the output type for a request key. */\nexport type RequestOutput<K extends keyof PluginRequests> =\n\tPluginRequests[K][\"output\"]\n\n/** Targets that can be invalidated from the plugin runtime. */\nexport type InvalidateTarget = \"resource\" | \"resources\" | \"messages\" | \"danmaku\"\n\n// ── Runtime wire constants ─────────────────────────────────────────────────\n//\n// The type tables above are the protocol's single source of truth; these\n// constants are their runtime mirror. The two are interlocked at compile\n// time in both directions: a constant value that is not a table key fails\n// `satisfies`, and a table key missing from the constant fails the\n// `AssertTrue` checks below. Add a key to both or neither.\n\n/** Wire keys for host→plugin pushes, mirroring {@link HostPushes}. */\nexport const hostPushKeys = {\n\tcontext: \"context\",\n\tvisibility: \"visibility\",\n\tthemeChanged: \"themeChanged\",\n\tfontsChanged: \"fontsChanged\",\n\tlanguageChanged: \"languageChanged\",\n\tprefsChanged: \"prefsChanged\",\n\tcacheChanged: \"cacheChanged\",\n\tanchorJump: \"anchorJump\",\n\tresInvalidate: \"res:invalidate\",\n\tresourcesInvalidate: \"resources:invalidate\",\n\tmessagesInvalidate: \"messages:invalidate\",\n\tdanmakuInvalidate: \"danmaku:invalidate\",\n} as const satisfies Record<string, keyof HostPushes>\n\n/** Wire method names for plugin→host requests, mirroring {@link PluginRequests}. */\nexport const pluginMethods = {\n\t// Files\n\treadFile: \"readFile\",\n\tlistFiles: \"listFiles\",\n\n\t// Messages\n\tlistMessages: \"listMessages\",\n\tcreateMessage: \"createMessage\",\n\n\t// Danmaku\n\tlistDanmaku: \"listDanmaku\",\n\tcreateDanmaku: \"createDanmaku\",\n\n\t// Preferences / cache\n\tsetPref: \"setPref\",\n\tsetCache: \"setCache\",\n\n\t// Cache invalidation\n\tinvalidate: \"invalidate\",\n\n\t// Plugin asset vault\n\tdownload: \"download\",\n\tdeleteAsset: \"deleteAsset\",\n\n\t// Logging — must match the PluginRequests keys exactly,\n\t// otherwise plugin log calls are silently swallowed.\n\tlogInfo: \"logInfo\",\n\tlogWarn: \"logWarn\",\n\tlogError: \"logError\",\n} as const satisfies Record<string, keyof PluginRequests>\n\n/** Push key broadcast after each {@link InvalidateTarget} is invalidated. */\nexport const invalidatePushKeys = {\n\tresource: hostPushKeys.resInvalidate,\n\tresources: hostPushKeys.resourcesInvalidate,\n\tmessages: hostPushKeys.messagesInvalidate,\n\tdanmaku: hostPushKeys.danmakuInvalidate,\n} as const satisfies Record<InvalidateTarget, keyof HostPushes>\n\n/** Per-method bridge timeouts (ms), mirroring the entries above. */\nexport const pluginRequestTimeouts = {\n\treadFile: 120_000,\n\tdownload: 300_000,\n} as const satisfies Partial<Record<keyof PluginRequests, number>>\n\n/** Resolves to `T` only when `T` is `true`; otherwise a compile error. */\ntype AssertTrue<T extends true> = T\n\n// Reverse coverage: every table key must appear as a constant value.\n// Exported only so noUnusedLocals keeps these checks alive — never import.\nexport type _HostPushesCoveredByKeys = AssertTrue<\n\tkeyof HostPushes extends (typeof hostPushKeys)[keyof typeof hostPushKeys]\n\t\t? true\n\t\t: false\n>\nexport type _PluginRequestsCoveredByMethods = AssertTrue<\n\tkeyof PluginRequests extends (typeof pluginMethods)[keyof typeof pluginMethods]\n\t\t? true\n\t\t: false\n>\n\n/**\n * Type-safe host bridge. The runtime still serialises messages as plain\n * postMessage objects; this contract gives compile-time guarantees to callers.\n *\n * Per-method timeouts are declared on the protocol table entries\n * (`timeoutMs`) — the bridge reads them, callers never pass one.\n */\nexport type Host = {\n\trequest<K extends keyof PluginRequests>(\n\t\tmethod: K,\n\t\t...args: RequestInput<K> extends void ? [] : [RequestInput<K>]\n\t): Promise<RequestOutput<K>>\n\tsubscribe<K extends keyof HostPushes>(\n\t\tkey: K,\n\t\thandler: (data: HostPushes[K]) => void,\n\t): () => void\n\t/**\n\t * Internal — returns a Host whose requests are stamped with the given\n\t * resource scope (see {@link PluginRequest.resId}). Used by the runtime\n\t * to bind one API instance to the resource it was created for.\n\t */\n\twithScope: (resId: string) => Host\n}\n","import type {\n\tHost,\n\tHostMessage,\n\tHostPushes,\n\tPluginMessage,\n\tPluginRequests,\n\tPluginSubscribe,\n\tRequestInput,\n\tRequestOutput,\n} from \"./protocol.ts\"\nimport { PROTOCOL_VERSION, pluginRequestTimeouts } from \"./protocol.ts\"\n\n// ── Host bridge ──────────────────────────────────────────────────────────\n\nconst REQUEST_TIMEOUT_MS = 10_000\n\nlet nextId = 1\nlet hostBridge: Host | undefined\n\n/**\n * Narrow an unknown value to a plain record. Handy for decoding\n * plugin-defined payloads (e.g. anchor data) without assertion casts.\n */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value)\n}\n\nfunction isValidHostMessage(msg: unknown): msg is HostMessage {\n\treturn isRecord(msg) && \"type\" in msg\n}\n\n/**\n * Lazily create (and then reuse) the singleton postMessage bridge to the\n * host parent window: request/response with a 10s timeout plus push\n * subscriptions. Only the host window may drive the bridge — messages\n * from any other source are ignored. Called automatically by\n * {@link createIframeHostAPI}; you only need this when talking to the\n * host outside the plugin API surface.\n */\nexport function ensureHostBridge(): Host {\n\tif (hostBridge !== undefined) return hostBridge\n\n\tconst pending = new Map<\n\t\tnumber,\n\t\t{\n\t\t\tresolve(value: unknown): void\n\t\t\treject(reason: Error): void\n\t\t\ttimeoutId: ReturnType<typeof setTimeout>\n\t\t}\n\t>()\n\tconst subscribers = new Map<string, Set<(data: unknown) => void>>()\n\n\twindow.addEventListener(\n\t\t\"message\",\n\t\tfunction handleMessage(event: MessageEvent) {\n\t\t\t// Only the host (parent window) may drive this bridge; any\n\t\t\t// other window that obtains a reference to this frame must\n\t\t\t// not be able to inject fake responses or pushes.\n\t\t\t//\n\t\t\t// jsdom delivers same-window postMessage with `source: null`\n\t\t\t// (real browsers always carry the posting window). A null\n\t\t\t// source is only trusted when this frame IS its own parent —\n\t\t\t// the jsdom/test embedding — which cannot happen for a real\n\t\t\t// sandboxed iframe, where `window.parent !== window`.\n\t\t\tconst selfEmbedded = window.parent === window\n\t\t\tif (\n\t\t\t\tevent.source !== window.parent &&\n\t\t\t\t!(selfEmbedded && event.source === null)\n\t\t\t) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst msg = event.data\n\t\t\tif (!isValidHostMessage(msg)) return\n\n\t\t\tif (msg.type === \"response\") {\n\t\t\t\tconst entry = pending.get(msg.id)\n\t\t\t\tif (entry === undefined) return\n\t\t\t\tpending.delete(msg.id)\n\t\t\t\tclearTimeout(entry.timeoutId)\n\t\t\t\tif (msg.ok) {\n\t\t\t\t\tentry.resolve(msg.data)\n\t\t\t\t} else {\n\t\t\t\t\tconst err = new Error(msg.error ?? \"Unknown error\")\n\t\t\t\t\t// Preserve the machine-readable code (e.g. the asset\n\t\t\t\t\t// DENIED/UNAVAILABLE/POLICY vocabulary) so plugin code\n\t\t\t\t\t// can branch on `err.name` across the bridge.\n\t\t\t\t\tconst code = msg.errorCode ?? msg.errorName\n\t\t\t\t\tif (code !== undefined && code.length > 0) {\n\t\t\t\t\t\terr.name = code\n\t\t\t\t\t}\n\t\t\t\t\tentry.reject(err)\n\t\t\t\t}\n\t\t\t} else if (msg.type === \"push\") {\n\t\t\t\tconst handlers = subscribers.get(msg.key)\n\t\t\t\tif (handlers !== undefined) {\n\t\t\t\t\tfor (const handler of handlers) {\n\t\t\t\t\t\thandler(msg.data)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t)\n\n\tfunction request<K extends keyof PluginRequests>(\n\t\tresId: string | undefined,\n\t\tmethod: K,\n\t\t...args: RequestInput<K> extends void ? [] : [RequestInput<K>]\n\t): Promise<RequestOutput<K>> {\n\t\tconst id = nextId++\n\t\tconst input = args[0] as RequestInput<K> | undefined\n\t\t// Per-method timeouts are declared in the protocol meta\n\t\t// (`pluginRequestTimeouts`, e.g. downloads waiting on the consent\n\t\t// dialog) — callers never pass one.\n\t\tconst timeoutMs =\n\t\t\t(pluginRequestTimeouts as Partial<Record<string, number>>)[method] ??\n\t\t\tREQUEST_TIMEOUT_MS\n\t\tconst params = input\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeoutId = setTimeout(() => {\n\t\t\t\tpending.delete(id)\n\t\t\t\treject(new Error(`Request timed out: ${String(method)}`))\n\t\t\t}, timeoutMs)\n\t\t\tpending.set(id, {\n\t\t\t\tresolve: resolve as (value: unknown) => void,\n\t\t\t\treject,\n\t\t\t\ttimeoutId,\n\t\t\t})\n\t\t\tconst message: PluginMessage = {\n\t\t\t\ttype: \"request\",\n\t\t\t\tid,\n\t\t\t\tmethod: method as string,\n\t\t\t\tparams,\n\t\t\t\tproto: PROTOCOL_VERSION,\n\t\t\t\tresId,\n\t\t\t}\n\t\t\twindow.parent.postMessage(message, \"*\")\n\t\t})\n\t}\n\n\tfunction subscribe<K extends keyof HostPushes>(\n\t\tkey: K,\n\t\thandler: (data: HostPushes[K]) => void,\n\t): () => void {\n\t\tconst keyString = key as string\n\t\tlet handlers = subscribers.get(keyString)\n\t\tif (handlers === undefined) {\n\t\t\thandlers = new Set()\n\t\t\tsubscribers.set(keyString, handlers)\n\t\t\tconst message: PluginSubscribe = {\n\t\t\t\ttype: \"subscribe\",\n\t\t\t\tkey: keyString,\n\t\t\t\tproto: PROTOCOL_VERSION,\n\t\t\t}\n\t\t\twindow.parent.postMessage(message, \"*\")\n\t\t}\n\t\tconst wrapped = (data: unknown) => handler(data as HostPushes[K])\n\t\thandlers.add(wrapped)\n\t\treturn function unsubscribe() {\n\t\t\thandlers!.delete(wrapped)\n\t\t\tif (handlers!.size === 0) {\n\t\t\t\tsubscribers.delete(keyString)\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction makeHost(resId: string | undefined): Host {\n\t\treturn {\n\t\t\trequest(method, ...args) {\n\t\t\t\treturn request(resId, method, ...args)\n\t\t\t},\n\t\t\tsubscribe,\n\t\t\twithScope: makeHost,\n\t\t}\n\t}\n\n\thostBridge = makeHost(undefined)\n\treturn hostBridge\n}\n","import type { Codec } from \"./types.ts\"\n\n/**\n * JSON codec covering the common case (`number`, `string`, `boolean`, plain\n * objects). Falls back to `undefined` on parse errors so callers see \"missing\"\n * instead of a corrupted value.\n */\nexport function jsonCodec<T>(): Codec<T> {\n\treturn {\n\t\tencode(value) {\n\t\t\treturn JSON.stringify(value)\n\t\t},\n\t\tdecode(raw) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(raw) as T\n\t\t\t} catch {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Plain-string number codec. Avoids the `\"\"` → `0` ambiguity of\n * {@link jsonCodec} by treating any non-finite parse as missing.\n */\nexport function numberCodec(fallback?: number): Codec<number> {\n\treturn {\n\t\tencode(value) {\n\t\t\treturn String(value)\n\t\t},\n\t\tdecode(raw) {\n\t\t\tconst n = Number(raw)\n\t\t\treturn Number.isFinite(n) ? n : fallback\n\t\t},\n\t}\n}\n\n/**\n * Boolean codec using `\"1\"`/`\"0\"` for compact storage. Also accepts\n * `\"true\"`/`\"false\"` for interoperability.\n */\nexport function booleanCodec(): Codec<boolean> {\n\treturn {\n\t\tencode(value) {\n\t\t\treturn value ? \"1\" : \"0\"\n\t\t},\n\t\tdecode(raw) {\n\t\t\tif (raw === \"1\" || raw === \"true\") return true\n\t\t\tif (raw === \"0\" || raw === \"false\") return false\n\t\t\treturn undefined\n\t\t},\n\t}\n}\n","import { imageVariantQuery } from \"@hoardodile/sdk-types/image-variant\"\nimport type {\n\tFileUrlVariant,\n\tReactivePluginAPI,\n\tWebPluginAPI,\n} from \"./types.ts\"\n\n/**\n * Deep-partial override type for {@link createWebPluginAPI}: nested\n * objects and functions can be overridden selectively; functions may\n * also be replaced by `undefined` to drop the default no-op.\n */\nexport type DeepPartial<T> = {\n\t[K in keyof T]?: T[K] extends (...args: never[]) => unknown\n\t\t? T[K] | undefined\n\t\t: T[K] extends object\n\t\t\t? DeepPartial<T[K]>\n\t\t\t: T[K]\n}\n\n/** The complete plugin API shape stubbed by {@link createWebPluginAPI}. */\nexport type StubbedPluginAPI = WebPluginAPI & ReactivePluginAPI\n\nfunction mergeDeep<T extends object>(target: T, source: DeepPartial<T>): T {\n\tconst result = { ...target }\n\tfor (const key of Object.keys(source) as Array<keyof T>) {\n\t\tconst sourceValue = source[key]\n\t\tconst targetValue = result[key]\n\t\tif (\n\t\t\tsourceValue !== undefined &&\n\t\t\ttypeof sourceValue === \"object\" &&\n\t\t\t!Array.isArray(sourceValue) &&\n\t\t\ttargetValue !== undefined &&\n\t\t\ttypeof targetValue === \"object\" &&\n\t\t\t!Array.isArray(targetValue)\n\t\t) {\n\t\t\tresult[key] = mergeDeep(\n\t\t\t\ttargetValue as object,\n\t\t\t\tsourceValue as DeepPartial<object>,\n\t\t\t) as T[keyof T]\n\t\t} else if (sourceValue !== undefined) {\n\t\t\tresult[key] = sourceValue as T[keyof T]\n\t\t}\n\t}\n\treturn result\n}\n\n/**\n * Returns a minimal complete plugin API for render tests — the imperative\n * surface plus no-op reactive hooks. All fields return empty/loading/no-op\n * values; override via `overrides` to exercise plugin-specific code paths.\n *\n * Framework-agnostic counterpart of the React `createWebPluginAPI` in\n * `@hoardodile/sdk-react`: this one returns a plain object (no React\n * provider), that one wraps the same stub in a `StubPluginAPIProvider`\n * so component tests can render against it.\n */\nexport function createWebPluginAPI(\n\toverrides?: DeepPartial<StubbedPluginAPI>,\n): StubbedPluginAPI {\n\tconst base: StubbedPluginAPI = {\n\t\tlogInfo: () => {},\n\t\tlogWarn: () => {},\n\t\tlogError: () => {},\n\t\tresource: {\n\t\t\tid: \"r-test\",\n\t\t\tname: \"test\",\n\t\t\tsourceMeta: undefined,\n\t\t\tsearchMeta: undefined,\n\t\t\tfileStats: undefined,\n\t\t\tcontentPluginId: \"p-test\",\n\t\t},\n\t\tlistFiles: async () => [],\n\t\treadFile: async () => new ArrayBuffer(0),\n\t\tresolveFileUrl: (filename, variant) => buildMockFileUrl(filename, variant),\n\t\tresolveExtractedUrl: (path) => `/extracted/${path}`,\n\t\textractProgressUrl: () => \"/extract-progress/\",\n\t\tresolveBaseUrl: () => \"/files/\",\n\t\tresolveFrameUrl: (filename, timeMs) => `/frame/${filename}/${timeMs}`,\n\t\tdownload: async () => {\n\t\t\tthrow new Error(\"download stub not overridden\")\n\t\t},\n\t\tresolveAssetUrl: (path) => `/plugin-assets/p-test/./${path}`,\n\t\tdeleteAsset: async () => ({ existed: false }),\n\t\tlistMessages: async () => [],\n\t\tcreateMessage: async () => {\n\t\t\tthrow new Error(\"createMessage stub not overridden\")\n\t\t},\n\t\tlistDanmaku: async () => [],\n\t\tcreateDanmaku: async () => {\n\t\t\tthrow new Error(\"createDanmaku stub not overridden\")\n\t\t},\n\t\tgetPref: () => undefined,\n\t\tsetPref: () => {},\n\t\tgetCache: () => undefined,\n\t\tsetCache: () => {},\n\t\tlistCache: () => [],\n\t\tinvalidate: async () => {},\n\t\tonAnchorJump: () => () => {},\n\t\tuseFileList: () => ({\n\t\t\tdata: [],\n\t\t\tisLoading: false,\n\t\t\tisError: false,\n\t\t\terror: null,\n\t\t}),\n\t\tuseMessageList: () => ({\n\t\t\tdata: [],\n\t\t\tisLoading: false,\n\t\t\tisError: false,\n\t\t\terror: null,\n\t\t}),\n\t\tuseCreateMessage: () => ({\n\t\t\tmutate: async () => {\n\t\t\t\tthrow new Error(\"useCreateMessage stub not overridden\")\n\t\t\t},\n\t\t\tisPending: false,\n\t\t}),\n\t\tuseDanmakuList: () => ({\n\t\t\tdata: [],\n\t\t\tisLoading: false,\n\t\t\tisError: false,\n\t\t\terror: null,\n\t\t}),\n\t\tuseCreateDanmaku: () => ({\n\t\t\tmutate: async () => {\n\t\t\t\tthrow new Error(\"useCreateDanmaku stub not overridden\")\n\t\t\t},\n\t\t\tisPending: false,\n\t\t}),\n\t\tusePref: (_key, defaultValue, _codec) => [defaultValue, (_next) => {}],\n\t\tuseTheme: () => ({\n\t\t\tresolvedTheme: \"light\",\n\t\t\tpalette: \"mono\",\n\t\t\ticonStyle: \"duotone\",\n\t\t}),\n\t\tuseFont: () => ({ family: \"\", cssPaths: [] }),\n\t}\n\treturn overrides === undefined ? base : mergeDeep(base, overrides)\n}\n\n/**\n * Mock {@link WebPluginAPI.resolveFileUrl} shape: the same query the\n * real runtime emits for each variant (without the resId/token prefix).\n */\nfunction buildMockFileUrl(filename: string, variant?: FileUrlVariant): string {\n\tconst url = `/files/${filename}`\n\tif (variant === \"preview\") {\n\t\treturn `${url}?size=preview`\n\t}\n\tif (variant !== undefined && variant !== \"original\") {\n\t\treturn `${url}?${imageVariantQuery(variant)}`\n\t}\n\treturn url\n}\n","import type { PluginContextPainted, PluginIframeContext } from \"./protocol.ts\"\nimport { hostPushKeys, PROTOCOL_VERSION } from \"./protocol.ts\"\n\nlet pluginContext: PluginIframeContext | undefined\n\n/** The last context pushed by the host, if any. */\nexport function getPluginContext(): PluginIframeContext | undefined {\n\treturn pluginContext\n}\n\nfunction setPluginContext(ctx: PluginIframeContext): void {\n\tpluginContext = ctx\n}\n\n// ── Visibility (framework-agnostic store) ────────────────────────────────\n\nlet currentVisibility = true\nconst visibilityListeners = new Set<(visible: boolean) => void>()\n\n/**\n * Subscribe to iframe visibility changes (tab hidden, iframe released,\n * overlays opened by the host). The callback receives the current\n * visibility; returns an unsubscribe function. Backs `useVisibility`\n * in `@hoardodile/sdk-react`.\n */\nexport function subscribeToVisibility(\n\tcb: (visible: boolean) => void,\n): () => void {\n\tvisibilityListeners.add(cb)\n\treturn () => {\n\t\tvisibilityListeners.delete(cb)\n\t}\n}\n\n/** Current visibility snapshot without subscribing. */\nexport function getVisibilitySnapshot(): boolean {\n\treturn currentVisibility\n}\n\nfunction publishVisibilityChange(visible: boolean): void {\n\tif (currentVisibility === visible) return\n\tcurrentVisibility = visible\n\tfor (const cb of visibilityListeners) {\n\t\tcb(visible)\n\t}\n}\n\n// ── Mount lifecycle ──────────────────────────────────────────────────────\n\n/**\n * Invokes `cb` once the current DOM state has painted: the first rAF\n * frame paints the pending tree, the second guarantees the compositor\n * surface update landed. If rAF is throttled (document not rendering)\n * `cb` never fires — callers need their own fallback.\n */\nfunction afterNextPaintedFrame(cb: () => void): void {\n\trequestAnimationFrame(() => {\n\t\trequestAnimationFrame(cb)\n\t})\n}\n\n/**\n * Sets up listeners for host→plugin communication via `postMessage` and\n * `CustomEvent` fallback. The host pushes context and visibility updates;\n * this function invokes `mount(ctx)` whenever a new context arrives.\n *\n * Lifecycle contract: the host may replace the context at any time — a\n * pooled iframe document is reused across resources without a reload — so\n * `mount` runs once per context, not once per page load. Clean up\n * per-resource state yourself, or rely on `createPluginRoot` from\n * `@hoardodile/sdk-react`, which remounts the tree by `resId` by\n * default. When the host rebinds the iframe to a new resource, a late\n * unmount cache flush stamped with the old resId is stale-dropped by the\n * host (the plugin's debounced write still lands via the new binding).\n */\nexport function mountPlugin(mount: (ctx: PluginIframeContext) => void): void {\n\tfunction applyContext(ctx: PluginIframeContext) {\n\t\tsetPluginContext(ctx)\n\t\t// A fresh context means the host is showing this iframe again.\n\t\t// Publish rather than assign so plugins that keep their tree\n\t\t// mounted across contexts (remountOnResourceChange: false) wake\n\t\t// up after a release pushed visible:false; the publish helper\n\t\t// already dedupes no-change transitions.\n\t\tpublishVisibilityChange(true)\n\t\tmount(ctx)\n\t\t// Acknowledge only once the new tree has painted into the iframe's\n\t\t// compositor surface: the host keeps a freshly claimed iframe\n\t\t// transparent (but painted) until this ack arrives, and a bare DOM\n\t\t// commit would still replay the previous resource's last frame for\n\t\t// a frame or two while the new raster is in flight. If rAF is\n\t\t// throttled the ack never fires and the host's fallback takes over.\n\t\tafterNextPaintedFrame(() => {\n\t\t\twindow.parent.postMessage(\n\t\t\t\t{\n\t\t\t\t\ttype: \"contextPainted\",\n\t\t\t\t\tresId: ctx.resId,\n\t\t\t\t\tproto: PROTOCOL_VERSION,\n\t\t\t\t} satisfies PluginContextPainted,\n\t\t\t\t\"*\",\n\t\t\t)\n\t\t})\n\t}\n\n\twindow.addEventListener(\"message\", (event: MessageEvent) => {\n\t\t// Only trust context/visibility pushes from the host parent window.\n\t\tif (event.source !== window.parent) return\n\t\tconst msg = event.data\n\t\tif (!isRecord(msg) || msg.type !== \"push\") return\n\t\tif (msg.key === hostPushKeys.context) {\n\t\t\tapplyContext(msg.data as PluginIframeContext)\n\t\t} else if (msg.key === hostPushKeys.visibility) {\n\t\t\tpublishVisibilityChange((msg.data as { visible: boolean }).visible)\n\t\t}\n\t})\n\n\twindow.addEventListener(\"context-ready\", (e: Event) => {\n\t\tapplyContext((e as CustomEvent<PluginIframeContext>).detail)\n\t})\n\twindow.addEventListener(\"visibility-changed\", (e: Event) => {\n\t\tpublishVisibilityChange(\n\t\t\t(e as CustomEvent<{ visible: boolean }>).detail.visible,\n\t\t)\n\t})\n\n\tconst w = window as unknown as Record<string, unknown>\n\tif (w.__pluginContext !== undefined) {\n\t\tapplyContext(w.__pluginContext as PluginIframeContext)\n\t}\n\tif (w.__pluginVisibility !== undefined) {\n\t\tpublishVisibilityChange(\n\t\t\t(w.__pluginVisibility as { visible: boolean }).visible,\n\t\t)\n\t}\n}\n\n// ── Theme & font application ─────────────────────────────────────────────\n\n/** Applies theme classes to `document.documentElement` so CSS variables update. */\nexport function applyTheme(\n\tresolvedTheme: string,\n\tpalette: string,\n\ticonStyle: string,\n): void {\n\tconst root = document.documentElement\n\troot.classList.remove(\"light\", \"dark\")\n\troot.classList.add(resolvedTheme)\n\t// Strip every palette class generically — no per-palette list to keep in\n\t// sync, and stale classes from older versions get cleaned up too.\n\tfor (const cls of [...root.classList]) {\n\t\tif (cls.startsWith(\"theme-\")) root.classList.remove(cls)\n\t}\n\tif (palette !== \"mono\") {\n\t\troot.classList.add(`theme-${palette}`)\n\t}\n\t// `data-icon-style` drives the grayscale rule in `@hoardodile/ui/theme.css`;\n\t// `linear` only matters to the host's own icon registry.\n\troot.dataset.iconStyle = iconStyle\n}\n\n// The plugin bundle's theme.css only defines a `--font-sans` fallback;\n// nothing in it consumes `--font-app`, so the SDK installs this rule once\n// to make the document actually pick up the inherited font.\nconst FONT_STYLE_ID = \"plugin-host-font\"\n\n/**\n * Applies the host app font to the iframe document: injects each preset\n * stylesheet once (idempotent per path — they are absolute `/fonts/...`\n * URLs statically served by the host, which the sandboxed iframe can\n * load), then points `--font-app` at the family stack. An empty family\n * means the plugin opted out of font inheritance: the variable is\n * removed and the document falls back to the plugin's own `--font-sans`.\n */\nexport function applyFonts(family: string, cssPaths: readonly string[]): void {\n\tfor (const path of cssPaths) {\n\t\tconst selector = `link[rel=\"stylesheet\"][href=\"${path}\"]`\n\t\tif (document.head.querySelector(selector) !== null) continue\n\t\tconst link = document.createElement(\"link\")\n\t\tlink.rel = \"stylesheet\"\n\t\tlink.href = path\n\t\tdocument.head.appendChild(link)\n\t}\n\n\tconst root = document.documentElement\n\tif (family === \"\") {\n\t\troot.style.removeProperty(\"--font-app\")\n\t} else {\n\t\troot.style.setProperty(\"--font-app\", family)\n\t}\n\n\tif (document.getElementById(FONT_STYLE_ID) === null) {\n\t\tconst style = document.createElement(\"style\")\n\t\tstyle.id = FONT_STYLE_ID\n\t\tstyle.textContent = \"html{font-family:var(--font-app,var(--font-sans))}\"\n\t\tdocument.head.appendChild(style)\n\t}\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value)\n}\n","import type { PluginIframeContext } from \"./protocol.ts\"\n\n/**\n * In-memory module-level stores backing the plugin's pref/cache APIs\n * (`api.getPref`/`setPref`, `api.getCache`/`setCache`). Seeded from the\n * iframe context on mount; prefs mirror the host's plugin-wide settings,\n * cache is per-resource state that the host persists (debounced) and\n * restores on the next visit.\n */\nconst pluginPrefStore = new Map<string, string>()\nconst pluginCacheStore = new Map<string, string>()\n\n/**\n * Reset both stores and seed them from the iframe context. Called once\n * by the runtime on mount; must run before any pref/cache access.\n */\nexport function seedPluginStores(ctx: PluginIframeContext): void {\n\tpluginPrefStore.clear()\n\tfor (const [k, v] of Object.entries(ctx.initialPrefs)) {\n\t\tpluginPrefStore.set(k, v)\n\t}\n\tpluginCacheStore.clear()\n\tfor (const [k, v] of Object.entries(ctx.initialCache)) {\n\t\tpluginCacheStore.set(k, v)\n\t}\n}\n\n/** Read-only view of the plugin's in-memory pref store. */\nexport function getPluginPrefStore(): ReadonlyMap<string, string> {\n\treturn pluginPrefStore\n}\n\n/** Write a pref locally; mirror it to the host immediately. */\nexport function setPluginPref(key: string, value: string): void {\n\tpluginPrefStore.set(key, value)\n}\n\n/** Read-only view of the plugin's in-memory cache store. */\nexport function getPluginCacheStore(): ReadonlyMap<string, string> {\n\treturn pluginCacheStore\n}\n\n/**\n * Write a cache entry locally and mirror it to the host for persistence.\n * Continuously-changing state (scroll positions, resume timestamps)\n * should go through the debounced `useCacheWriter` in\n * `@hoardodile/sdk-react` instead of calling this directly on every\n * change.\n */\nexport function setPluginCache(key: string, value: string): void {\n\tpluginCacheStore.set(key, value)\n}\n\n/**\n * Snapshot of all cache entries, as returned by `api.listCache`. Values\n * are the raw serialized strings stored via {@link setPluginCache}.\n */\nexport function snapshotCacheEntries(): {\n\treadonly key: string\n\treadonly value: string\n}[] {\n\tconst result: { readonly key: string; readonly value: string }[] = []\n\tfor (const [key, value] of pluginCacheStore) {\n\t\tresult.push({ key, value })\n\t}\n\treturn result\n}\n\n// ── Pref change pub/sub ──────────────────────────────────────────────────\n\nconst prefChangeListeners = new Map<string, Set<() => void>>()\n\n/**\n * Subscribe to pref changes for `key` (both local writes and host-pushed\n * updates). Returns an unsubscribe function. Backs the reactive\n * `usePref` hook in `@hoardodile/sdk-react`.\n */\nexport function subscribeToPrefChanges(\n\tkey: string,\n\tcb: () => void,\n): () => void {\n\tlet listeners = prefChangeListeners.get(key)\n\tif (listeners === undefined) {\n\t\tlisteners = new Set()\n\t\tprefChangeListeners.set(key, listeners)\n\t}\n\tlisteners.add(cb)\n\treturn function unsubscribe() {\n\t\tlisteners!.delete(cb)\n\t\tif (listeners!.size === 0) {\n\t\t\tprefChangeListeners.delete(key)\n\t\t}\n\t}\n}\n\n/** Notify all subscribers of `key` about a value change. */\nexport function broadcastPrefChange(key: string): void {\n\tconst listeners = prefChangeListeners.get(key)\n\tif (listeners === undefined) return\n\tfor (const cb of listeners) {\n\t\tcb()\n\t}\n}\n","import { imageVariantQuery } from \"@hoardodile/sdk-types/image-variant\"\nimport type { FileUrlVariant } from \"./types.ts\"\n\n// ── File URL resolution ──────────────────────────────────────────────────\n// Pure string builders: the iframe's null origin cannot send cookies, so\n// the host embeds a short-lived token in the path (see\n// apps/server/src/infra/http/plugin.ts). Kept side-effect free so the\n// wire shape is unit-testable without the host bridge.\n\nexport function resolveFilesBaseUrl(resId: string, token: string): string {\n\treturn `/api/resources/${resId}/files/${encodeURIComponent(token)}/`\n}\n\n/**\n * Build the URL for a resource file. Without `variant` (or with\n * `\"original\"`) the original bytes are addressed; `\"preview\"` selects\n * the default preview variant; an {@link ImageVariantSpec} requests a\n * custom derived image. Derived URLs always carry `size=preview` (the\n * compatibility alias) alongside the explicit variant parameters, so a\n * server that predates the generic contract degrades to its default\n * preview instead of silently serving the original.\n */\nexport function buildFileUrl(\n\tresId: string,\n\tfilename: string,\n\ttoken: string,\n\tvariant?: FileUrlVariant,\n): string {\n\tconst url = `/api/resources/${resId}/files/${encodeURIComponent(token)}/${encodeURIComponent(filename)}`\n\tif (variant === \"preview\") {\n\t\treturn `${url}?size=preview`\n\t}\n\tif (variant !== undefined && variant !== \"original\") {\n\t\treturn `${url}?${imageVariantQuery(variant)}`\n\t}\n\treturn url\n}\n\n/** Build the URL for a video frame thumbnail at `timeMs`. */\nexport function buildFrameUrl(\n\tresId: string,\n\tfilename: string,\n\ttimeMs: number,\n\ttoken: string,\n): string {\n\tconst time = String(Math.max(0, Math.round(timeMs)))\n\treturn `/api/resources/${resId}/frame/${encodeURIComponent(token)}/${encodeURIComponent(filename)}/${time}`\n}\n\n/**\n * Build the URL of a file in the plugin's own asset vault. The host\n * serves it via the tokenized `/api/plugin-assets/:id/:token/:path`\n * route (see `apps/server/src/infra/http/plugin-assets.ts`): `token` is\n * the plugin-scoped asset token from the iframe context — vault files\n * are host data, so they are served fresh (`no-cache`), never through\n * the service worker.\n */\nexport function buildAssetUrl(\n\tpluginId: string,\n\tpath: string,\n\ttoken: string,\n): string {\n\treturn `/api/plugin-assets/${encodeURIComponent(pluginId)}/${encodeURIComponent(\n\t\ttoken,\n\t)}/${encodeURIComponent(path)}`\n}\n","import type {\n\tAnchorData,\n\tDanmaku,\n\tDanmakuListFilter,\n\tDanmakuMode,\n\tMessage,\n\tPluginAssetDeleteResult,\n\tPluginDownloadRequest,\n\tPluginDownloadResult,\n\tPluginSchema,\n} from \"@hoardodile/sdk-types\"\nimport { ensureHostBridge } from \"./bridge.ts\"\nimport type {\n\tInvalidateTarget,\n\tPluginFonts,\n\tPluginIframeContext,\n\tReadFileRange,\n} from \"./protocol.ts\"\nimport {\n\tbroadcastPrefChange,\n\tgetPluginCacheStore,\n\tgetPluginPrefStore,\n\tseedPluginStores,\n\tsetPluginCache,\n\tsetPluginPref,\n\tsnapshotCacheEntries,\n} from \"./stores.ts\"\nimport type { FileUrlVariant, WebPluginAPI } from \"./types.ts\"\nimport {\n\tbuildAssetUrl,\n\tbuildFileUrl,\n\tbuildFrameUrl,\n\tresolveFilesBaseUrl,\n} from \"./urls.ts\"\n\n// ── Pure helpers ─────────────────────────────────────────────────────────\n\n/**\n * Narrow an unknown value to a plain record. Handy for decoding\n * plugin-defined payloads (e.g. anchor data) without assertion casts.\n */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value)\n}\n\n/**\n * Extract `{ resolvedTheme, palette, iconStyle }` from an unknown host\n * payload. Malformed input yields `undefined` fields rather than throwing —\n * theme pushes are best-effort.\n */\nexport function extractThemePayload(data: unknown): {\n\tresolvedTheme: string | undefined\n\tpalette: string | undefined\n\ticonStyle: string | undefined\n} {\n\tif (!isRecord(data)) {\n\t\treturn {\n\t\t\tresolvedTheme: undefined,\n\t\t\tpalette: undefined,\n\t\t\ticonStyle: undefined,\n\t\t}\n\t}\n\treturn {\n\t\tresolvedTheme:\n\t\t\ttypeof data.resolvedTheme === \"string\" ? data.resolvedTheme : undefined,\n\t\tpalette: typeof data.palette === \"string\" ? data.palette : undefined,\n\t\ticonStyle: typeof data.iconStyle === \"string\" ? data.iconStyle : undefined,\n\t}\n}\n\n/**\n * Extract the host app font payload (`family` + stylesheet paths) from\n * an unknown host push; `undefined` when the shape does not match.\n */\nexport function extractFontsPayload(data: unknown): PluginFonts | undefined {\n\tif (!isRecord(data) || typeof data.family !== \"string\") return undefined\n\tconst cssPaths = Array.isArray(data.cssPaths)\n\t\t? data.cssPaths.filter((p): p is string => typeof p === \"string\")\n\t\t: []\n\treturn { family: data.family, cssPaths }\n}\n\n/**\n * Extract a `{ key, value }` pref update from an unknown host push;\n * `undefined` when the shape does not match. `value` may be `undefined`\n * for a removal.\n */\nexport function extractPrefPayload(\n\tdata: unknown,\n): { readonly key: string; readonly value: string | undefined } | undefined {\n\tif (!isRecord(data)) return undefined\n\tconst key = data.key\n\tif (typeof key !== \"string\") return undefined\n\treturn {\n\t\tkey,\n\t\tvalue: typeof data.value === \"string\" ? data.value : undefined,\n\t}\n}\n\n// ── Public API ───────────────────────────────────────────────────────────\n\n/**\n * Builds the full {@link WebPluginAPI} for a plugin running inside a sandboxed\n * iframe. Communicates with the host via postMessage.\n */\nexport function createIframeHostAPI<\n\tTSchema extends PluginSchema = PluginSchema,\n>(ctx: PluginIframeContext): WebPluginAPI<TSchema> {\n\tconst host = ensureHostBridge().withScope(ctx.resId)\n\tseedPluginStores(ctx)\n\n\tfunction logInfo(message: string, data?: Record<string, unknown>): void {\n\t\thost.request(\"logInfo\", { message, data }).catch(() => {})\n\t}\n\tfunction logWarn(message: string, data?: Record<string, unknown>): void {\n\t\thost.request(\"logWarn\", { message, data }).catch(() => {})\n\t}\n\tfunction logError(message: string, data?: Record<string, unknown>): void {\n\t\thost.request(\"logError\", { message, data }).catch(() => {})\n\t}\n\n\tfunction listFiles(): Promise<readonly TSchema[\"file\"][]> {\n\t\treturn host.request(\"listFiles\") as Promise<readonly TSchema[\"file\"][]>\n\t}\n\n\tfunction readFile(path: string, range?: ReadFileRange): Promise<ArrayBuffer> {\n\t\treturn host.request(\"readFile\", { path, range })\n\t}\n\n\tfunction resolveFileUrl(filename: string, variant?: FileUrlVariant): string {\n\t\treturn buildFileUrl(ctx.resId, filename, ctx.fileToken, variant)\n\t}\n\n\tfunction resolveExtractedUrl(path: string): string {\n\t\treturn `/api/resources/${ctx.resId}/extracted/${encodeURIComponent(\n\t\t\tctx.fileToken,\n\t\t)}/${encodeURIComponent(path)}`\n\t}\n\n\tfunction extractProgressUrl(): string {\n\t\t// The trailing slash carries the token segment the auth preHandler\n\t\t// strips (see apps/server/src/infra/http/plugin.ts).\n\t\treturn `/api/resources/${ctx.resId}/extract-progress/${encodeURIComponent(\n\t\t\tctx.fileToken,\n\t\t)}/`\n\t}\n\n\tfunction resolveBaseUrl(): string {\n\t\treturn resolveFilesBaseUrl(ctx.resId, ctx.fileToken)\n\t}\n\n\tfunction resolveFrameUrl(filename: string, timeMs: number): string {\n\t\treturn buildFrameUrl(ctx.resId, filename, timeMs, ctx.fileToken)\n\t}\n\n\tfunction listMessages(): Promise<readonly Message[]> {\n\t\treturn host.request(\"listMessages\")\n\t}\n\n\tfunction createMessage(input: {\n\t\treadonly body: string\n\t\treadonly anchor?: unknown\n\t}): Promise<Message> {\n\t\t// The plugin deals in raw location data; the wire anchor is the\n\t\t// `{ data }` envelope (the resource id is host state).\n\t\treturn host.request(\"createMessage\", {\n\t\t\tbody: input.body,\n\t\t\tanchor: input.anchor === undefined ? undefined : { data: input.anchor },\n\t\t})\n\t}\n\n\tfunction listDanmaku(\n\t\tfilter?: DanmakuListFilter,\n\t): Promise<readonly Danmaku[]> {\n\t\treturn host.request(\"listDanmaku\", { filter })\n\t}\n\n\tfunction createDanmaku(input: {\n\t\treadonly text: string\n\t\treadonly anchor: unknown\n\t\treadonly mode?: DanmakuMode\n\t}): Promise<Danmaku> {\n\t\treturn host.request(\"createDanmaku\", {\n\t\t\ttext: input.text,\n\t\t\tanchor: { data: input.anchor },\n\t\t\tmode: input.mode,\n\t\t})\n\t}\n\n\tfunction getPref(key: string): string | undefined {\n\t\treturn getPluginPrefStore().get(key) ?? undefined\n\t}\n\n\tfunction setPref(key: string, value: string): void {\n\t\tsetPluginPref(key, value)\n\t\tbroadcastPrefChange(key)\n\t\thost.request(\"setPref\", { key, value }).catch(() => {})\n\t}\n\n\tfunction getCache(key: string): string | undefined {\n\t\treturn getPluginCacheStore().get(key) ?? undefined\n\t}\n\n\tfunction setCache(key: string, value: string): void {\n\t\tsetPluginCache(key, value)\n\t\thost.request(\"setCache\", { key, value }).catch(() => {})\n\t}\n\n\tfunction listCache(): readonly {\n\t\treadonly key: string\n\t\treadonly value: string\n\t}[] {\n\t\treturn snapshotCacheEntries()\n\t}\n\n\tfunction invalidate(target: InvalidateTarget): Promise<void> {\n\t\treturn host.request(\"invalidate\", { target })\n\t}\n\n\tfunction download(\n\t\trequest: PluginDownloadRequest,\n\t): Promise<PluginDownloadResult>\n\tfunction download(\n\t\trequests: readonly PluginDownloadRequest[],\n\t): Promise<readonly PluginDownloadResult[]>\n\tfunction download(\n\t\trequest: PluginDownloadRequest | readonly PluginDownloadRequest[],\n\t): Promise<PluginDownloadResult | readonly PluginDownloadResult[]> {\n\t\t// The protocol table declares the 5-minute ceiling (consent dialog\n\t\t// + transfer) — the bridge reads it; no per-call override here.\n\t\t// The host answers with the array shape on the wire; a single call\n\t\t// unwraps its one result, so the plugin sees the shape it sent.\n\t\treturn host.request(\"download\", request).then((result) => {\n\t\t\tif (Array.isArray(request)) {\n\t\t\t\treturn result as readonly PluginDownloadResult[]\n\t\t\t}\n\t\t\treturn (result as readonly PluginDownloadResult[])[0]!\n\t\t})\n\t}\n\n\tfunction resolveAssetUrl(path: string): string {\n\t\tif (ctx.assetToken.length === 0) {\n\t\t\tthrow new Error(\n\t\t\t\t'resolveAssetUrl() — the plugin has no asset token: declare \"download\": true in the manifest and reload the preview',\n\t\t\t)\n\t\t}\n\t\treturn buildAssetUrl(ctx.pluginId, path, ctx.assetToken)\n\t}\n\n\tfunction deleteAsset(path: string): Promise<PluginAssetDeleteResult> {\n\t\treturn host.request(\"deleteAsset\", { path })\n\t}\n\n\tfunction onAnchorJump(cb: (anchor: AnchorData) => void): () => void {\n\t\treturn host.subscribe(\"anchorJump\", cb)\n\t}\n\n\treturn {\n\t\tlogInfo,\n\t\tlogWarn,\n\t\tlogError,\n\t\tresource: {\n\t\t\tid: ctx.resId,\n\t\t\tname: ctx.resName,\n\t\t\tsourceMeta: ctx.sourceMeta as TSchema[\"sourceMeta\"],\n\t\t\tsearchMeta: ctx.searchMeta as TSchema[\"searchMeta\"],\n\t\t\tfileStats: ctx.fileStats,\n\t\t\tcontentPluginId: ctx.contentPluginId,\n\t\t},\n\t\tlistFiles,\n\t\treadFile,\n\t\tresolveFileUrl,\n\t\tresolveExtractedUrl,\n\t\textractProgressUrl,\n\t\tresolveBaseUrl,\n\t\tresolveFrameUrl,\n\t\tdownload,\n\t\tresolveAssetUrl,\n\t\tdeleteAsset,\n\t\tlistMessages,\n\t\tcreateMessage,\n\t\tlistDanmaku,\n\t\tcreateDanmaku,\n\t\tgetPref,\n\t\tsetPref,\n\t\tgetCache,\n\t\tsetCache,\n\t\tlistCache,\n\t\tinvalidate,\n\t\tonAnchorJump,\n\t} satisfies WebPluginAPI<TSchema>\n}\n"]}
1
+ {"version":3,"sources":["../src/protocol.ts","../src/bridge.ts","../src/codecs.ts","../src/fixtures.ts","../src/lifecycle.ts","../src/stores.ts","../src/urls.ts","../src/runtime.ts"],"names":["isRecord","imageVariantQuery"],"mappings":"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","file":"index.js","sourcesContent":["import type {\n\tFileStats,\n\tPluginAssetDeleteResult,\n\tPluginDownloadRequest,\n\tPluginDownloadResult,\n\tReadFileRange,\n\tSearchMeta,\n\tSerializedFileList,\n} from \"@hoardodile/sdk-types\"\n\n/**\n * Wire protocol version shared between the plugin SDK and the browser\n * host. Bumped only on incompatible protocol changes. Plugins stamp every\n * outbound message with it; the host warns loudly when a plugin was built\n * against a different version.\n */\nexport const PROTOCOL_VERSION = 1 as const\n\n/** Shared read-range contract; defined once in `@hoardodile/sdk-types`. */\nexport type { ReadFileRange } from \"@hoardodile/sdk-types\"\n\nexport type PluginResolvedTheme = \"light\" | \"dark\"\n\n/**\n * Canonical list of theme palette ids — the single source of truth shared\n * by the host app and every plugin. `mono` is the default and has no CSS\n * class (it lives in `:root` / `.dark`); every other id maps to a\n * `.theme-<id>` block in `@hoardodile/ui/theme.css` and a\n * `theme.palette.<id>` i18n label.\n */\nexport const pluginThemePalettes = [\n\t\"mono\",\n\t\"sage\",\n\t\"parchment\",\n\t\"azure\",\n\t\"hoardodile\",\n] as const\n\nexport type PluginThemePalette = (typeof pluginThemePalettes)[number]\n\n/** Icon rendering style as chosen in host Settings → Icons. */\nexport type PluginIconStyle = \"duotone\" | \"grayscale\" | \"linear\"\n\n/** Host app font as observed by the plugin. */\nexport type PluginFonts = {\n\t/** CSS `font-family` stack; empty when the plugin opted out of inheritance. */\n\treadonly family: string\n\t/** Absolute paths (`/fonts/...`) of the preset stylesheets backing the stack. */\n\treadonly cssPaths: readonly string[]\n}\n\n/**\n * Context injected into the iframe as `window.__context__` and pushed via\n * the `context` host push. Not a one-shot: the host may push a replacement\n * context at any time (a pooled iframe is rebound across resources without\n * a reload), and every push re-invokes the `mountPlugin` mount callback.\n */\nexport type PluginIframeContext = {\n\treadonly pluginId: string\n\treadonly resId: string\n\treadonly resName: string\n\treadonly sourceMeta: unknown\n\treadonly searchMeta: SearchMeta | undefined\n\treadonly fileStats: FileStats | undefined\n\treadonly contentPluginId: string\n\t/** Current UI language code. The iframe uses this to select its own locale bundle. */\n\treadonly language: string\n\t/** Current resolved theme (light or dark). */\n\treadonly resolvedTheme: PluginResolvedTheme\n\t/** Current theme palette. */\n\treadonly palette: PluginThemePalette\n\t/** Current icon rendering style (Settings → Icons). */\n\treadonly iconStyle: PluginIconStyle\n\t/**\n\t * Host app font to apply inside the iframe: a CSS `font-family` stack\n\t * plus the preset stylesheets that back it. An empty family means the\n\t * plugin opted out (`ui.inheritFont: false`) and keeps its own fonts.\n\t */\n\treadonly fonts: PluginFonts\n\t/** Initial plugin-scoped prefs (unprefixed keys) loaded from server. */\n\treadonly initialPrefs: Record<string, string>\n\t/** Initial plugin+resId cache entries (unprefixed keys) loaded from server. */\n\treadonly initialCache: Record<string, string>\n\t/**\n\t * Short-lived token that lets the sandboxed iframe fetch resource files\n\t * without a session cookie (null-origin iframe cannot send SameSite cookies).\n\t */\n\treadonly fileToken: string\n\t/**\n\t * Short-lived token for the plugin's own asset vault URLs\n\t * (`/api/plugin-assets/<pluginId>/<token>/<path>`), issued only when\n\t * the plugin's manifest declares the `download` permission. Empty\n\t * string otherwise — `resolveAssetUrl` throws on the empty token\n\t * (never builds a malformed `/…//path` URL); check the permission\n\t * before relying on vault URLs.\n\t */\n\treadonly assetToken: string\n}\n\n/** Wire request from plugin (iframe) to host. */\nexport type PluginRequest = {\n\treadonly type: \"request\"\n\treadonly id: number\n\treadonly method: string\n\treadonly params?: unknown\n\t/** Wire protocol version the plugin was built against (see {@link PROTOCOL_VERSION}). */\n\treadonly proto?: number\n\t/**\n\t * SDK-internal scope stamp: the resource the request was issued for,\n\t * captured by the runtime when the plugin called the API. The host\n\t * drops the request as stale when the stamp no longer matches the\n\t * iframe's binding (e.g. an unmount flush racing a rebind), so late\n\t * requests never leak into the wrong resource. Plugin code never\n\t * sets this.\n\t */\n\treadonly resId?: string\n}\n\n/** Wire response from host to plugin for a prior request. */\nexport type HostResponse = {\n\treadonly type: \"response\"\n\treadonly id: number\n\treadonly ok: boolean\n\treadonly data?: unknown\n\treadonly error?: string\n\t/**\n\t * Optional machine-readable plugin error code (e.g. the asset\n\t * `DENIED` / `UNAVAILABLE` / `POLICY` vocabulary) so the bridge can\n\t * reject with an Error carrying the code — plugin code branches on\n\t * `err.name` across the postMessage boundary.\n\t */\n\treadonly errorCode?: string\n\t/**\n\t * Legacy alias of {@link errorCode} (same value) kept for host\n\t * builds predating the unified field. The bridge reads\n\t * `errorCode ?? errorName`.\n\t */\n\treadonly errorName?: string\n}\n\n/** Wire push event from host to plugin. */\nexport type HostPush = {\n\treadonly type: \"push\"\n\treadonly key: string\n\treadonly data?: unknown\n}\n\n/** Wire subscription request from plugin to host. */\nexport type PluginSubscribe = {\n\treadonly type: \"subscribe\"\n\treadonly key: string\n\t/** Wire protocol version the plugin was built against (see {@link PROTOCOL_VERSION}). */\n\treadonly proto?: number\n}\n\n/**\n * Wire acknowledgement from plugin to host: the pushed context has been\n * applied *and painted* — the mount callback returned (for React plugins\n * the new tree is already committed via flushSync) and a frame with the\n * new content has reached the compositor. The host keeps a freshly\n * claimed pooled iframe transparent until this arrives, so the previous\n * resource's content never shows under a new claim.\n */\nexport type PluginContextPainted = {\n\treadonly type: \"contextPainted\"\n\treadonly resId: string\n\t/** Wire protocol version the plugin was built against (see {@link PROTOCOL_VERSION}). */\n\treadonly proto?: number\n}\n\n/** Union of all messages a plugin can send to the host. */\nexport type PluginMessage =\n\t| PluginRequest\n\t| PluginSubscribe\n\t| PluginContextPainted\n\n/** Union of all messages the host can send to a plugin. */\nexport type HostMessage = HostResponse | HostPush\n\n/** Type-safe request protocol table. Each entry declares input and output. */\nexport type PluginRequests = {\n\tlogInfo: {\n\t\treadonly input: {\n\t\t\treadonly message: string\n\t\t\treadonly data?: Record<string, unknown>\n\t\t}\n\t\treadonly output: undefined\n\t}\n\tlogWarn: {\n\t\treadonly input: {\n\t\t\treadonly message: string\n\t\t\treadonly data?: Record<string, unknown>\n\t\t}\n\t\treadonly output: undefined\n\t}\n\tlogError: {\n\t\treadonly input: {\n\t\t\treadonly message: string\n\t\t\treadonly data?: Record<string, unknown>\n\t\t}\n\t\treadonly output: undefined\n\t}\n\tlistFiles: {\n\t\treadonly input: undefined\n\t\treadonly output: SerializedFileList\n\t}\n\treadFile: {\n\t\treadonly input: {\n\t\t\treadonly path: string\n\t\t\t/** Byte range (see {@link ReadFileRange}); omitted = whole file. */\n\t\t\treadonly range?: ReadFileRange\n\t\t}\n\t\treadonly output: ArrayBuffer\n\t\t// Timeout: see {@link pluginRequestTimeouts.readFile} (large files\n\t\t// stream through the host process before the bytes arrive).\n\t}\n\tlistMessages: {\n\t\treadonly input: undefined\n\t\treadonly output: readonly import(\"@hoardodile/sdk-types\").Message[]\n\t}\n\tcreateMessage: {\n\t\treadonly input: {\n\t\t\treadonly body: string\n\t\t\t/** Wire anchor envelope; plugins pass raw data, the SDK wraps it. */\n\t\t\treadonly anchor?: import(\"@hoardodile/sdk-types\").AnchorData\n\t\t}\n\t\treadonly output: import(\"@hoardodile/sdk-types\").Message\n\t}\n\tlistDanmaku: {\n\t\treadonly input: {\n\t\t\treadonly filter?: import(\"@hoardodile/sdk-types\").DanmakuListFilter\n\t\t}\n\t\treadonly output: readonly import(\"@hoardodile/sdk-types\").Danmaku[]\n\t}\n\tcreateDanmaku: {\n\t\treadonly input: {\n\t\t\treadonly text: string\n\t\t\t/** Wire anchor envelope (see {@link PluginRequests.createMessage}). */\n\t\t\treadonly anchor: import(\"@hoardodile/sdk-types\").AnchorData\n\t\t\treadonly mode?: import(\"@hoardodile/sdk-types\").DanmakuMode\n\t\t}\n\t\treadonly output: import(\"@hoardodile/sdk-types\").Danmaku\n\t}\n\tsetPref: {\n\t\t/** Persist a plugin-wide preference; host broadcasts `prefsChanged`. */\n\t\treadonly input: { readonly key: string; readonly value: string }\n\t\treadonly output: undefined\n\t}\n\tsetCache: {\n\t\t/**\n\t\t * Persist a per-resource cache entry; host broadcasts\n\t\t * `cacheChanged`.\n\t\t */\n\t\treadonly input: {\n\t\t\treadonly key: string\n\t\t\treadonly value: string\n\t\t}\n\t\treadonly output: undefined\n\t}\n\tinvalidate: {\n\t\t/** Request the host to invalidate cached data for a target. */\n\t\treadonly input: { readonly target: InvalidateTarget }\n\t\treadonly output: undefined\n\t}\n\t/**\n\t * User-consented download into the plugin's own asset vault (see\n\t * `@hoardodile/sdk-types/plugin-asset`). The host asks the user with\n\t * the shared consent dialog — an array of requests is ONE dialog\n\t * listing every item (all-or-nothing); cached destinations resolve\n\t * without any dialog. Rejections carry a machine-readable `err.name`\n\t * (`DENIED` / `UNAVAILABLE` / `POLICY`).\n\t *\n\t * The wire mirrors the call: single in → single result out, array in\n\t * → array of results out, in request order.\n\t *\n\t * Timeout: {@link pluginRequestTimeouts.download} — the client-side\n\t * ceiling for the whole flow (consent dialog + transfer), declared in\n\t * the protocol meta rather than ad hoc at the call site.\n\t */\n\tdownload: {\n\t\treadonly input: PluginDownloadRequest | readonly PluginDownloadRequest[]\n\t\treadonly output: PluginDownloadResult | readonly PluginDownloadResult[]\n\t}\n\t/**\n\t * Remove a vault file (idempotent); the plugin decides its own vault\n\t * lifecycle — no user consent, nothing leaves the host.\n\t */\n\tdeleteAsset: {\n\t\treadonly input: { readonly path: string }\n\t\treadonly output: PluginAssetDeleteResult\n\t}\n}\n\n/** Type-safe push protocol table. */\nexport type HostPushes = {\n\tcontext: PluginIframeContext\n\tvisibility: { readonly visible: boolean }\n\tthemeChanged: {\n\t\treadonly resolvedTheme: string\n\t\treadonly palette: string\n\t\t/** Active icon rendering style — host applies it as `data-icon-style`. */\n\t\treadonly iconStyle: PluginIconStyle\n\t}\n\tfontsChanged: PluginFonts\n\t/**\n\t * The wire payload is a bare language-code string: it predates the\n\t * typed protocol table and must stay stable for already-installed\n\t * plugin builds (see `pushLanguageChanged` in apps/web — do not wrap\n\t * it in an object).\n\t */\n\tlanguageChanged: string\n\tprefsChanged: { readonly key: string; readonly value?: string }\n\t/**\n\t * A plugin+resource cache entry changed. With data, carries the single\n\t * changed entry; without data (undefined), all entries were cleared and\n\t * the plugin should drop its whole cache store.\n\t */\n\tcacheChanged:\n\t\t| { readonly resId: string; readonly key: string; readonly value?: string }\n\t\t| undefined\n\t/**\n\t * Host-initiated request to jump to an anchor (e.g. the user clicked a\n\t * comment anchor in the host UI). Carries the plugin-defined anchor data\n\t * only — the resource is always the iframe's own.\n\t */\n\tanchorJump: import(\"@hoardodile/sdk-types\").AnchorData\n\t\"res:invalidate\": undefined\n\t\"resources:invalidate\": undefined\n\t\"messages:invalidate\": undefined\n\t\"danmaku:invalidate\": undefined\n}\n\n/** Extract the input type for a request key. */\nexport type RequestInput<K extends keyof PluginRequests> =\n\tPluginRequests[K][\"input\"]\n\n/** Extract the output type for a request key. */\nexport type RequestOutput<K extends keyof PluginRequests> =\n\tPluginRequests[K][\"output\"]\n\n/** Targets that can be invalidated from the plugin runtime. */\nexport type InvalidateTarget = \"resource\" | \"resources\" | \"messages\" | \"danmaku\"\n\n// ── Runtime wire constants ─────────────────────────────────────────────────\n//\n// The type tables above are the protocol's single source of truth; these\n// constants are their runtime mirror. The two are interlocked at compile\n// time in both directions: a constant value that is not a table key fails\n// `satisfies`, and a table key missing from the constant fails the\n// `AssertTrue` checks below. Add a key to both or neither.\n\n/** Wire keys for host→plugin pushes, mirroring {@link HostPushes}. */\nexport const hostPushKeys = {\n\tcontext: \"context\",\n\tvisibility: \"visibility\",\n\tthemeChanged: \"themeChanged\",\n\tfontsChanged: \"fontsChanged\",\n\tlanguageChanged: \"languageChanged\",\n\tprefsChanged: \"prefsChanged\",\n\tcacheChanged: \"cacheChanged\",\n\tanchorJump: \"anchorJump\",\n\tresInvalidate: \"res:invalidate\",\n\tresourcesInvalidate: \"resources:invalidate\",\n\tmessagesInvalidate: \"messages:invalidate\",\n\tdanmakuInvalidate: \"danmaku:invalidate\",\n} as const satisfies Record<string, keyof HostPushes>\n\n/** Wire method names for plugin→host requests, mirroring {@link PluginRequests}. */\nexport const pluginMethods = {\n\t// Files\n\treadFile: \"readFile\",\n\tlistFiles: \"listFiles\",\n\n\t// Messages\n\tlistMessages: \"listMessages\",\n\tcreateMessage: \"createMessage\",\n\n\t// Danmaku\n\tlistDanmaku: \"listDanmaku\",\n\tcreateDanmaku: \"createDanmaku\",\n\n\t// Preferences / cache\n\tsetPref: \"setPref\",\n\tsetCache: \"setCache\",\n\n\t// Cache invalidation\n\tinvalidate: \"invalidate\",\n\n\t// Plugin asset vault\n\tdownload: \"download\",\n\tdeleteAsset: \"deleteAsset\",\n\n\t// Logging — must match the PluginRequests keys exactly,\n\t// otherwise plugin log calls are silently swallowed.\n\tlogInfo: \"logInfo\",\n\tlogWarn: \"logWarn\",\n\tlogError: \"logError\",\n} as const satisfies Record<string, keyof PluginRequests>\n\n/** Push key broadcast after each {@link InvalidateTarget} is invalidated. */\nexport const invalidatePushKeys = {\n\tresource: hostPushKeys.resInvalidate,\n\tresources: hostPushKeys.resourcesInvalidate,\n\tmessages: hostPushKeys.messagesInvalidate,\n\tdanmaku: hostPushKeys.danmakuInvalidate,\n} as const satisfies Record<InvalidateTarget, keyof HostPushes>\n\n/** Per-method bridge timeouts (ms), mirroring the entries above. */\nexport const pluginRequestTimeouts = {\n\treadFile: 120_000,\n\tdownload: 300_000,\n} as const satisfies Partial<Record<keyof PluginRequests, number>>\n\n/** Resolves to `T` only when `T` is `true`; otherwise a compile error. */\ntype AssertTrue<T extends true> = T\n\n// Reverse coverage: every table key must appear as a constant value.\n// Exported only so noUnusedLocals keeps these checks alive — never import.\nexport type _HostPushesCoveredByKeys = AssertTrue<\n\tkeyof HostPushes extends (typeof hostPushKeys)[keyof typeof hostPushKeys]\n\t\t? true\n\t\t: false\n>\nexport type _PluginRequestsCoveredByMethods = AssertTrue<\n\tkeyof PluginRequests extends (typeof pluginMethods)[keyof typeof pluginMethods]\n\t\t? true\n\t\t: false\n>\n\n/**\n * Type-safe host bridge. The runtime still serialises messages as plain\n * postMessage objects; this contract gives compile-time guarantees to callers.\n *\n * Per-method timeouts are declared on the protocol table entries\n * (`timeoutMs`) — the bridge reads them, callers never pass one.\n */\nexport type Host = {\n\trequest<K extends keyof PluginRequests>(\n\t\tmethod: K,\n\t\t...args: RequestInput<K> extends void ? [] : [RequestInput<K>]\n\t): Promise<RequestOutput<K>>\n\tsubscribe<K extends keyof HostPushes>(\n\t\tkey: K,\n\t\thandler: (data: HostPushes[K]) => void,\n\t): () => void\n\t/**\n\t * Internal — returns a Host whose requests are stamped with the given\n\t * resource scope (see {@link PluginRequest.resId}). Used by the runtime\n\t * to bind one API instance to the resource it was created for.\n\t */\n\twithScope: (resId: string) => Host\n}\n","import type {\n\tHost,\n\tHostMessage,\n\tHostPushes,\n\tPluginMessage,\n\tPluginRequests,\n\tPluginSubscribe,\n\tRequestInput,\n\tRequestOutput,\n} from \"./protocol.ts\"\nimport { PROTOCOL_VERSION, pluginRequestTimeouts } from \"./protocol.ts\"\n\n// ── Host bridge ──────────────────────────────────────────────────────────\n\nconst REQUEST_TIMEOUT_MS = 10_000\n\nlet nextId = 1\nlet hostBridge: Host | undefined\n\n/**\n * Narrow an unknown value to a plain record. Handy for decoding\n * plugin-defined payloads (e.g. anchor data) without assertion casts.\n */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value)\n}\n\nfunction isValidHostMessage(msg: unknown): msg is HostMessage {\n\treturn isRecord(msg) && \"type\" in msg\n}\n\n/**\n * Lazily create (and then reuse) the singleton postMessage bridge to the\n * host parent window: request/response with a 10s timeout plus push\n * subscriptions. Only the host window may drive the bridge — messages\n * from any other source are ignored. Called automatically by\n * {@link createIframeHostAPI}; you only need this when talking to the\n * host outside the plugin API surface.\n */\nexport function ensureHostBridge(): Host {\n\tif (hostBridge !== undefined) return hostBridge\n\n\tconst pending = new Map<\n\t\tnumber,\n\t\t{\n\t\t\tresolve(value: unknown): void\n\t\t\treject(reason: Error): void\n\t\t\ttimeoutId: ReturnType<typeof setTimeout>\n\t\t}\n\t>()\n\tconst subscribers = new Map<string, Set<(data: unknown) => void>>()\n\n\twindow.addEventListener(\n\t\t\"message\",\n\t\tfunction handleMessage(event: MessageEvent) {\n\t\t\t// Only the host (parent window) may drive this bridge; any\n\t\t\t// other window that obtains a reference to this frame must\n\t\t\t// not be able to inject fake responses or pushes.\n\t\t\t//\n\t\t\t// jsdom delivers same-window postMessage with `source: null`\n\t\t\t// (real browsers always carry the posting window). A null\n\t\t\t// source is only trusted when this frame IS its own parent —\n\t\t\t// the jsdom/test embedding — which cannot happen for a real\n\t\t\t// sandboxed iframe, where `window.parent !== window`.\n\t\t\tconst selfEmbedded = window.parent === window\n\t\t\tif (\n\t\t\t\tevent.source !== window.parent &&\n\t\t\t\t!(selfEmbedded && event.source === null)\n\t\t\t) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tconst msg = event.data\n\t\t\tif (!isValidHostMessage(msg)) return\n\n\t\t\tif (msg.type === \"response\") {\n\t\t\t\tconst entry = pending.get(msg.id)\n\t\t\t\tif (entry === undefined) return\n\t\t\t\tpending.delete(msg.id)\n\t\t\t\tclearTimeout(entry.timeoutId)\n\t\t\t\tif (msg.ok) {\n\t\t\t\t\tentry.resolve(msg.data)\n\t\t\t\t} else {\n\t\t\t\t\tconst err = new Error(msg.error ?? \"Unknown error\")\n\t\t\t\t\t// Preserve the machine-readable code (e.g. the asset\n\t\t\t\t\t// DENIED/UNAVAILABLE/POLICY vocabulary) so plugin code\n\t\t\t\t\t// can branch on `err.name` across the bridge.\n\t\t\t\t\tconst code = msg.errorCode ?? msg.errorName\n\t\t\t\t\tif (code !== undefined && code.length > 0) {\n\t\t\t\t\t\terr.name = code\n\t\t\t\t\t}\n\t\t\t\t\tentry.reject(err)\n\t\t\t\t}\n\t\t\t} else if (msg.type === \"push\") {\n\t\t\t\tconst handlers = subscribers.get(msg.key)\n\t\t\t\tif (handlers !== undefined) {\n\t\t\t\t\tfor (const handler of handlers) {\n\t\t\t\t\t\thandler(msg.data)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t)\n\n\tfunction request<K extends keyof PluginRequests>(\n\t\tresId: string | undefined,\n\t\tmethod: K,\n\t\t...args: RequestInput<K> extends void ? [] : [RequestInput<K>]\n\t): Promise<RequestOutput<K>> {\n\t\tconst id = nextId++\n\t\tconst input = args[0] as RequestInput<K> | undefined\n\t\t// Per-method timeouts are declared in the protocol meta\n\t\t// (`pluginRequestTimeouts`, e.g. downloads waiting on the consent\n\t\t// dialog) — callers never pass one.\n\t\tconst timeoutMs =\n\t\t\t(pluginRequestTimeouts as Partial<Record<string, number>>)[method] ??\n\t\t\tREQUEST_TIMEOUT_MS\n\t\tconst params = input\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timeoutId = setTimeout(() => {\n\t\t\t\tpending.delete(id)\n\t\t\t\treject(new Error(`Request timed out: ${String(method)}`))\n\t\t\t}, timeoutMs)\n\t\t\tpending.set(id, {\n\t\t\t\tresolve: resolve as (value: unknown) => void,\n\t\t\t\treject,\n\t\t\t\ttimeoutId,\n\t\t\t})\n\t\t\tconst message: PluginMessage = {\n\t\t\t\ttype: \"request\",\n\t\t\t\tid,\n\t\t\t\tmethod: method as string,\n\t\t\t\tparams,\n\t\t\t\tproto: PROTOCOL_VERSION,\n\t\t\t\tresId,\n\t\t\t}\n\t\t\twindow.parent.postMessage(message, \"*\")\n\t\t})\n\t}\n\n\tfunction subscribe<K extends keyof HostPushes>(\n\t\tkey: K,\n\t\thandler: (data: HostPushes[K]) => void,\n\t): () => void {\n\t\tconst keyString = key as string\n\t\tlet handlers = subscribers.get(keyString)\n\t\tif (handlers === undefined) {\n\t\t\thandlers = new Set()\n\t\t\tsubscribers.set(keyString, handlers)\n\t\t\tconst message: PluginSubscribe = {\n\t\t\t\ttype: \"subscribe\",\n\t\t\t\tkey: keyString,\n\t\t\t\tproto: PROTOCOL_VERSION,\n\t\t\t}\n\t\t\twindow.parent.postMessage(message, \"*\")\n\t\t}\n\t\tconst wrapped = (data: unknown) => handler(data as HostPushes[K])\n\t\thandlers.add(wrapped)\n\t\treturn function unsubscribe() {\n\t\t\thandlers!.delete(wrapped)\n\t\t\tif (handlers!.size === 0) {\n\t\t\t\tsubscribers.delete(keyString)\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction makeHost(resId: string | undefined): Host {\n\t\treturn {\n\t\t\trequest(method, ...args) {\n\t\t\t\treturn request(resId, method, ...args)\n\t\t\t},\n\t\t\tsubscribe,\n\t\t\twithScope: makeHost,\n\t\t}\n\t}\n\n\thostBridge = makeHost(undefined)\n\treturn hostBridge\n}\n","import type { Codec } from \"./types.ts\"\n\n/**\n * JSON codec covering the common case (`number`, `string`, `boolean`, plain\n * objects). Falls back to `undefined` on parse errors so callers see \"missing\"\n * instead of a corrupted value.\n */\nexport function jsonCodec<T>(): Codec<T> {\n\treturn {\n\t\tencode(value) {\n\t\t\treturn JSON.stringify(value)\n\t\t},\n\t\tdecode(raw) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(raw) as T\n\t\t\t} catch {\n\t\t\t\treturn undefined\n\t\t\t}\n\t\t},\n\t}\n}\n\n/**\n * Plain-string number codec. Avoids the `\"\"` → `0` ambiguity of\n * {@link jsonCodec} by treating any non-finite parse as missing.\n */\nexport function numberCodec(fallback?: number): Codec<number> {\n\treturn {\n\t\tencode(value) {\n\t\t\treturn String(value)\n\t\t},\n\t\tdecode(raw) {\n\t\t\tconst n = Number(raw)\n\t\t\treturn Number.isFinite(n) ? n : fallback\n\t\t},\n\t}\n}\n\n/**\n * Boolean codec using `\"1\"`/`\"0\"` for compact storage. Also accepts\n * `\"true\"`/`\"false\"` for interoperability.\n */\nexport function booleanCodec(): Codec<boolean> {\n\treturn {\n\t\tencode(value) {\n\t\t\treturn value ? \"1\" : \"0\"\n\t\t},\n\t\tdecode(raw) {\n\t\t\tif (raw === \"1\" || raw === \"true\") return true\n\t\t\tif (raw === \"0\" || raw === \"false\") return false\n\t\t\treturn undefined\n\t\t},\n\t}\n}\n","import { imageVariantQuery } from \"@hoardodile/sdk-types/image-variant\"\nimport type {\n\tFileUrlVariant,\n\tReactivePluginAPI,\n\tWebPluginAPI,\n} from \"./types.ts\"\n\n/**\n * Deep-partial override type for {@link createWebPluginAPI}: nested\n * objects and functions can be overridden selectively; functions may\n * also be replaced by `undefined` to drop the default no-op.\n */\nexport type DeepPartial<T> = {\n\t[K in keyof T]?: T[K] extends (...args: never[]) => unknown\n\t\t? T[K] | undefined\n\t\t: T[K] extends object\n\t\t\t? DeepPartial<T[K]>\n\t\t\t: T[K]\n}\n\n/** The complete plugin API shape stubbed by {@link createWebPluginAPI}. */\nexport type StubbedPluginAPI = WebPluginAPI & ReactivePluginAPI\n\nfunction mergeDeep<T extends object>(target: T, source: DeepPartial<T>): T {\n\tconst result = { ...target }\n\tfor (const key of Object.keys(source) as Array<keyof T>) {\n\t\tconst sourceValue = source[key]\n\t\tconst targetValue = result[key]\n\t\tif (\n\t\t\tsourceValue !== undefined &&\n\t\t\ttypeof sourceValue === \"object\" &&\n\t\t\t!Array.isArray(sourceValue) &&\n\t\t\ttargetValue !== undefined &&\n\t\t\ttypeof targetValue === \"object\" &&\n\t\t\t!Array.isArray(targetValue)\n\t\t) {\n\t\t\tresult[key] = mergeDeep(\n\t\t\t\ttargetValue as object,\n\t\t\t\tsourceValue as DeepPartial<object>,\n\t\t\t) as T[keyof T]\n\t\t} else if (sourceValue !== undefined) {\n\t\t\tresult[key] = sourceValue as T[keyof T]\n\t\t}\n\t}\n\treturn result\n}\n\n/**\n * Returns a minimal complete plugin API for render tests — the imperative\n * surface plus no-op reactive hooks. All fields return empty/loading/no-op\n * values; override via `overrides` to exercise plugin-specific code paths.\n *\n * Framework-agnostic counterpart of the React `createWebPluginAPI` in\n * `@hoardodile/sdk-react`: this one returns a plain object (no React\n * provider), that one wraps the same stub in a `StubPluginAPIProvider`\n * so component tests can render against it.\n */\nexport function createWebPluginAPI(\n\toverrides?: DeepPartial<StubbedPluginAPI>,\n): StubbedPluginAPI {\n\tconst base: StubbedPluginAPI = {\n\t\tlogInfo: () => {},\n\t\tlogWarn: () => {},\n\t\tlogError: () => {},\n\t\tresource: {\n\t\t\tid: \"r-test\",\n\t\t\tname: \"test\",\n\t\t\tsourceMeta: undefined,\n\t\t\tsearchMeta: undefined,\n\t\t\tfileStats: undefined,\n\t\t\tcontentPluginId: \"p-test\",\n\t\t},\n\t\tlistFiles: async () => [],\n\t\treadFile: async () => new ArrayBuffer(0),\n\t\tresolveFileUrl: (filename, variant) => buildMockFileUrl(filename, variant),\n\t\tresolveExtractedUrl: (path) => `/extracted/${path}`,\n\t\textractProgressUrl: () => \"/extract-progress/\",\n\t\tresolveBaseUrl: () => \"/files/\",\n\t\tresolveFrameUrl: (filename, timeMs) => `/frame/${filename}/${timeMs}`,\n\t\tdownload: async () => {\n\t\t\tthrow new Error(\"download stub not overridden\")\n\t\t},\n\t\tresolveAssetUrl: (path) => `/plugin-assets/p-test/./${path}`,\n\t\tdeleteAsset: async () => ({ existed: false }),\n\t\tlistMessages: async () => [],\n\t\tcreateMessage: async () => {\n\t\t\tthrow new Error(\"createMessage stub not overridden\")\n\t\t},\n\t\tlistDanmaku: async () => [],\n\t\tcreateDanmaku: async () => {\n\t\t\tthrow new Error(\"createDanmaku stub not overridden\")\n\t\t},\n\t\tgetPref: () => undefined,\n\t\tsetPref: () => {},\n\t\tgetCache: () => undefined,\n\t\tsetCache: () => {},\n\t\tlistCache: () => [],\n\t\tinvalidate: async () => {},\n\t\tonAnchorJump: () => () => {},\n\t\tuseFileList: () => ({\n\t\t\tdata: [],\n\t\t\tisLoading: false,\n\t\t\tisError: false,\n\t\t\terror: null,\n\t\t}),\n\t\tuseMessageList: () => ({\n\t\t\tdata: [],\n\t\t\tisLoading: false,\n\t\t\tisError: false,\n\t\t\terror: null,\n\t\t}),\n\t\tuseCreateMessage: () => ({\n\t\t\tmutate: async () => {\n\t\t\t\tthrow new Error(\"useCreateMessage stub not overridden\")\n\t\t\t},\n\t\t\tisPending: false,\n\t\t}),\n\t\tuseDanmakuList: () => ({\n\t\t\tdata: [],\n\t\t\tisLoading: false,\n\t\t\tisError: false,\n\t\t\terror: null,\n\t\t}),\n\t\tuseCreateDanmaku: () => ({\n\t\t\tmutate: async () => {\n\t\t\t\tthrow new Error(\"useCreateDanmaku stub not overridden\")\n\t\t\t},\n\t\t\tisPending: false,\n\t\t}),\n\t\tusePref: (_key, defaultValue, _codec) => [defaultValue, (_next) => {}],\n\t\tuseTheme: () => ({\n\t\t\tresolvedTheme: \"light\",\n\t\t\tpalette: \"mono\",\n\t\t\ticonStyle: \"duotone\",\n\t\t}),\n\t\tuseFont: () => ({ family: \"\", cssPaths: [] }),\n\t}\n\treturn overrides === undefined ? base : mergeDeep(base, overrides)\n}\n\n/**\n * Mock {@link WebPluginAPI.resolveFileUrl} shape: the same query the\n * real runtime emits for each variant (without the resId/token prefix).\n */\nfunction buildMockFileUrl(filename: string, variant?: FileUrlVariant): string {\n\tconst url = `/files/${filename}`\n\tif (variant === \"preview\") {\n\t\treturn `${url}?size=preview`\n\t}\n\tif (variant !== undefined && variant !== \"original\") {\n\t\treturn `${url}?${imageVariantQuery(variant)}`\n\t}\n\treturn url\n}\n","import type { PluginContextPainted, PluginIframeContext } from \"./protocol.ts\"\nimport { hostPushKeys, PROTOCOL_VERSION } from \"./protocol.ts\"\n\nlet pluginContext: PluginIframeContext | undefined\n\n/** The last context pushed by the host, if any. */\nexport function getPluginContext(): PluginIframeContext | undefined {\n\treturn pluginContext\n}\n\nfunction setPluginContext(ctx: PluginIframeContext): void {\n\tpluginContext = ctx\n}\n\n// ── Visibility (framework-agnostic store) ────────────────────────────────\n\nlet currentVisibility = true\nconst visibilityListeners = new Set<(visible: boolean) => void>()\n\n/**\n * Subscribe to iframe visibility changes (tab hidden, iframe released,\n * overlays opened by the host). The callback receives the current\n * visibility; returns an unsubscribe function. Backs `useVisibility`\n * in `@hoardodile/sdk-react`.\n */\nexport function subscribeToVisibility(\n\tcb: (visible: boolean) => void,\n): () => void {\n\tvisibilityListeners.add(cb)\n\treturn () => {\n\t\tvisibilityListeners.delete(cb)\n\t}\n}\n\n/** Current visibility snapshot without subscribing. */\nexport function getVisibilitySnapshot(): boolean {\n\treturn currentVisibility\n}\n\nfunction publishVisibilityChange(visible: boolean): void {\n\tif (currentVisibility === visible) return\n\tcurrentVisibility = visible\n\tfor (const cb of visibilityListeners) {\n\t\tcb(visible)\n\t}\n}\n\n// ── Mount lifecycle ──────────────────────────────────────────────────────\n\n/**\n * Invokes `cb` once the current DOM state has painted: the first rAF\n * frame paints the pending tree, the second guarantees the compositor\n * surface update landed. If rAF is throttled (document not rendering)\n * `cb` never fires — callers need their own fallback.\n */\nfunction afterNextPaintedFrame(cb: () => void): void {\n\trequestAnimationFrame(() => {\n\t\trequestAnimationFrame(cb)\n\t})\n}\n\n/**\n * Sets up listeners for host→plugin communication via `postMessage` and\n * `CustomEvent` fallback. The host pushes context and visibility updates;\n * this function invokes `mount(ctx)` whenever a new context arrives.\n *\n * Lifecycle contract: the host may replace the context at any time — a\n * pooled iframe document is reused across resources without a reload — so\n * `mount` runs once per context, not once per page load. Clean up\n * per-resource state yourself, or rely on `createPluginRoot` from\n * `@hoardodile/sdk-react`, which remounts the tree by `resId` by\n * default. When the host rebinds the iframe to a new resource, a late\n * unmount cache flush stamped with the old resId is stale-dropped by the\n * host (the plugin's debounced write still lands via the new binding).\n */\nexport function mountPlugin(mount: (ctx: PluginIframeContext) => void): void {\n\tfunction applyContext(ctx: PluginIframeContext) {\n\t\tsetPluginContext(ctx)\n\t\t// A fresh context means the host is showing this iframe again.\n\t\t// Publish rather than assign so plugins that keep their tree\n\t\t// mounted across contexts (remountOnResourceChange: false) wake\n\t\t// up after a release pushed visible:false; the publish helper\n\t\t// already dedupes no-change transitions.\n\t\tpublishVisibilityChange(true)\n\t\tmount(ctx)\n\t\t// Acknowledge only once the new tree has painted into the iframe's\n\t\t// compositor surface: the host keeps a freshly claimed iframe\n\t\t// transparent (but painted) until this ack arrives, and a bare DOM\n\t\t// commit would still replay the previous resource's last frame for\n\t\t// a frame or two while the new raster is in flight. If rAF is\n\t\t// throttled the ack never fires and the host's fallback takes over.\n\t\tafterNextPaintedFrame(() => {\n\t\t\twindow.parent.postMessage(\n\t\t\t\t{\n\t\t\t\t\ttype: \"contextPainted\",\n\t\t\t\t\tresId: ctx.resId,\n\t\t\t\t\tproto: PROTOCOL_VERSION,\n\t\t\t\t} satisfies PluginContextPainted,\n\t\t\t\t\"*\",\n\t\t\t)\n\t\t})\n\t}\n\n\twindow.addEventListener(\"message\", (event: MessageEvent) => {\n\t\t// Only trust context/visibility pushes from the host parent window.\n\t\tif (event.source !== window.parent) return\n\t\tconst msg = event.data\n\t\tif (!isRecord(msg) || msg.type !== \"push\") return\n\t\tif (msg.key === hostPushKeys.context) {\n\t\t\tapplyContext(msg.data as PluginIframeContext)\n\t\t} else if (msg.key === hostPushKeys.visibility) {\n\t\t\tpublishVisibilityChange((msg.data as { visible: boolean }).visible)\n\t\t}\n\t})\n\n\twindow.addEventListener(\"context-ready\", (e: Event) => {\n\t\tapplyContext((e as CustomEvent<PluginIframeContext>).detail)\n\t})\n\twindow.addEventListener(\"visibility-changed\", (e: Event) => {\n\t\tpublishVisibilityChange(\n\t\t\t(e as CustomEvent<{ visible: boolean }>).detail.visible,\n\t\t)\n\t})\n\n\tconst w = window as unknown as Record<string, unknown>\n\tif (w.__pluginContext !== undefined) {\n\t\tapplyContext(w.__pluginContext as PluginIframeContext)\n\t}\n\tif (w.__pluginVisibility !== undefined) {\n\t\tpublishVisibilityChange(\n\t\t\t(w.__pluginVisibility as { visible: boolean }).visible,\n\t\t)\n\t}\n}\n\n// ── Theme & font application ─────────────────────────────────────────────\n\n/** Applies theme classes to `document.documentElement` so CSS variables update. */\nexport function applyTheme(\n\tresolvedTheme: string,\n\tpalette: string,\n\ticonStyle: string,\n): void {\n\tconst root = document.documentElement\n\troot.classList.remove(\"light\", \"dark\")\n\troot.classList.add(resolvedTheme)\n\t// Strip every palette class generically — no per-palette list to keep in\n\t// sync, and stale classes from older versions get cleaned up too.\n\tfor (const cls of [...root.classList]) {\n\t\tif (cls.startsWith(\"theme-\")) root.classList.remove(cls)\n\t}\n\tif (palette !== \"mono\") {\n\t\troot.classList.add(`theme-${palette}`)\n\t}\n\t// `data-icon-style` drives the grayscale rule in `@hoardodile/ui/theme.css`;\n\t// `linear` only matters to the host's own icon registry.\n\troot.dataset.iconStyle = iconStyle\n\t// The plugin document's canvas follows the theme's color-scheme. Without\n\t// this the html/body stay transparent but the *canvas* paints the\n\t// browser default (white) even in dark mode, so a transparent plugin\n\t// (e.g. the media gallery) occludes the host's themed surface. Syncing\n\t// color-scheme makes the canvas match `resolvedTheme`.\n\troot.style.colorScheme = resolvedTheme\n}\n\n// The plugin bundle's theme.css only defines a `--font-sans` fallback;\n// nothing in it consumes `--font-app`, so the SDK installs this rule once\n// to make the document actually pick up the inherited font.\nconst FONT_STYLE_ID = \"plugin-host-font\"\n\n/**\n * Applies the host app font to the iframe document: injects each preset\n * stylesheet once (idempotent per path — they are absolute `/fonts/...`\n * URLs statically served by the host, which the sandboxed iframe can\n * load), then points `--font-app` at the family stack. An empty family\n * means the plugin opted out of font inheritance: the variable is\n * removed and the document falls back to the plugin's own `--font-sans`.\n */\nexport function applyFonts(family: string, cssPaths: readonly string[]): void {\n\tfor (const path of cssPaths) {\n\t\tconst selector = `link[rel=\"stylesheet\"][href=\"${path}\"]`\n\t\tif (document.head.querySelector(selector) !== null) continue\n\t\tconst link = document.createElement(\"link\")\n\t\tlink.rel = \"stylesheet\"\n\t\tlink.href = path\n\t\tdocument.head.appendChild(link)\n\t}\n\n\tconst root = document.documentElement\n\tif (family === \"\") {\n\t\troot.style.removeProperty(\"--font-app\")\n\t} else {\n\t\troot.style.setProperty(\"--font-app\", family)\n\t}\n\n\tif (document.getElementById(FONT_STYLE_ID) === null) {\n\t\tconst style = document.createElement(\"style\")\n\t\tstyle.id = FONT_STYLE_ID\n\t\tstyle.textContent = \"html{font-family:var(--font-app,var(--font-sans))}\"\n\t\tdocument.head.appendChild(style)\n\t}\n}\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value)\n}\n","import type { PluginIframeContext } from \"./protocol.ts\"\n\n/**\n * In-memory module-level stores backing the plugin's pref/cache APIs\n * (`api.getPref`/`setPref`, `api.getCache`/`setCache`). Seeded from the\n * iframe context on mount; prefs mirror the host's plugin-wide settings,\n * cache is per-resource state that the host persists (debounced) and\n * restores on the next visit.\n */\nconst pluginPrefStore = new Map<string, string>()\nconst pluginCacheStore = new Map<string, string>()\n\n/**\n * Reset both stores and seed them from the iframe context. Called once\n * by the runtime on mount; must run before any pref/cache access.\n */\nexport function seedPluginStores(ctx: PluginIframeContext): void {\n\tpluginPrefStore.clear()\n\tfor (const [k, v] of Object.entries(ctx.initialPrefs)) {\n\t\tpluginPrefStore.set(k, v)\n\t}\n\tpluginCacheStore.clear()\n\tfor (const [k, v] of Object.entries(ctx.initialCache)) {\n\t\tpluginCacheStore.set(k, v)\n\t}\n}\n\n/** Read-only view of the plugin's in-memory pref store. */\nexport function getPluginPrefStore(): ReadonlyMap<string, string> {\n\treturn pluginPrefStore\n}\n\n/** Write a pref locally; mirror it to the host immediately. */\nexport function setPluginPref(key: string, value: string): void {\n\tpluginPrefStore.set(key, value)\n}\n\n/** Read-only view of the plugin's in-memory cache store. */\nexport function getPluginCacheStore(): ReadonlyMap<string, string> {\n\treturn pluginCacheStore\n}\n\n/**\n * Write a cache entry locally and mirror it to the host for persistence.\n * Continuously-changing state (scroll positions, resume timestamps)\n * should go through the debounced `useCacheWriter` in\n * `@hoardodile/sdk-react` instead of calling this directly on every\n * change.\n */\nexport function setPluginCache(key: string, value: string): void {\n\tpluginCacheStore.set(key, value)\n}\n\n/**\n * Snapshot of all cache entries, as returned by `api.listCache`. Values\n * are the raw serialized strings stored via {@link setPluginCache}.\n */\nexport function snapshotCacheEntries(): {\n\treadonly key: string\n\treadonly value: string\n}[] {\n\tconst result: { readonly key: string; readonly value: string }[] = []\n\tfor (const [key, value] of pluginCacheStore) {\n\t\tresult.push({ key, value })\n\t}\n\treturn result\n}\n\n// ── Pref change pub/sub ──────────────────────────────────────────────────\n\nconst prefChangeListeners = new Map<string, Set<() => void>>()\n\n/**\n * Subscribe to pref changes for `key` (both local writes and host-pushed\n * updates). Returns an unsubscribe function. Backs the reactive\n * `usePref` hook in `@hoardodile/sdk-react`.\n */\nexport function subscribeToPrefChanges(\n\tkey: string,\n\tcb: () => void,\n): () => void {\n\tlet listeners = prefChangeListeners.get(key)\n\tif (listeners === undefined) {\n\t\tlisteners = new Set()\n\t\tprefChangeListeners.set(key, listeners)\n\t}\n\tlisteners.add(cb)\n\treturn function unsubscribe() {\n\t\tlisteners!.delete(cb)\n\t\tif (listeners!.size === 0) {\n\t\t\tprefChangeListeners.delete(key)\n\t\t}\n\t}\n}\n\n/** Notify all subscribers of `key` about a value change. */\nexport function broadcastPrefChange(key: string): void {\n\tconst listeners = prefChangeListeners.get(key)\n\tif (listeners === undefined) return\n\tfor (const cb of listeners) {\n\t\tcb()\n\t}\n}\n","import { imageVariantQuery } from \"@hoardodile/sdk-types/image-variant\"\nimport type { FileUrlVariant } from \"./types.ts\"\n\n// ── File URL resolution ──────────────────────────────────────────────────\n// Pure string builders: the iframe's null origin cannot send cookies, so\n// the host embeds a short-lived token in the path (see\n// apps/server/src/infra/http/plugin.ts). Kept side-effect free so the\n// wire shape is unit-testable without the host bridge.\n\nexport function resolveFilesBaseUrl(resId: string, token: string): string {\n\treturn `/api/resources/${resId}/files/${encodeURIComponent(token)}/`\n}\n\n/**\n * Build the URL for a resource file. Without `variant` (or with\n * `\"original\"`) the original bytes are addressed; `\"preview\"` selects\n * the default preview variant; an {@link ImageVariantSpec} requests a\n * custom derived image. Derived URLs always carry `size=preview` (the\n * compatibility alias) alongside the explicit variant parameters, so a\n * server that predates the generic contract degrades to its default\n * preview instead of silently serving the original.\n */\nexport function buildFileUrl(\n\tresId: string,\n\tfilename: string,\n\ttoken: string,\n\tvariant?: FileUrlVariant,\n): string {\n\tconst url = `/api/resources/${resId}/files/${encodeURIComponent(token)}/${encodeURIComponent(filename)}`\n\tif (variant === \"preview\") {\n\t\treturn `${url}?size=preview`\n\t}\n\tif (variant !== undefined && variant !== \"original\") {\n\t\treturn `${url}?${imageVariantQuery(variant)}`\n\t}\n\treturn url\n}\n\n/** Build the URL for a video frame thumbnail at `timeMs`. */\nexport function buildFrameUrl(\n\tresId: string,\n\tfilename: string,\n\ttimeMs: number,\n\ttoken: string,\n): string {\n\tconst time = String(Math.max(0, Math.round(timeMs)))\n\treturn `/api/resources/${resId}/frame/${encodeURIComponent(token)}/${encodeURIComponent(filename)}/${time}`\n}\n\n/**\n * Build the URL of a file in the plugin's own asset vault. The host\n * serves it via the tokenized `/api/plugin-assets/:id/:token/:path`\n * route (see `apps/server/src/infra/http/plugin-assets.ts`): `token` is\n * the plugin-scoped asset token from the iframe context — vault files\n * are host data, so they are served fresh (`no-cache`), never through\n * the service worker.\n */\nexport function buildAssetUrl(\n\tpluginId: string,\n\tpath: string,\n\ttoken: string,\n): string {\n\treturn `/api/plugin-assets/${encodeURIComponent(pluginId)}/${encodeURIComponent(\n\t\ttoken,\n\t)}/${encodeURIComponent(path)}`\n}\n","import type {\n\tAnchorData,\n\tDanmaku,\n\tDanmakuListFilter,\n\tDanmakuMode,\n\tMessage,\n\tPluginAssetDeleteResult,\n\tPluginDownloadRequest,\n\tPluginDownloadResult,\n\tPluginSchema,\n} from \"@hoardodile/sdk-types\"\nimport { ensureHostBridge } from \"./bridge.ts\"\nimport type {\n\tInvalidateTarget,\n\tPluginFonts,\n\tPluginIframeContext,\n\tReadFileRange,\n} from \"./protocol.ts\"\nimport {\n\tbroadcastPrefChange,\n\tgetPluginCacheStore,\n\tgetPluginPrefStore,\n\tseedPluginStores,\n\tsetPluginCache,\n\tsetPluginPref,\n\tsnapshotCacheEntries,\n} from \"./stores.ts\"\nimport type { FileUrlVariant, WebPluginAPI } from \"./types.ts\"\nimport {\n\tbuildAssetUrl,\n\tbuildFileUrl,\n\tbuildFrameUrl,\n\tresolveFilesBaseUrl,\n} from \"./urls.ts\"\n\n// ── Pure helpers ─────────────────────────────────────────────────────────\n\n/**\n * Narrow an unknown value to a plain record. Handy for decoding\n * plugin-defined payloads (e.g. anchor data) without assertion casts.\n */\nexport function isRecord(value: unknown): value is Record<string, unknown> {\n\treturn value !== null && typeof value === \"object\" && !Array.isArray(value)\n}\n\n/**\n * Extract `{ resolvedTheme, palette, iconStyle }` from an unknown host\n * payload. Malformed input yields `undefined` fields rather than throwing —\n * theme pushes are best-effort.\n */\nexport function extractThemePayload(data: unknown): {\n\tresolvedTheme: string | undefined\n\tpalette: string | undefined\n\ticonStyle: string | undefined\n} {\n\tif (!isRecord(data)) {\n\t\treturn {\n\t\t\tresolvedTheme: undefined,\n\t\t\tpalette: undefined,\n\t\t\ticonStyle: undefined,\n\t\t}\n\t}\n\treturn {\n\t\tresolvedTheme:\n\t\t\ttypeof data.resolvedTheme === \"string\" ? data.resolvedTheme : undefined,\n\t\tpalette: typeof data.palette === \"string\" ? data.palette : undefined,\n\t\ticonStyle: typeof data.iconStyle === \"string\" ? data.iconStyle : undefined,\n\t}\n}\n\n/**\n * Extract the host app font payload (`family` + stylesheet paths) from\n * an unknown host push; `undefined` when the shape does not match.\n */\nexport function extractFontsPayload(data: unknown): PluginFonts | undefined {\n\tif (!isRecord(data) || typeof data.family !== \"string\") return undefined\n\tconst cssPaths = Array.isArray(data.cssPaths)\n\t\t? data.cssPaths.filter((p): p is string => typeof p === \"string\")\n\t\t: []\n\treturn { family: data.family, cssPaths }\n}\n\n/**\n * Extract a `{ key, value }` pref update from an unknown host push;\n * `undefined` when the shape does not match. `value` may be `undefined`\n * for a removal.\n */\nexport function extractPrefPayload(\n\tdata: unknown,\n): { readonly key: string; readonly value: string | undefined } | undefined {\n\tif (!isRecord(data)) return undefined\n\tconst key = data.key\n\tif (typeof key !== \"string\") return undefined\n\treturn {\n\t\tkey,\n\t\tvalue: typeof data.value === \"string\" ? data.value : undefined,\n\t}\n}\n\n// ── Public API ───────────────────────────────────────────────────────────\n\n/**\n * Builds the full {@link WebPluginAPI} for a plugin running inside a sandboxed\n * iframe. Communicates with the host via postMessage.\n */\nexport function createIframeHostAPI<\n\tTSchema extends PluginSchema = PluginSchema,\n>(ctx: PluginIframeContext): WebPluginAPI<TSchema> {\n\tconst host = ensureHostBridge().withScope(ctx.resId)\n\tseedPluginStores(ctx)\n\n\tfunction logInfo(message: string, data?: Record<string, unknown>): void {\n\t\thost.request(\"logInfo\", { message, data }).catch(() => {})\n\t}\n\tfunction logWarn(message: string, data?: Record<string, unknown>): void {\n\t\thost.request(\"logWarn\", { message, data }).catch(() => {})\n\t}\n\tfunction logError(message: string, data?: Record<string, unknown>): void {\n\t\thost.request(\"logError\", { message, data }).catch(() => {})\n\t}\n\n\tfunction listFiles(): Promise<readonly TSchema[\"file\"][]> {\n\t\treturn host.request(\"listFiles\") as Promise<readonly TSchema[\"file\"][]>\n\t}\n\n\tfunction readFile(path: string, range?: ReadFileRange): Promise<ArrayBuffer> {\n\t\treturn host.request(\"readFile\", { path, range })\n\t}\n\n\tfunction resolveFileUrl(filename: string, variant?: FileUrlVariant): string {\n\t\treturn buildFileUrl(ctx.resId, filename, ctx.fileToken, variant)\n\t}\n\n\tfunction resolveExtractedUrl(path: string): string {\n\t\treturn `/api/resources/${ctx.resId}/extracted/${encodeURIComponent(\n\t\t\tctx.fileToken,\n\t\t)}/${encodeURIComponent(path)}`\n\t}\n\n\tfunction extractProgressUrl(): string {\n\t\t// The trailing slash carries the token segment the auth preHandler\n\t\t// strips (see apps/server/src/infra/http/plugin.ts).\n\t\treturn `/api/resources/${ctx.resId}/extract-progress/${encodeURIComponent(\n\t\t\tctx.fileToken,\n\t\t)}/`\n\t}\n\n\tfunction resolveBaseUrl(): string {\n\t\treturn resolveFilesBaseUrl(ctx.resId, ctx.fileToken)\n\t}\n\n\tfunction resolveFrameUrl(filename: string, timeMs: number): string {\n\t\treturn buildFrameUrl(ctx.resId, filename, timeMs, ctx.fileToken)\n\t}\n\n\tfunction listMessages(): Promise<readonly Message[]> {\n\t\treturn host.request(\"listMessages\")\n\t}\n\n\tfunction createMessage(input: {\n\t\treadonly body: string\n\t\treadonly anchor?: unknown\n\t}): Promise<Message> {\n\t\t// The plugin deals in raw location data; the wire anchor is the\n\t\t// `{ data }` envelope (the resource id is host state).\n\t\treturn host.request(\"createMessage\", {\n\t\t\tbody: input.body,\n\t\t\tanchor: input.anchor === undefined ? undefined : { data: input.anchor },\n\t\t})\n\t}\n\n\tfunction listDanmaku(\n\t\tfilter?: DanmakuListFilter,\n\t): Promise<readonly Danmaku[]> {\n\t\treturn host.request(\"listDanmaku\", { filter })\n\t}\n\n\tfunction createDanmaku(input: {\n\t\treadonly text: string\n\t\treadonly anchor: unknown\n\t\treadonly mode?: DanmakuMode\n\t}): Promise<Danmaku> {\n\t\treturn host.request(\"createDanmaku\", {\n\t\t\ttext: input.text,\n\t\t\tanchor: { data: input.anchor },\n\t\t\tmode: input.mode,\n\t\t})\n\t}\n\n\tfunction getPref(key: string): string | undefined {\n\t\treturn getPluginPrefStore().get(key) ?? undefined\n\t}\n\n\tfunction setPref(key: string, value: string): void {\n\t\tsetPluginPref(key, value)\n\t\tbroadcastPrefChange(key)\n\t\thost.request(\"setPref\", { key, value }).catch(() => {})\n\t}\n\n\tfunction getCache(key: string): string | undefined {\n\t\treturn getPluginCacheStore().get(key) ?? undefined\n\t}\n\n\tfunction setCache(key: string, value: string): void {\n\t\tsetPluginCache(key, value)\n\t\thost.request(\"setCache\", { key, value }).catch(() => {})\n\t}\n\n\tfunction listCache(): readonly {\n\t\treadonly key: string\n\t\treadonly value: string\n\t}[] {\n\t\treturn snapshotCacheEntries()\n\t}\n\n\tfunction invalidate(target: InvalidateTarget): Promise<void> {\n\t\treturn host.request(\"invalidate\", { target })\n\t}\n\n\tfunction download(\n\t\trequest: PluginDownloadRequest,\n\t): Promise<PluginDownloadResult>\n\tfunction download(\n\t\trequests: readonly PluginDownloadRequest[],\n\t): Promise<readonly PluginDownloadResult[]>\n\tfunction download(\n\t\trequest: PluginDownloadRequest | readonly PluginDownloadRequest[],\n\t): Promise<PluginDownloadResult | readonly PluginDownloadResult[]> {\n\t\t// The protocol table declares the 5-minute ceiling (consent dialog\n\t\t// + transfer) — the bridge reads it; no per-call override here.\n\t\t// The host answers with the array shape on the wire; a single call\n\t\t// unwraps its one result, so the plugin sees the shape it sent.\n\t\treturn host.request(\"download\", request).then((result) => {\n\t\t\tif (Array.isArray(request)) {\n\t\t\t\treturn result as readonly PluginDownloadResult[]\n\t\t\t}\n\t\t\treturn (result as readonly PluginDownloadResult[])[0]!\n\t\t})\n\t}\n\n\tfunction resolveAssetUrl(path: string): string {\n\t\tif (ctx.assetToken.length === 0) {\n\t\t\tthrow new Error(\n\t\t\t\t'resolveAssetUrl() — the plugin has no asset token: declare \"download\": true in the manifest and reload the preview',\n\t\t\t)\n\t\t}\n\t\treturn buildAssetUrl(ctx.pluginId, path, ctx.assetToken)\n\t}\n\n\tfunction deleteAsset(path: string): Promise<PluginAssetDeleteResult> {\n\t\treturn host.request(\"deleteAsset\", { path })\n\t}\n\n\tfunction onAnchorJump(cb: (anchor: AnchorData) => void): () => void {\n\t\treturn host.subscribe(\"anchorJump\", cb)\n\t}\n\n\treturn {\n\t\tlogInfo,\n\t\tlogWarn,\n\t\tlogError,\n\t\tresource: {\n\t\t\tid: ctx.resId,\n\t\t\tname: ctx.resName,\n\t\t\tsourceMeta: ctx.sourceMeta as TSchema[\"sourceMeta\"],\n\t\t\tsearchMeta: ctx.searchMeta as TSchema[\"searchMeta\"],\n\t\t\tfileStats: ctx.fileStats,\n\t\t\tcontentPluginId: ctx.contentPluginId,\n\t\t},\n\t\tlistFiles,\n\t\treadFile,\n\t\tresolveFileUrl,\n\t\tresolveExtractedUrl,\n\t\textractProgressUrl,\n\t\tresolveBaseUrl,\n\t\tresolveFrameUrl,\n\t\tdownload,\n\t\tresolveAssetUrl,\n\t\tdeleteAsset,\n\t\tlistMessages,\n\t\tcreateMessage,\n\t\tlistDanmaku,\n\t\tcreateDanmaku,\n\t\tgetPref,\n\t\tsetPref,\n\t\tgetCache,\n\t\tsetCache,\n\t\tlistCache,\n\t\tinvalidate,\n\t\tonAnchorJump,\n\t} satisfies WebPluginAPI<TSchema>\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hoardodile/sdk-web",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "license": "MIT",
5
5
  "description": "Pure-browser iframe runtime for hoardodile content plugins.",
6
6
  "keywords": [
@@ -35,7 +35,7 @@
35
35
  "access": "public"
36
36
  },
37
37
  "dependencies": {
38
- "@hoardodile/sdk-types": "0.1.2"
38
+ "@hoardodile/sdk-types": "0.1.4"
39
39
  },
40
40
  "devDependencies": {
41
41
  "tsup": "^8.5.1",
package/src/index.ts CHANGED
@@ -62,6 +62,7 @@ export {
62
62
  invalidatePushKeys,
63
63
  PROTOCOL_VERSION,
64
64
  pluginMethods,
65
+ pluginRequestTimeouts,
65
66
  pluginThemePalettes,
66
67
  } from "./protocol.ts"
67
68
  export {
package/src/lifecycle.ts CHANGED
@@ -155,6 +155,12 @@ export function applyTheme(
155
155
  // `data-icon-style` drives the grayscale rule in `@hoardodile/ui/theme.css`;
156
156
  // `linear` only matters to the host's own icon registry.
157
157
  root.dataset.iconStyle = iconStyle
158
+ // The plugin document's canvas follows the theme's color-scheme. Without
159
+ // this the html/body stay transparent but the *canvas* paints the
160
+ // browser default (white) even in dark mode, so a transparent plugin
161
+ // (e.g. the media gallery) occludes the host's themed surface. Syncing
162
+ // color-scheme makes the canvas match `resolvedTheme`.
163
+ root.style.colorScheme = resolvedTheme
158
164
  }
159
165
 
160
166
  // The plugin bundle's theme.css only defines a `--font-sans` fallback;