{"version":3,"file":"index.cjs","names":[],"sources":["../../src/shared/protocol.ts","../../src/preload/index.ts"],"sourcesContent":["/**\n * Deck framework wire protocol —— main ↔ webview 之间的 channel 名 + 帧\n * 形态。SoT 在此，preload / client / main runtime 都从这里 import。\n *\n * 设计：把 declared `hostServices` / `simulatorApis` / `events` 三类全部走\n * **两个** 统一 channel，避免 channel name 爆炸 / 难以加 senderPolicy 白名单。\n * - `__electron-deck:invoke`  — webview → main RPC (ipcRenderer.invoke)\n * - `__electron-deck:event`   — main → webview event push (webContents.send)\n * - `__electron-deck:probe`   — webview → main 探活，bridge ready 检查\n *\n * 帧用 JSON 对象一层 envelope，便于扩展 + 校验。\n *\n * @internal\n */\n\nimport type { JsonValue } from '../types.js'\n\n/** Bridge global 默认挂的全局名（contextBridge.exposeInMainWorld） */\nexport const DEFAULT_BRIDGE_GLOBAL = '__electronDeckBridge'\n\n/**\n * Slot-token LAYOUT bridge 默认挂的全局名。`exposeDeckLayoutBridge()` 默认暴露到\n * 此名，renderer 的 `createDeckLayoutClient({ bridge: window.__electronDeckLayoutBridge })`\n * 读取同名。单一来源避免 preload helper 与 client 之间字符串漂移。\n */\nexport const DEFAULT_LAYOUT_BRIDGE_GLOBAL = '__electronDeckLayoutBridge'\n\n/** Bridge protocol semver；client 在 ready() 时校验 major 一致 */\nexport const BRIDGE_PROTOCOL_VERSION = '1.0.0'\n\nexport const DeckChannel = {\n\tInvoke: '__electron-deck:invoke',\n\tEvent: '__electron-deck:event',\n\tProbe: '__electron-deck:probe',\n\tSnapshot: '__electron-deck:snapshot',\n\tSlotGrant: '__electron-deck:slot-grant',\n\tLayoutSubscribe: '__electron-deck:layout-subscribe',\n} as const\n\nexport type InvokeKind = 'host' | 'simulator'\n\nexport interface InvokeRequest {\n\treadonly kind: InvokeKind\n\treadonly name: string\n\treadonly args: readonly JsonValue[]\n}\n\nexport interface InvokeSuccess<R extends JsonValue = JsonValue> {\n\treadonly ok: true\n\treadonly result: R\n}\n\nexport interface InvokeFailure {\n\treadonly ok: false\n\treadonly error: {\n\t\treadonly remoteName: string\n\t\treadonly message: string\n\t\treadonly code?: string\n\t}\n}\n\nexport type InvokeResponse<R extends JsonValue = JsonValue> =\n\t| InvokeSuccess<R>\n\t| InvokeFailure\n\nexport interface EventEnvelope<P extends JsonValue = JsonValue> {\n\treadonly name: string\n\treadonly payload: P\n}\n\nexport interface ProbeResponse {\n\treadonly ready: true\n\treadonly version: typeof BRIDGE_PROTOCOL_VERSION\n}\n\n/**\n * Bridge global 暴露到 webview window 的 shape。preload 把它通过\n * `contextBridge.exposeInMainWorld(globalName, bridge)` 注入；webview-side\n * `createDeckClient()` 通过 `globalThis[globalName]` 读取。\n *\n * 注意所有方法必须是 contextBridge-friendly（plain values + serializable\n * arguments），不要把 Map / Set / Date / Promise.race 等 leak 进 bridge 接口。\n */\nexport interface DeckBridge {\n\treadonly version: typeof BRIDGE_PROTOCOL_VERSION\n\tprobe(): Promise<ProbeResponse>\n\tinvoke(req: InvokeRequest): Promise<InvokeResponse>\n\t/** 订阅 event channel；返回 unsubscribe 函数（不是 Disposable，因为要跨 contextBridge） */\n\tonEvent(listener: (env: EventEnvelope) => void): () => void\n}\n","import { contextBridge, ipcRenderer } from 'electron'\nimport {\n\tBRIDGE_PROTOCOL_VERSION,\n\tDEFAULT_BRIDGE_GLOBAL,\n\tDEFAULT_LAYOUT_BRIDGE_GLOBAL,\n\tDeckChannel,\n} from '../shared/protocol.js'\nimport type {\n\tEventEnvelope,\n\tInvokeRequest,\n\tInvokeResponse,\n\tProbeResponse,\n\tDeckBridge,\n} from '../shared/protocol.js'\nimport type { LayoutBridge, SlotGrant } from '../client/layout-client.js'\n\nexport interface ExposeBridgeOptions {\n\t/** 暴露到 window 的全局名，默认 `__electronDeckBridge` */\n\treadonly globalName?: string\n}\n\n/**\n * 在 host preload 内调用，把 framework typed RPC + event push bridge 暴露到\n * webview window：\n *\n * ```ts\n * import { contextBridge, ipcRenderer } from 'electron'\n * import { exposeDeckBridge } from '@dimina-kit/electron-deck/preload'\n * exposeDeckBridge()\n * ```\n *\n * 见 `DeckBridge` 接口（`shared/protocol.ts`）。\n *\n * `@experimental` No production consumer yet — pairs with `createDeckClient` /\n * `DeckConfig.hostServices` / `events`, which only `examples/` / `spike/` use;\n * no host in this repo calls `exposeDeckBridge`. Contract may change until a\n * second real consumer adopts it.\n */\nexport function exposeDeckBridge(options?: ExposeBridgeOptions): void {\n\tif (typeof contextBridge?.exposeInMainWorld !== 'function' || typeof ipcRenderer?.invoke !== 'function') {\n\t\tthrow new Error('exposeDeckBridge: must be called from a preload script (electron contextBridge / ipcRenderer unavailable)')\n\t}\n\n\tconst globalName = options?.globalName ?? DEFAULT_BRIDGE_GLOBAL\n\t// 自检 globalThis —— contextBridge 内部也维护去重，但我们抢先抛更明确的诊断\n\tconst g = globalThis as unknown as Record<string, unknown>\n\tif (g[globalName] !== undefined) {\n\t\tthrow new Error(`Deck bridge already exposed at \"${globalName}\"`)\n\t}\n\n\tconst bridge: DeckBridge = {\n\t\tversion: BRIDGE_PROTOCOL_VERSION,\n\t\tprobe(): Promise<ProbeResponse> {\n\t\t\treturn ipcRenderer.invoke(DeckChannel.Probe) as Promise<ProbeResponse>\n\t\t},\n\t\tinvoke(req: InvokeRequest): Promise<InvokeResponse> {\n\t\t\treturn ipcRenderer.invoke(DeckChannel.Invoke, req) as Promise<InvokeResponse>\n\t\t},\n\t\tonEvent(listener: (env: EventEnvelope) => void): () => void {\n\t\t\tconst wrapped = (_event: unknown, env: EventEnvelope): void => {\n\t\t\t\tlistener(env)\n\t\t\t}\n\t\t\tipcRenderer.on(DeckChannel.Event, wrapped)\n\t\t\treturn () => {\n\t\t\t\tipcRenderer.removeListener(DeckChannel.Event, wrapped)\n\t\t\t}\n\t\t},\n\t}\n\n\tcontextBridge.exposeInMainWorld(globalName, bridge)\n}\n\nexport interface ExposeLayoutBridgeOptions {\n\t/** 暴露到 window 的全局名，默认 `__electronDeckLayoutBridge` */\n\treadonly globalName?: string\n}\n\n/**\n * 在 host preload 内调用，把三条 slot-token LAYOUT channel（`slot-grant` PUSH /\n * `snapshot` send / `layout-subscribe` invoke）封装成一个 `LayoutBridge`-shaped\n * 对象暴露到 webview window，供 renderer：\n *\n * ```ts\n * import { exposeDeckLayoutBridge } from '@dimina-kit/electron-deck/preload'\n * exposeDeckLayoutBridge()\n * // renderer:\n * createDeckLayoutClient({ bridge: window.__electronDeckLayoutBridge })\n * ```\n *\n * channel 名一律取自框架 `DeckChannel`（不手抄字符串）。`onSlotGrant` 返回一个\n * 纯 unsubscribe 函数（可跨 contextBridge），不是 Disposable 对象。\n */\nexport function exposeDeckLayoutBridge(options?: ExposeLayoutBridgeOptions): void {\n\tif (typeof contextBridge?.exposeInMainWorld !== 'function' || typeof ipcRenderer?.on !== 'function') {\n\t\tthrow new Error('exposeDeckLayoutBridge: must be called from a preload script (electron contextBridge / ipcRenderer unavailable)')\n\t}\n\n\tconst globalName = options?.globalName ?? DEFAULT_LAYOUT_BRIDGE_GLOBAL\n\tconst g = globalThis as unknown as Record<string, unknown>\n\tif (g[globalName] !== undefined) {\n\t\tthrow new Error(`Deck layout bridge already exposed at \"${globalName}\"`)\n\t}\n\n\tconst bridge: LayoutBridge = {\n\t\tonSlotGrant(cb: (grant: SlotGrant) => void): () => void {\n\t\t\tconst listener = (_event: unknown, grant: SlotGrant): void => {\n\t\t\t\tcb(grant)\n\t\t\t}\n\t\t\tipcRenderer.on(DeckChannel.SlotGrant, listener)\n\t\t\treturn () => {\n\t\t\t\tipcRenderer.removeListener(DeckChannel.SlotGrant, listener)\n\t\t\t}\n\t\t},\n\t\tsendSnapshot(snapshot): void {\n\t\t\tvoid ipcRenderer.invoke(DeckChannel.Snapshot, snapshot).catch(() => {})\n\t\t},\n\t\tsubscribe(): void {\n\t\t\tvoid ipcRenderer.invoke(DeckChannel.LayoutSubscribe).catch(() => {})\n\t\t},\n\t}\n\n\tcontextBridge.exposeInMainWorld(globalName, bridge)\n}\n\nexport type {\n\tEventEnvelope,\n\tInvokeRequest,\n\tInvokeResponse,\n\tProbeResponse,\n\tDeckBridge,\n\tLayoutBridge,\n\tSlotGrant,\n}\nexport { BRIDGE_PROTOCOL_VERSION, DEFAULT_BRIDGE_GLOBAL, DEFAULT_LAYOUT_BRIDGE_GLOBAL, DeckChannel }\n"],"mappings":";;;;AAkBA,IAAa,wBAAwB;;;;;;AAOrC,IAAa,+BAA+B;;AAG5C,IAAa,0BAA0B;AAEvC,IAAa,cAAc;CAC1B,QAAQ;CACR,OAAO;CACP,OAAO;CACP,UAAU;CACV,WAAW;CACX,iBAAiB;AAClB;;;;;;;;;;;;;;;;;;;;ACCA,SAAgB,iBAAiB,SAAqC;CACrE,IAAI,OAAO,SAAA,eAAe,sBAAsB,cAAc,OAAO,SAAA,aAAa,WAAW,YAC5F,MAAM,IAAI,MAAM,2GAA2G;CAG5H,MAAM,aAAa,SAAS,cAAA;CAG5B,IAAI,WAAE,gBAAgB,KAAA,GACrB,MAAM,IAAI,MAAM,mCAAmC,WAAW,EAAE;CAGjE,MAAM,SAAqB;EAC1B,SAAS;EACT,QAAgC;GAC/B,OAAO,SAAA,YAAY,OAAO,YAAY,KAAK;EAC5C;EACA,OAAO,KAA6C;GACnD,OAAO,SAAA,YAAY,OAAO,YAAY,QAAQ,GAAG;EAClD;EACA,QAAQ,UAAoD;GAC3D,MAAM,WAAW,QAAiB,QAA6B;IAC9D,SAAS,GAAG;GACb;GACA,SAAA,YAAY,GAAG,YAAY,OAAO,OAAO;GACzC,aAAa;IACZ,SAAA,YAAY,eAAe,YAAY,OAAO,OAAO;GACtD;EACD;CACD;CAEA,SAAA,cAAc,kBAAkB,YAAY,MAAM;AACnD;;;;;;;;;;;;;;;;AAsBA,SAAgB,uBAAuB,SAA2C;CACjF,IAAI,OAAO,SAAA,eAAe,sBAAsB,cAAc,OAAO,SAAA,aAAa,OAAO,YACxF,MAAM,IAAI,MAAM,iHAAiH;CAGlI,MAAM,aAAa,SAAS,cAAA;CAE5B,IAAI,WAAE,gBAAgB,KAAA,GACrB,MAAM,IAAI,MAAM,0CAA0C,WAAW,EAAE;CAqBxE,SAAA,cAAc,kBAAkB,YAAY;EAjB3C,YAAY,IAA4C;GACvD,MAAM,YAAY,QAAiB,UAA2B;IAC7D,GAAG,KAAK;GACT;GACA,SAAA,YAAY,GAAG,YAAY,WAAW,QAAQ;GAC9C,aAAa;IACZ,SAAA,YAAY,eAAe,YAAY,WAAW,QAAQ;GAC3D;EACD;EACA,aAAa,UAAgB;GAC5B,SAAK,YAAY,OAAO,YAAY,UAAU,QAAQ,EAAE,YAAY,CAAC,CAAC;EACvE;EACA,YAAkB;GACjB,SAAK,YAAY,OAAO,YAAY,eAAe,EAAE,YAAY,CAAC,CAAC;EACpE;CAG2C,CAAM;AACnD"}