syncorejs 0.2.2 → 0.2.3

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.
Files changed (140) hide show
  1. package/dist/_vendor/cli/app.d.mts.map +1 -1
  2. package/dist/_vendor/cli/app.mjs +8 -5
  3. package/dist/_vendor/cli/app.mjs.map +1 -1
  4. package/dist/_vendor/cli/context.mjs.map +1 -1
  5. package/dist/_vendor/cli/dev-session.mjs.map +1 -1
  6. package/dist/_vendor/cli/doctor.mjs.map +1 -1
  7. package/dist/_vendor/cli/errors.mjs.map +1 -1
  8. package/dist/_vendor/cli/help.mjs.map +1 -1
  9. package/dist/_vendor/cli/index.mjs +9 -2
  10. package/dist/_vendor/cli/index.mjs.map +1 -1
  11. package/dist/_vendor/cli/messages.mjs.map +1 -1
  12. package/dist/_vendor/cli/preflight.mjs.map +1 -1
  13. package/dist/_vendor/cli/project.mjs +20 -20
  14. package/dist/_vendor/cli/project.mjs.map +1 -1
  15. package/dist/_vendor/cli/render.mjs.map +1 -1
  16. package/dist/_vendor/cli/targets.mjs.map +1 -1
  17. package/dist/_vendor/core/cli.d.mts +8 -2
  18. package/dist/_vendor/core/cli.d.mts.map +1 -1
  19. package/dist/_vendor/core/cli.mjs +238 -64
  20. package/dist/_vendor/core/cli.mjs.map +1 -1
  21. package/dist/_vendor/core/devtools-auth.mjs.map +1 -1
  22. package/dist/_vendor/core/runtime/components.d.mts.map +1 -1
  23. package/dist/_vendor/core/runtime/components.mjs.map +1 -1
  24. package/dist/_vendor/core/runtime/devtools.d.mts.map +1 -1
  25. package/dist/_vendor/core/runtime/devtools.mjs +130 -23
  26. package/dist/_vendor/core/runtime/devtools.mjs.map +1 -1
  27. package/dist/_vendor/core/runtime/functions.d.mts +388 -6
  28. package/dist/_vendor/core/runtime/functions.d.mts.map +1 -1
  29. package/dist/_vendor/core/runtime/functions.mjs +72 -1
  30. package/dist/_vendor/core/runtime/functions.mjs.map +1 -1
  31. package/dist/_vendor/core/runtime/id.d.mts.map +1 -1
  32. package/dist/_vendor/core/runtime/id.mjs.map +1 -1
  33. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs +11 -5
  34. package/dist/_vendor/core/runtime/internal/engines/devtoolsEngine.mjs.map +1 -1
  35. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs +123 -20
  36. package/dist/_vendor/core/runtime/internal/engines/executionEngine.mjs.map +1 -1
  37. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs +56 -8
  38. package/dist/_vendor/core/runtime/internal/engines/reactivityEngine.mjs.map +1 -1
  39. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs +49 -14
  40. package/dist/_vendor/core/runtime/internal/engines/schedulerEngine.mjs.map +1 -1
  41. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs +4 -7
  42. package/dist/_vendor/core/runtime/internal/engines/schemaEngine.mjs.map +1 -1
  43. package/dist/_vendor/core/runtime/internal/engines/shared.mjs +76 -1
  44. package/dist/_vendor/core/runtime/internal/engines/shared.mjs.map +1 -1
  45. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs +1 -0
  46. package/dist/_vendor/core/runtime/internal/engines/storageEngine.mjs.map +1 -1
  47. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs +4 -3
  48. package/dist/_vendor/core/runtime/internal/runtimeKernel.mjs.map +1 -1
  49. package/dist/_vendor/core/runtime/internal/runtimeStatus.mjs.map +1 -1
  50. package/dist/_vendor/core/runtime/internal/systemMeta.mjs.map +1 -1
  51. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs +4 -0
  52. package/dist/_vendor/core/runtime/internal/transactionCoordinator.mjs.map +1 -1
  53. package/dist/_vendor/core/runtime/runtime.d.mts +1040 -9
  54. package/dist/_vendor/core/runtime/runtime.d.mts.map +1 -1
  55. package/dist/_vendor/core/runtime/runtime.mjs +63 -0
  56. package/dist/_vendor/core/runtime/runtime.mjs.map +1 -1
  57. package/dist/_vendor/core/transport.d.mts +2 -0
  58. package/dist/_vendor/core/transport.d.mts.map +1 -1
  59. package/dist/_vendor/core/transport.mjs +33 -24
  60. package/dist/_vendor/core/transport.mjs.map +1 -1
  61. package/dist/_vendor/devtools-protocol/index.d.ts +149 -4
  62. package/dist/_vendor/devtools-protocol/index.d.ts.map +1 -1
  63. package/dist/_vendor/devtools-protocol/index.js.map +1 -1
  64. package/dist/_vendor/next/config.d.ts +3 -4
  65. package/dist/_vendor/next/config.d.ts.map +1 -1
  66. package/dist/_vendor/next/config.js +37 -19
  67. package/dist/_vendor/next/config.js.map +1 -1
  68. package/dist/_vendor/next/index.d.ts +109 -29
  69. package/dist/_vendor/next/index.d.ts.map +1 -1
  70. package/dist/_vendor/next/index.js +77 -17
  71. package/dist/_vendor/next/index.js.map +1 -1
  72. package/dist/_vendor/platform-expo/index.d.ts +146 -27
  73. package/dist/_vendor/platform-expo/index.d.ts.map +1 -1
  74. package/dist/_vendor/platform-expo/index.js +76 -10
  75. package/dist/_vendor/platform-expo/index.js.map +1 -1
  76. package/dist/_vendor/platform-expo/react.js.map +1 -1
  77. package/dist/_vendor/platform-expo/web-sqljs-wasm.js +16 -0
  78. package/dist/_vendor/platform-expo/web-sqljs-wasm.js.map +1 -0
  79. package/dist/_vendor/platform-node/index.d.mts +173 -9
  80. package/dist/_vendor/platform-node/index.d.mts.map +1 -1
  81. package/dist/_vendor/platform-node/index.mjs +225 -94
  82. package/dist/_vendor/platform-node/index.mjs.map +1 -1
  83. package/dist/_vendor/platform-node/ipc-react.mjs.map +1 -1
  84. package/dist/_vendor/platform-node/ipc.d.mts.map +1 -1
  85. package/dist/_vendor/platform-node/ipc.mjs.map +1 -1
  86. package/dist/_vendor/platform-web/external-change.d.ts +41 -0
  87. package/dist/_vendor/platform-web/external-change.d.ts.map +1 -1
  88. package/dist/_vendor/platform-web/external-change.js +30 -0
  89. package/dist/_vendor/platform-web/external-change.js.map +1 -1
  90. package/dist/_vendor/platform-web/index.d.ts +307 -35
  91. package/dist/_vendor/platform-web/index.d.ts.map +1 -1
  92. package/dist/_vendor/platform-web/index.js +189 -23
  93. package/dist/_vendor/platform-web/index.js.map +1 -1
  94. package/dist/_vendor/platform-web/indexeddb.d.ts +12 -0
  95. package/dist/_vendor/platform-web/indexeddb.d.ts.map +1 -1
  96. package/dist/_vendor/platform-web/indexeddb.js +10 -0
  97. package/dist/_vendor/platform-web/indexeddb.js.map +1 -1
  98. package/dist/_vendor/platform-web/opfs.d.ts +13 -0
  99. package/dist/_vendor/platform-web/opfs.d.ts.map +1 -1
  100. package/dist/_vendor/platform-web/opfs.js +12 -0
  101. package/dist/_vendor/platform-web/opfs.js.map +1 -1
  102. package/dist/_vendor/platform-web/persistence.d.ts +54 -0
  103. package/dist/_vendor/platform-web/persistence.d.ts.map +1 -1
  104. package/dist/_vendor/platform-web/persistence.js +15 -0
  105. package/dist/_vendor/platform-web/persistence.js.map +1 -1
  106. package/dist/_vendor/platform-web/react.d.ts +1 -2
  107. package/dist/_vendor/platform-web/react.d.ts.map +1 -1
  108. package/dist/_vendor/platform-web/react.js +2 -4
  109. package/dist/_vendor/platform-web/react.js.map +1 -1
  110. package/dist/_vendor/platform-web/sqljs.js +10 -1
  111. package/dist/_vendor/platform-web/sqljs.js.map +1 -1
  112. package/dist/_vendor/platform-web/web-sqljs-wasm.js +8 -0
  113. package/dist/_vendor/platform-web/web-sqljs-wasm.js.map +1 -0
  114. package/dist/_vendor/platform-web/worker.d.ts +60 -9
  115. package/dist/_vendor/platform-web/worker.d.ts.map +1 -1
  116. package/dist/_vendor/platform-web/worker.js +37 -4
  117. package/dist/_vendor/platform-web/worker.js.map +1 -1
  118. package/dist/_vendor/react/index.d.ts +196 -13
  119. package/dist/_vendor/react/index.d.ts.map +1 -1
  120. package/dist/_vendor/react/index.js +208 -17
  121. package/dist/_vendor/react/index.js.map +1 -1
  122. package/dist/_vendor/schema/definition.d.ts +129 -0
  123. package/dist/_vendor/schema/definition.d.ts.map +1 -1
  124. package/dist/_vendor/schema/definition.js +99 -0
  125. package/dist/_vendor/schema/definition.js.map +1 -1
  126. package/dist/_vendor/schema/planner.d.ts.map +1 -1
  127. package/dist/_vendor/schema/planner.js.map +1 -1
  128. package/dist/_vendor/schema/validators.d.ts +180 -4
  129. package/dist/_vendor/schema/validators.d.ts.map +1 -1
  130. package/dist/_vendor/schema/validators.js +35 -1
  131. package/dist/_vendor/schema/validators.js.map +1 -1
  132. package/dist/_vendor/svelte/index.d.ts +205 -7
  133. package/dist/_vendor/svelte/index.d.ts.map +1 -1
  134. package/dist/_vendor/svelte/index.js +199 -6
  135. package/dist/_vendor/svelte/index.js.map +1 -1
  136. package/dist/browser.d.ts.map +1 -1
  137. package/dist/cli.js +3 -1
  138. package/dist/cli.js.map +1 -1
  139. package/dist/index.d.ts +1 -1
  140. package/package.json +24 -21
@@ -1 +1 @@
1
- {"version":3,"file":"ipc-react.mjs","names":[],"sources":["../src/ipc-react.tsx"],"sourcesContent":["import { useEffect, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createUnavailableSyncoreClient } from \"@syncore/core\";\nimport { SyncoreProvider } from \"@syncore/react\";\nimport { createRendererSyncoreWindowClient } from \"./ipc.js\";\n\n/**\n * Props for {@link SyncoreElectronProvider}.\n */\nexport interface SyncoreElectronProviderProps {\n /** The React subtree that should receive the renderer Syncore client. */\n children: ReactNode;\n\n /** Optional custom bridge name exposed on `window`. */\n bridgeName?: string;\n\n /** Optional window-like object for tests or custom shells. */\n windowObject?: Window & typeof globalThis;\n}\n\n/**\n * Create a renderer Syncore client from `window.syncoreBridge` and provide it to React.\n */\nexport function SyncoreElectronProvider({\n children,\n bridgeName,\n windowObject\n}: SyncoreElectronProviderProps): ReactNode {\n const resolvedWindow = windowObject ?? window;\n const client = useMemo(\n () => {\n try {\n return createRendererSyncoreWindowClient(\n resolvedWindow,\n bridgeName ?? \"syncoreBridge\"\n );\n } catch (error) {\n return createUnavailableSyncoreClient({\n kind: \"unavailable\",\n reason: \"ipc-unavailable\",\n ...(error instanceof Error ? { error } : {})\n });\n }\n },\n [bridgeName, resolvedWindow]\n );\n\n useEffect(\n () => () => {\n if (\"dispose\" in client && typeof client.dispose === \"function\") {\n client.dispose();\n }\n },\n [client]\n );\n\n return <SyncoreProvider client={client}>{children}</SyncoreProvider>;\n}\n"],"mappings":";;;;;;;;;AAuBA,SAAgB,wBAAwB,EACtC,UACA,YACA,gBAC0C;CAC1C,MAAM,iBAAiB,gBAAgB;CACvC,MAAM,SAAS,cACP;AACJ,MAAI;AACF,UAAO,kCACL,gBACA,cAAc,gBACf;WACM,OAAO;AACd,UAAO,+BAA+B;IACpC,MAAM;IACN,QAAQ;IACR,GAAI,iBAAiB,QAAQ,EAAE,OAAO,GAAG,EAAE;IAC5C,CAAC;;IAGN,CAAC,YAAY,eAAe,CAC7B;AAED,uBACc;AACV,MAAI,aAAa,UAAU,OAAO,OAAO,YAAY,WACnD,QAAO,SAAS;IAGpB,CAAC,OAAO,CACT;AAED,QAAO,oBAAC,iBAAD;EAAyB;EAAS;EAA2B,CAAA"}
1
+ {"version":3,"file":"ipc-react.mjs","names":[],"sources":["../src/ipc-react.tsx"],"sourcesContent":["import { useEffect, useMemo } from \"react\";\nimport type { ReactNode } from \"react\";\nimport { createUnavailableSyncoreClient } from \"@syncore/core\";\nimport { SyncoreProvider } from \"@syncore/react\";\nimport { createRendererSyncoreWindowClient } from \"./ipc.js\";\n\n/**\n * Props for {@link SyncoreElectronProvider}.\n */\nexport interface SyncoreElectronProviderProps {\n /** The React subtree that should receive the renderer Syncore client. */\n children: ReactNode;\n\n /** Optional custom bridge name exposed on `window`. */\n bridgeName?: string;\n\n /** Optional window-like object for tests or custom shells. */\n windowObject?: Window & typeof globalThis;\n}\n\n/**\n * Create a renderer Syncore client from `window.syncoreBridge` and provide it to React.\n */\nexport function SyncoreElectronProvider({\n children,\n bridgeName,\n windowObject\n}: SyncoreElectronProviderProps): ReactNode {\n const resolvedWindow = windowObject ?? window;\n const client = useMemo(\n () => {\n try {\n return createRendererSyncoreWindowClient(\n resolvedWindow,\n bridgeName ?? \"syncoreBridge\"\n );\n } catch (error) {\n return createUnavailableSyncoreClient({\n kind: \"unavailable\",\n reason: \"ipc-unavailable\",\n ...(error instanceof Error ? { error } : {})\n });\n }\n },\n [bridgeName, resolvedWindow]\n );\n\n useEffect(\n () => () => {\n if (\"dispose\" in client && typeof client.dispose === \"function\") {\n client.dispose();\n }\n },\n [client]\n );\n\n return <SyncoreProvider client={client}>{children}</SyncoreProvider>;\n}\n"],"mappings":";;;;;;;;;AAuBA,SAAgB,wBAAwB,EACtC,UACA,YACA,gBAC0C;CAC1C,MAAM,iBAAiB,gBAAgB;CACvC,MAAM,SAAS,cACP;EACJ,IAAI;GACF,OAAO,kCACL,gBACA,cAAc,eAChB;EACF,SAAS,OAAO;GACd,OAAO,+BAA+B;IACpC,MAAM;IACN,QAAQ;IACR,GAAI,iBAAiB,QAAQ,EAAE,MAAM,IAAI,CAAC;GAC5C,CAAC;EACH;CACF,GACA,CAAC,YAAY,cAAc,CAC7B;CAEA,sBACc;EACV,IAAI,aAAa,UAAU,OAAO,OAAO,YAAY,YACnD,OAAO,QAAQ;CAEnB,GACA,CAAC,MAAM,CACT;CAEA,OAAO,oBAAC,iBAAD;EAAyB;EAAS;CAA0B,CAAA;AACrE"}
@@ -1 +1 @@
1
- {"version":3,"file":"ipc.d.mts","names":[],"sources":["../src/ipc.ts"],"mappings":";;;KAUY,oBAAA,iBACM,gBAAA,GAAmB,gBAAA,IACjC,OAAA;AAAA,KACQ,yBAAA,GAA4B,4BAAA;AAAA,KAC5B,kBAAA,WAA6B,gBAAA,CAAiB,MAAA;AAAA,cAE7C,qBAAA,SAA8B,mBAAA;EACjC,KAAA,EAAO,mBAAA;EACP,QAAA,EAAU,mBAAA;EACV,MAAA,EAAQ,mBAAA;EACR,UAAA,EAAY,mBAAA;AAAA;AAAA,KAGV,2BAAA,iBACM,oBAAA,GAAuB,oBAAA,IACrC,0BAAA,CAA2B,OAAA;AAAA,KACnB,sBAAA,GAAyB,qBAAA;AAAA,UAEpB,qBAAA;EACf,WAAA,CAAY,OAAA;EACZ,SAAA,CAAU,QAAA,GAAW,OAAA;AAAA;AAAA,UAGN,mBAAA;EACf,WAAA,CAAY,OAAA;EACZ,SAAA,CAAU,QAAA,GAAW,OAAA;AAAA;AAAA,UAGN,wBAAA;EACf,WAAA,CAAY,OAAA;EACZ,SAAA,CAAU,QAAA,GAAW,OAAA;AAAA;;;;iBAMP,0BAAA,CAA2B,OAAA;EACzC,UAAA;AAAA;;AA/BF;;iBAuCgB,2BAAA,CACd,QAAA,EAAU,yBAAA,GACT,qBAAA;;;;iBAOa,iCAAA,CACd,MAAA,EAAQ,qBAAA,GACP,qBAAA;;;;iBA4Ba,iCAAA,CACd,YAAA,EAAc,MAAA,UAAgB,UAAA,EAC9B,UAAA,YACC,qBAAA;AAAA,iBAqBa,4BAAA,CACd,MAAA,EAAQ,wBAAA,GACP,yBAAA;EAA8B,OAAA;AAAA;AAAA,iBA+BjB,oBAAA,CACd,OAAA,EAAS,2BAAA,GACR,sBAAA"}
1
+ {"version":3,"file":"ipc.d.mts","names":[],"sources":["../src/ipc.ts"],"mappings":";;;KAUY,oBAAA,iBACM,gBAAA,GAAmB,gBAAA,IACjC,OAAA;AAAA,KACQ,yBAAA,GAA4B,4BAA4B;AAAA,KACxD,kBAAA,WAA6B,gBAAgB,CAAC,MAAA;AAAA,cAE7C,qBAAA,SAA8B,mBAAA;EACjC,KAAA,EAAO,mBAAA;EACP,QAAA,EAAU,mBAAA;EACV,MAAA,EAAQ,mBAAA;EACR,UAAA,EAAY,mBAAA;AAAA;AAAA,KAGV,2BAAA,iBACM,oBAAA,GAAuB,oBAAA,IACrC,0BAAA,CAA2B,OAAA;AAAA,KACnB,sBAAA,GAAyB,qBAAqB;AAAA,UAEzC,qBAAA;EACf,WAAA,CAAY,OAAA;EACZ,SAAA,CAAU,QAAA,GAAW,OAAA;AAAA;AAAA,UAGN,mBAAA;EACf,WAAA,CAAY,OAAA;EACZ,SAAA,CAAU,QAAA,GAAW,OAAA;AAAA;AAAA,UAGN,wBAAA;EACf,WAAA,CAAY,OAAA;EACZ,SAAA,CAAU,QAAA,GAAW,OAAA;AAAA;;;;iBAMP,0BAAA,CAA2B,OAE1C;EADC,UAAA;AAAA;AAjC8D;AAEhE;;AAFgE,iBAyChD,2BAAA,CACd,QAAA,EAAU,yBAAA,GACT,qBAAqB;;;;iBAOR,iCAAA,CACd,MAAA,EAAQ,qBAAA,GACP,qBAAqB;;;;iBA4BR,iCAAA,CACd,YAAA,EAAc,MAAA,UAAgB,UAAA,EAC9B,UAAA,YACC,qBAAA;AAAA,iBAqBa,4BAAA,CACd,MAAA,EAAQ,wBAAA,GACP,yBAAyB;EAAK,OAAA;AAAA;AAAA,iBA+BjB,oBAAA,CACd,OAAA,EAAS,2BAAA,GACR,sBAAsB"}
@@ -1 +1 @@
1
- {"version":3,"file":"ipc.mjs","names":[],"sources":["../src/ipc.ts"],"sourcesContent":["import {\n attachRuntimeBridge,\n type AttachRuntimeBridgeOptions,\n type AttachedRuntimeBridge,\n type BridgeQueryWatch,\n SyncoreBridgeClient,\n type SyncoreBridgeMessageEndpoint,\n type SyncoreDataModel\n} from \"@syncore/core\";\n\nexport type NodeIpcSyncoreSchema<\n TSchema extends SyncoreDataModel = SyncoreDataModel\n> = TSchema;\nexport type SyncoreIpcMessageEndpoint = SyncoreBridgeMessageEndpoint;\nexport type RendererQueryWatch<TValue> = BridgeQueryWatch<TValue>;\n\nexport class SyncoreRendererClient extends SyncoreBridgeClient {\n declare query: SyncoreBridgeClient[\"query\"];\n declare mutation: SyncoreBridgeClient[\"mutation\"];\n declare action: SyncoreBridgeClient[\"action\"];\n declare watchQuery: SyncoreBridgeClient[\"watchQuery\"];\n}\n\nexport type AttachNodeIpcRuntimeOptions<\n TSchema extends NodeIpcSyncoreSchema = NodeIpcSyncoreSchema\n> = AttachRuntimeBridgeOptions<TSchema>;\nexport type AttachedNodeIpcRuntime = AttachedRuntimeBridge;\n\nexport interface SyncoreRendererBridge {\n postMessage(message: unknown): void;\n onMessage(listener: (message: unknown) => void): () => void;\n}\n\nexport interface SyncoreWindowBridge {\n postMessage(message: unknown): void;\n onMessage(listener: (message: unknown) => void): () => void;\n}\n\nexport interface SyncoreMainProcessBridge {\n postMessage(message: unknown): void;\n onMessage(listener: (message: unknown) => void): () => void;\n}\n\n/**\n * Install the default Electron preload bridge used by Syncore renderer helpers.\n */\nexport function installSyncoreWindowBridge(options?: {\n bridgeName?: string;\n}): string {\n return `(function(){const bridgeName=${JSON.stringify(options?.bridgeName ?? \"syncoreBridge\")};const {contextBridge,ipcRenderer}=require(\"electron\");const channel=\"syncore:message\";const listeners=new Map();contextBridge.exposeInMainWorld(bridgeName,{postMessage(message){ipcRenderer.send(channel,message);},onMessage(listener){const wrapped=(_event,payload)=>{listener(payload);};listeners.set(listener,wrapped);ipcRenderer.on(channel,wrapped);return()=>{ipcRenderer.off(channel,wrapped);listeners.delete(listener);};}});})();`;\n}\n\n/**\n * Create a renderer client from a low-level IPC message endpoint.\n */\nexport function createRendererSyncoreClient(\n endpoint: SyncoreIpcMessageEndpoint\n): SyncoreRendererClient {\n return new SyncoreRendererClient(endpoint);\n}\n\n/**\n * Create a renderer client from a bridge object exposed by preload code.\n */\nexport function createRendererSyncoreBridgeClient(\n bridge: SyncoreRendererBridge\n): SyncoreRendererClient {\n const listeners = new Map<\n (event: MessageEvent<unknown>) => void,\n () => void\n >();\n\n return createRendererSyncoreClient({\n postMessage(message) {\n bridge.postMessage(message);\n },\n addEventListener(_type, listener) {\n listeners.set(\n listener,\n bridge.onMessage((message) => {\n listener({ data: message } as MessageEvent<unknown>);\n })\n );\n },\n removeEventListener(_type, listener) {\n listeners.get(listener)?.();\n listeners.delete(listener);\n }\n });\n}\n\n/**\n * Create a renderer client from `window.syncoreBridge` or another named bridge.\n */\nexport function createRendererSyncoreWindowClient(\n windowObject: Window & typeof globalThis,\n bridgeName = \"syncoreBridge\"\n): SyncoreRendererClient {\n const bridge = (\n windowObject as typeof windowObject & Record<string, unknown>\n )[bridgeName];\n if (!bridge || typeof bridge !== \"object\") {\n throw new Error(`Missing window.${bridgeName} bridge.`);\n }\n\n const candidate = bridge as SyncoreWindowBridge;\n if (\n typeof candidate.postMessage !== \"function\" ||\n typeof candidate.onMessage !== \"function\"\n ) {\n throw new Error(\n `window.${bridgeName} must expose postMessage() and onMessage().`\n );\n }\n\n return createRendererSyncoreBridgeClient(candidate);\n}\n\nexport function createNodeIpcMessageEndpoint(\n bridge: SyncoreMainProcessBridge\n): SyncoreIpcMessageEndpoint & { dispose(): void } {\n const listeners = new Map<\n (event: MessageEvent<unknown>) => void,\n () => void\n >();\n\n return {\n postMessage(message) {\n bridge.postMessage(message);\n },\n addEventListener(_type, listener) {\n listeners.set(\n listener,\n bridge.onMessage((message) => {\n listener({ data: message } as MessageEvent<unknown>);\n })\n );\n },\n removeEventListener(_type, listener) {\n listeners.get(listener)?.();\n listeners.delete(listener);\n },\n dispose() {\n for (const dispose of listeners.values()) {\n dispose();\n }\n listeners.clear();\n }\n };\n}\n\nexport function attachNodeIpcRuntime(\n options: AttachNodeIpcRuntimeOptions\n): AttachedNodeIpcRuntime {\n return attachRuntimeBridge(options);\n}\n"],"mappings":";;AAgBA,IAAa,wBAAb,cAA2C,oBAAoB;;;;AA8B/D,SAAgB,2BAA2B,SAEhC;AACT,QAAO,gCAAgC,KAAK,UAAU,SAAS,cAAc,gBAAgB,CAAC;;;;;AAMhG,SAAgB,4BACd,UACuB;AACvB,QAAO,IAAI,sBAAsB,SAAS;;;;;AAM5C,SAAgB,kCACd,QACuB;CACvB,MAAM,4BAAY,IAAI,KAGnB;AAEH,QAAO,4BAA4B;EACjC,YAAY,SAAS;AACnB,UAAO,YAAY,QAAQ;;EAE7B,iBAAiB,OAAO,UAAU;AAChC,aAAU,IACR,UACA,OAAO,WAAW,YAAY;AAC5B,aAAS,EAAE,MAAM,SAAS,CAA0B;KACpD,CACH;;EAEH,oBAAoB,OAAO,UAAU;AACnC,aAAU,IAAI,SAAS,IAAI;AAC3B,aAAU,OAAO,SAAS;;EAE7B,CAAC;;;;;AAMJ,SAAgB,kCACd,cACA,aAAa,iBACU;CACvB,MAAM,SACJ,aACA;AACF,KAAI,CAAC,UAAU,OAAO,WAAW,SAC/B,OAAM,IAAI,MAAM,kBAAkB,WAAW,UAAU;CAGzD,MAAM,YAAY;AAClB,KACE,OAAO,UAAU,gBAAgB,cACjC,OAAO,UAAU,cAAc,WAE/B,OAAM,IAAI,MACR,UAAU,WAAW,6CACtB;AAGH,QAAO,kCAAkC,UAAU;;AAGrD,SAAgB,6BACd,QACiD;CACjD,MAAM,4BAAY,IAAI,KAGnB;AAEH,QAAO;EACL,YAAY,SAAS;AACnB,UAAO,YAAY,QAAQ;;EAE7B,iBAAiB,OAAO,UAAU;AAChC,aAAU,IACR,UACA,OAAO,WAAW,YAAY;AAC5B,aAAS,EAAE,MAAM,SAAS,CAA0B;KACpD,CACH;;EAEH,oBAAoB,OAAO,UAAU;AACnC,aAAU,IAAI,SAAS,IAAI;AAC3B,aAAU,OAAO,SAAS;;EAE5B,UAAU;AACR,QAAK,MAAM,WAAW,UAAU,QAAQ,CACtC,UAAS;AAEX,aAAU,OAAO;;EAEpB;;AAGH,SAAgB,qBACd,SACwB;AACxB,QAAO,oBAAoB,QAAQ"}
1
+ {"version":3,"file":"ipc.mjs","names":[],"sources":["../src/ipc.ts"],"sourcesContent":["import {\n attachRuntimeBridge,\n type AttachRuntimeBridgeOptions,\n type AttachedRuntimeBridge,\n type BridgeQueryWatch,\n SyncoreBridgeClient,\n type SyncoreBridgeMessageEndpoint,\n type SyncoreDataModel\n} from \"@syncore/core\";\n\nexport type NodeIpcSyncoreSchema<\n TSchema extends SyncoreDataModel = SyncoreDataModel\n> = TSchema;\nexport type SyncoreIpcMessageEndpoint = SyncoreBridgeMessageEndpoint;\nexport type RendererQueryWatch<TValue> = BridgeQueryWatch<TValue>;\n\nexport class SyncoreRendererClient extends SyncoreBridgeClient {\n declare query: SyncoreBridgeClient[\"query\"];\n declare mutation: SyncoreBridgeClient[\"mutation\"];\n declare action: SyncoreBridgeClient[\"action\"];\n declare watchQuery: SyncoreBridgeClient[\"watchQuery\"];\n}\n\nexport type AttachNodeIpcRuntimeOptions<\n TSchema extends NodeIpcSyncoreSchema = NodeIpcSyncoreSchema\n> = AttachRuntimeBridgeOptions<TSchema>;\nexport type AttachedNodeIpcRuntime = AttachedRuntimeBridge;\n\nexport interface SyncoreRendererBridge {\n postMessage(message: unknown): void;\n onMessage(listener: (message: unknown) => void): () => void;\n}\n\nexport interface SyncoreWindowBridge {\n postMessage(message: unknown): void;\n onMessage(listener: (message: unknown) => void): () => void;\n}\n\nexport interface SyncoreMainProcessBridge {\n postMessage(message: unknown): void;\n onMessage(listener: (message: unknown) => void): () => void;\n}\n\n/**\n * Install the default Electron preload bridge used by Syncore renderer helpers.\n */\nexport function installSyncoreWindowBridge(options?: {\n bridgeName?: string;\n}): string {\n return `(function(){const bridgeName=${JSON.stringify(options?.bridgeName ?? \"syncoreBridge\")};const {contextBridge,ipcRenderer}=require(\"electron\");const channel=\"syncore:message\";const listeners=new Map();contextBridge.exposeInMainWorld(bridgeName,{postMessage(message){ipcRenderer.send(channel,message);},onMessage(listener){const wrapped=(_event,payload)=>{listener(payload);};listeners.set(listener,wrapped);ipcRenderer.on(channel,wrapped);return()=>{ipcRenderer.off(channel,wrapped);listeners.delete(listener);};}});})();`;\n}\n\n/**\n * Create a renderer client from a low-level IPC message endpoint.\n */\nexport function createRendererSyncoreClient(\n endpoint: SyncoreIpcMessageEndpoint\n): SyncoreRendererClient {\n return new SyncoreRendererClient(endpoint);\n}\n\n/**\n * Create a renderer client from a bridge object exposed by preload code.\n */\nexport function createRendererSyncoreBridgeClient(\n bridge: SyncoreRendererBridge\n): SyncoreRendererClient {\n const listeners = new Map<\n (event: MessageEvent<unknown>) => void,\n () => void\n >();\n\n return createRendererSyncoreClient({\n postMessage(message) {\n bridge.postMessage(message);\n },\n addEventListener(_type, listener) {\n listeners.set(\n listener,\n bridge.onMessage((message) => {\n listener({ data: message } as MessageEvent<unknown>);\n })\n );\n },\n removeEventListener(_type, listener) {\n listeners.get(listener)?.();\n listeners.delete(listener);\n }\n });\n}\n\n/**\n * Create a renderer client from `window.syncoreBridge` or another named bridge.\n */\nexport function createRendererSyncoreWindowClient(\n windowObject: Window & typeof globalThis,\n bridgeName = \"syncoreBridge\"\n): SyncoreRendererClient {\n const bridge = (\n windowObject as typeof windowObject & Record<string, unknown>\n )[bridgeName];\n if (!bridge || typeof bridge !== \"object\") {\n throw new Error(`Missing window.${bridgeName} bridge.`);\n }\n\n const candidate = bridge as SyncoreWindowBridge;\n if (\n typeof candidate.postMessage !== \"function\" ||\n typeof candidate.onMessage !== \"function\"\n ) {\n throw new Error(\n `window.${bridgeName} must expose postMessage() and onMessage().`\n );\n }\n\n return createRendererSyncoreBridgeClient(candidate);\n}\n\nexport function createNodeIpcMessageEndpoint(\n bridge: SyncoreMainProcessBridge\n): SyncoreIpcMessageEndpoint & { dispose(): void } {\n const listeners = new Map<\n (event: MessageEvent<unknown>) => void,\n () => void\n >();\n\n return {\n postMessage(message) {\n bridge.postMessage(message);\n },\n addEventListener(_type, listener) {\n listeners.set(\n listener,\n bridge.onMessage((message) => {\n listener({ data: message } as MessageEvent<unknown>);\n })\n );\n },\n removeEventListener(_type, listener) {\n listeners.get(listener)?.();\n listeners.delete(listener);\n },\n dispose() {\n for (const dispose of listeners.values()) {\n dispose();\n }\n listeners.clear();\n }\n };\n}\n\nexport function attachNodeIpcRuntime(\n options: AttachNodeIpcRuntimeOptions\n): AttachedNodeIpcRuntime {\n return attachRuntimeBridge(options);\n}\n"],"mappings":";;AAgBA,IAAa,wBAAb,cAA2C,oBAAoB,CAK/D;;;;AAyBA,SAAgB,2BAA2B,SAEhC;CACT,OAAO,gCAAgC,KAAK,UAAU,SAAS,cAAc,eAAe,EAAE;AAChG;;;;AAKA,SAAgB,4BACd,UACuB;CACvB,OAAO,IAAI,sBAAsB,QAAQ;AAC3C;;;;AAKA,SAAgB,kCACd,QACuB;CACvB,MAAM,4BAAY,IAAI,IAGpB;CAEF,OAAO,4BAA4B;EACjC,YAAY,SAAS;GACnB,OAAO,YAAY,OAAO;EAC5B;EACA,iBAAiB,OAAO,UAAU;GAChC,UAAU,IACR,UACA,OAAO,WAAW,YAAY;IAC5B,SAAS,EAAE,MAAM,QAAQ,CAA0B;GACrD,CAAC,CACH;EACF;EACA,oBAAoB,OAAO,UAAU;GACnC,UAAU,IAAI,QAAQ,IAAI;GAC1B,UAAU,OAAO,QAAQ;EAC3B;CACF,CAAC;AACH;;;;AAKA,SAAgB,kCACd,cACA,aAAa,iBACU;CACvB,MAAM,SACJ,aACA;CACF,IAAI,CAAC,UAAU,OAAO,WAAW,UAC/B,MAAM,IAAI,MAAM,kBAAkB,WAAW,SAAS;CAGxD,MAAM,YAAY;CAClB,IACE,OAAO,UAAU,gBAAgB,cACjC,OAAO,UAAU,cAAc,YAE/B,MAAM,IAAI,MACR,UAAU,WAAW,4CACvB;CAGF,OAAO,kCAAkC,SAAS;AACpD;AAEA,SAAgB,6BACd,QACiD;CACjD,MAAM,4BAAY,IAAI,IAGpB;CAEF,OAAO;EACL,YAAY,SAAS;GACnB,OAAO,YAAY,OAAO;EAC5B;EACA,iBAAiB,OAAO,UAAU;GAChC,UAAU,IACR,UACA,OAAO,WAAW,YAAY;IAC5B,SAAS,EAAE,MAAM,QAAQ,CAA0B;GACrD,CAAC,CACH;EACF;EACA,oBAAoB,OAAO,UAAU;GACnC,UAAU,IAAI,QAAQ,IAAI;GAC1B,UAAU,OAAO,QAAQ;EAC3B;EACA,UAAU;GACR,KAAK,MAAM,WAAW,UAAU,OAAO,GACrC,QAAQ;GAEV,UAAU,MAAM;EAClB;CACF;AACF;AAEA,SAAgB,qBACd,SACwB;CACxB,OAAO,oBAAoB,OAAO;AACpC"}
@@ -4,9 +4,23 @@ import initSqlJs from "sql.js";
4
4
 
5
5
  //#region src/external-change.d.ts
6
6
  type SqlJsDatabase = initSqlJs.Database;
7
+ /** Options for constructing a {@link BroadcastChannelExternalChangeSignal}. */
7
8
  interface BroadcastChannelExternalChangeSignalOptions {
9
+ /** Name of the `BroadcastChannel`, shared by all tabs with the same database. */
8
10
  channelName: string;
9
11
  }
12
+ /**
13
+ * A `BroadcastChannel`-based {@link SyncoreExternalChangeSignal} that
14
+ * propagates database-mutation events across all browser tabs sharing the same
15
+ * Syncore database.
16
+ *
17
+ * When a Syncore mutation commits, the runtime publishes a change event on this
18
+ * channel. Other tabs subscribed to the same channel reload their queries
19
+ * automatically, keeping all open tabs in sync without a server round-trip.
20
+ *
21
+ * Constructed automatically by `createWebSyncoreRuntime`. Exposed for
22
+ * advanced setups that build the persistence layer independently.
23
+ */
10
24
  declare class BroadcastChannelExternalChangeSignal implements SyncoreExternalChangeSignal {
11
25
  private readonly channel;
12
26
  private readonly listeners;
@@ -16,12 +30,32 @@ declare class BroadcastChannelExternalChangeSignal implements SyncoreExternalCha
16
30
  close(): void;
17
31
  private readonly messageListener;
18
32
  }
33
+ /** Options for constructing a {@link SqlJsExternalChangeApplier}. */
19
34
  interface SqlJsExternalChangeApplierOptions {
35
+ /** Logical name of the Syncore database, used to load the latest snapshot from persistence. */
20
36
  databaseName: string;
37
+ /** The web persistence layer to read the updated database bytes from. */
21
38
  persistence: SyncoreWebPersistence;
39
+ /**
40
+ * Factory that creates a new sql.js `Database` instance from optional
41
+ * initial bytes. Called whenever the database needs to be swapped after an
42
+ * external change.
43
+ */
22
44
  createDatabase: (bytes?: Uint8Array) => SqlJsDatabase;
45
+ /** Callback invoked with the newly created database so the runtime can swap its reference. */
23
46
  replaceDatabase(database: SqlJsDatabase): void;
24
47
  }
48
+ /**
49
+ * A {@link SyncoreExternalChangeApplier} for sql.js (in-memory) databases.
50
+ *
51
+ * When another tab commits a mutation and broadcasts the change event, this
52
+ * applier loads the latest database snapshot from web persistence (OPFS or
53
+ * IndexedDB) and swaps the in-memory `sql.js` database instance so the current
54
+ * tab reflects the new state.
55
+ *
56
+ * Constructed automatically by `createWebSyncoreRuntime` when using sql.js
57
+ * persistence. Exposed for advanced setups.
58
+ */
25
59
  declare class SqlJsExternalChangeApplier implements SyncoreExternalChangeApplier {
26
60
  private readonly databaseName;
27
61
  private readonly persistence;
@@ -34,6 +68,13 @@ declare class SqlJsExternalChangeApplier implements SyncoreExternalChangeApplier
34
68
  changedScopes: ImpactScope[];
35
69
  }>;
36
70
  }
71
+ /**
72
+ * Derive the canonical `BroadcastChannel` name for cross-tab sync from a
73
+ * logical database name.
74
+ *
75
+ * All Syncore runtimes sharing the same `databaseName` will use the same
76
+ * channel, ensuring mutations in one tab are visible to all others.
77
+ */
37
78
  declare function createDefaultSyncChannelName(databaseName: string): string;
38
79
  //#endregion
39
80
  export { BroadcastChannelExternalChangeSignal, BroadcastChannelExternalChangeSignalOptions, SqlJsExternalChangeApplier, SqlJsExternalChangeApplierOptions, createDefaultSyncChannelName };
@@ -1 +1 @@
1
- {"version":3,"file":"external-change.d.ts","names":[],"sources":["../src/external-change.ts"],"mappings":";;;;;KASK,aAAA,GAAgB,SAAA,CAAU,QAAA;AAAA,UAEd,2CAAA;EACf,WAAA;AAAA;AAAA,cAGW,oCAAA,YAAgD,2BAAA;EAAA,iBAC1C,OAAA;EAAA,iBACA,SAAA;cAIL,OAAA,EAAS,2CAAA;EAOrB,SAAA,CAAU,QAAA,GAAW,KAAA,EAAO,0BAAA;EAO5B,OAAA,CAAQ,KAAA,EAAO,0BAAA;EAIf,KAAA,CAAA;EAAA,iBAKiB,eAAA;AAAA;AAAA,UAUF,iCAAA;EACf,YAAA;EACA,WAAA,EAAa,qBAAA;EACb,cAAA,GAAiB,KAAA,GAAQ,UAAA,KAAe,aAAA;EACxC,eAAA,CAAgB,QAAA,EAAU,aAAA;AAAA;AAAA,cAGf,0BAAA,YAAsC,4BAAA;EAAA,iBAChC,YAAA;EAAA,iBACA,WAAA;EAAA,iBACA,cAAA;EAAA,iBACA,eAAA;cAEL,OAAA,EAAS,iCAAA;EAOf,mBAAA,CAAoB,KAAA,EAAO,0BAAA,GAA0B,OAAA;;;;;;iBAqB7C,4BAAA,CAA6B,YAAA"}
1
+ {"version":3,"file":"external-change.d.ts","names":[],"sources":["../src/external-change.ts"],"mappings":";;;;;KASK,aAAA,GAAgB,SAAA,CAAU,QAAQ;;UAGtB,2CAAA;EAHC;EAKhB,WAAW;AAAA;AAL0B;AAGvC;;;;AAEa;AAeb;;;;;;AApBuC,cAoB1B,oCAAA,YAAgD,2BAAA;EAAA,iBAC1C,OAAA;EAAA,iBACA,SAAA;cAIL,OAAA,EAAS,2CAAA;EAOrB,SAAA,CAAU,QAAA,GAAW,KAAA,EAAO,0BAAA;EAO5B,OAAA,CAAQ,KAAA,EAAO,0BAAA;EAIf,KAAA,CAAA;EAAA,iBAKiB,eAAA;AAAA;;UAWF,iCAAA;EA3BM;EA6BrB,YAAA;EAtBA;EAwBA,WAAA,EAAa,qBAAA;EAxBL;;;;AASwB;EAqBhC,cAAA,GAAiB,KAAA,GAAQ,UAAA,KAAe,aAAA;EAVQ;EAYhD,eAAA,CAAgB,QAAA,EAAU,aAAA;AAAA;;;;;;;;;;;;cAcf,0BAAA,YAAsC,4BAAA;EAAA,iBAChC,YAAA;EAAA,iBACA,WAAA;EAAA,iBACA,cAAA;EAAA,iBACA,eAAA;cAEL,OAAA,EAAS,iCAAA;EAOf,mBAAA,CAAoB,KAAA,EAAO,0BAAA,GAA0B,OAAA;;;;;;;;;;;;;iBA4B7C,4BAAA,CAA6B,YAAoB"}
@@ -1,4 +1,16 @@
1
1
  //#region src/external-change.ts
2
+ /**
3
+ * A `BroadcastChannel`-based {@link SyncoreExternalChangeSignal} that
4
+ * propagates database-mutation events across all browser tabs sharing the same
5
+ * Syncore database.
6
+ *
7
+ * When a Syncore mutation commits, the runtime publishes a change event on this
8
+ * channel. Other tabs subscribed to the same channel reload their queries
9
+ * automatically, keeping all open tabs in sync without a server round-trip.
10
+ *
11
+ * Constructed automatically by `createWebSyncoreRuntime`. Exposed for
12
+ * advanced setups that build the persistence layer independently.
13
+ */
2
14
  var BroadcastChannelExternalChangeSignal = class {
3
15
  channel;
4
16
  listeners = /* @__PURE__ */ new Set();
@@ -26,6 +38,17 @@ var BroadcastChannelExternalChangeSignal = class {
26
38
  for (const listener of this.listeners) listener(event.data);
27
39
  };
28
40
  };
41
+ /**
42
+ * A {@link SyncoreExternalChangeApplier} for sql.js (in-memory) databases.
43
+ *
44
+ * When another tab commits a mutation and broadcasts the change event, this
45
+ * applier loads the latest database snapshot from web persistence (OPFS or
46
+ * IndexedDB) and swaps the in-memory `sql.js` database instance so the current
47
+ * tab reflects the new state.
48
+ *
49
+ * Constructed automatically by `createWebSyncoreRuntime` when using sql.js
50
+ * persistence. Exposed for advanced setups.
51
+ */
29
52
  var SqlJsExternalChangeApplier = class {
30
53
  databaseName;
31
54
  persistence;
@@ -50,6 +73,13 @@ var SqlJsExternalChangeApplier = class {
50
73
  };
51
74
  }
52
75
  };
76
+ /**
77
+ * Derive the canonical `BroadcastChannel` name for cross-tab sync from a
78
+ * logical database name.
79
+ *
80
+ * All Syncore runtimes sharing the same `databaseName` will use the same
81
+ * channel, ensuring mutations in one tab are visible to all others.
82
+ */
53
83
  function createDefaultSyncChannelName(databaseName) {
54
84
  return `syncore:external:${databaseName}`;
55
85
  }
@@ -1 +1 @@
1
- {"version":3,"file":"external-change.js","names":[],"sources":["../src/external-change.ts"],"sourcesContent":["import type {\n ImpactScope,\n SyncoreExternalChangeApplier,\n SyncoreExternalChangeEvent,\n SyncoreExternalChangeSignal\n} from \"@syncore/core\";\nimport type initSqlJs from \"sql.js\";\nimport type { SyncoreWebPersistence } from \"./persistence.js\";\n\ntype SqlJsDatabase = initSqlJs.Database;\n\nexport interface BroadcastChannelExternalChangeSignalOptions {\n channelName: string;\n}\n\nexport class BroadcastChannelExternalChangeSignal implements SyncoreExternalChangeSignal {\n private readonly channel: BroadcastChannel | undefined;\n private readonly listeners = new Set<\n (event: SyncoreExternalChangeEvent) => void\n >();\n\n constructor(options: BroadcastChannelExternalChangeSignalOptions) {\n if (typeof BroadcastChannel !== \"undefined\") {\n this.channel = new BroadcastChannel(options.channelName);\n this.channel.addEventListener(\"message\", this.messageListener);\n }\n }\n\n subscribe(listener: (event: SyncoreExternalChangeEvent) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n publish(event: SyncoreExternalChangeEvent): void {\n this.channel?.postMessage(event);\n }\n\n close(): void {\n this.channel?.removeEventListener(\"message\", this.messageListener);\n this.channel?.close();\n }\n\n private readonly messageListener = (event: MessageEvent<unknown>) => {\n if (!isExternalChangeEvent(event.data)) {\n return;\n }\n for (const listener of this.listeners) {\n listener(event.data);\n }\n };\n}\n\nexport interface SqlJsExternalChangeApplierOptions {\n databaseName: string;\n persistence: SyncoreWebPersistence;\n createDatabase: (bytes?: Uint8Array) => SqlJsDatabase;\n replaceDatabase(database: SqlJsDatabase): void;\n}\n\nexport class SqlJsExternalChangeApplier implements SyncoreExternalChangeApplier {\n private readonly databaseName: string;\n private readonly persistence: SyncoreWebPersistence;\n private readonly createDatabase: (bytes?: Uint8Array) => SqlJsDatabase;\n private readonly replaceDatabase: (database: SqlJsDatabase) => void;\n\n constructor(options: SqlJsExternalChangeApplierOptions) {\n this.databaseName = options.databaseName;\n this.persistence = options.persistence;\n this.createDatabase = (bytes) => options.createDatabase(bytes);\n this.replaceDatabase = (database) => options.replaceDatabase(database);\n }\n\n async applyExternalChange(event: SyncoreExternalChangeEvent) {\n const databaseChanged = event.scope === \"database\" || event.scope === \"all\";\n if (databaseChanged) {\n const bytes = await this.persistence.loadDatabase(this.databaseName);\n if (bytes) {\n this.replaceDatabase(this.createDatabase(bytes));\n }\n }\n return {\n databaseChanged,\n storageChanged: event.scope === \"storage\" || event.scope === \"all\",\n changedScopes:\n event.changedScopes ??\n ([\n ...(event.changedTables ?? []).map((tableName) => `table:${tableName}`),\n ...(event.storageIds ?? []).map((storageId) => `storage:${storageId}`)\n ] as ImpactScope[])\n };\n }\n}\n\nexport function createDefaultSyncChannelName(databaseName: string): string {\n return `syncore:external:${databaseName}`;\n}\n\nfunction isExternalChangeEvent(\n value: unknown\n): value is SyncoreExternalChangeEvent {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"sourceId\" in value &&\n \"scope\" in value &&\n \"reason\" in value &&\n \"timestamp\" in value\n );\n}\n"],"mappings":";AAeA,IAAa,uCAAb,MAAyF;CACvF;CACA,4BAA6B,IAAI,KAE9B;CAEH,YAAY,SAAsD;AAChE,MAAI,OAAO,qBAAqB,aAAa;AAC3C,QAAK,UAAU,IAAI,iBAAiB,QAAQ,YAAY;AACxD,QAAK,QAAQ,iBAAiB,WAAW,KAAK,gBAAgB;;;CAIlE,UAAU,UAAmE;AAC3E,OAAK,UAAU,IAAI,SAAS;AAC5B,eAAa;AACX,QAAK,UAAU,OAAO,SAAS;;;CAInC,QAAQ,OAAyC;AAC/C,OAAK,SAAS,YAAY,MAAM;;CAGlC,QAAc;AACZ,OAAK,SAAS,oBAAoB,WAAW,KAAK,gBAAgB;AAClE,OAAK,SAAS,OAAO;;CAGvB,mBAAoC,UAAiC;AACnE,MAAI,CAAC,sBAAsB,MAAM,KAAK,CACpC;AAEF,OAAK,MAAM,YAAY,KAAK,UAC1B,UAAS,MAAM,KAAK;;;AAY1B,IAAa,6BAAb,MAAgF;CAC9E;CACA;CACA;CACA;CAEA,YAAY,SAA4C;AACtD,OAAK,eAAe,QAAQ;AAC5B,OAAK,cAAc,QAAQ;AAC3B,OAAK,kBAAkB,UAAU,QAAQ,eAAe,MAAM;AAC9D,OAAK,mBAAmB,aAAa,QAAQ,gBAAgB,SAAS;;CAGxE,MAAM,oBAAoB,OAAmC;EAC3D,MAAM,kBAAkB,MAAM,UAAU,cAAc,MAAM,UAAU;AACtE,MAAI,iBAAiB;GACnB,MAAM,QAAQ,MAAM,KAAK,YAAY,aAAa,KAAK,aAAa;AACpE,OAAI,MACF,MAAK,gBAAgB,KAAK,eAAe,MAAM,CAAC;;AAGpD,SAAO;GACL;GACA,gBAAgB,MAAM,UAAU,aAAa,MAAM,UAAU;GAC7D,eACE,MAAM,iBACL,CACC,IAAI,MAAM,iBAAiB,EAAE,EAAE,KAAK,cAAc,SAAS,YAAY,EACvE,IAAI,MAAM,cAAc,EAAE,EAAE,KAAK,cAAc,WAAW,YAAY,CACvE;GACJ;;;AAIL,SAAgB,6BAA6B,cAA8B;AACzE,QAAO,oBAAoB;;AAG7B,SAAS,sBACP,OACqC;AACrC,QACE,OAAO,UAAU,YACjB,UAAU,QACV,cAAc,SACd,WAAW,SACX,YAAY,SACZ,eAAe"}
1
+ {"version":3,"file":"external-change.js","names":[],"sources":["../src/external-change.ts"],"sourcesContent":["import type {\n ImpactScope,\n SyncoreExternalChangeApplier,\n SyncoreExternalChangeEvent,\n SyncoreExternalChangeSignal\n} from \"@syncore/core\";\nimport type initSqlJs from \"sql.js\";\nimport type { SyncoreWebPersistence } from \"./persistence.js\";\n\ntype SqlJsDatabase = initSqlJs.Database;\n\n/** Options for constructing a {@link BroadcastChannelExternalChangeSignal}. */\nexport interface BroadcastChannelExternalChangeSignalOptions {\n /** Name of the `BroadcastChannel`, shared by all tabs with the same database. */\n channelName: string;\n}\n\n/**\n * A `BroadcastChannel`-based {@link SyncoreExternalChangeSignal} that\n * propagates database-mutation events across all browser tabs sharing the same\n * Syncore database.\n *\n * When a Syncore mutation commits, the runtime publishes a change event on this\n * channel. Other tabs subscribed to the same channel reload their queries\n * automatically, keeping all open tabs in sync without a server round-trip.\n *\n * Constructed automatically by `createWebSyncoreRuntime`. Exposed for\n * advanced setups that build the persistence layer independently.\n */\nexport class BroadcastChannelExternalChangeSignal implements SyncoreExternalChangeSignal {\n private readonly channel: BroadcastChannel | undefined;\n private readonly listeners = new Set<\n (event: SyncoreExternalChangeEvent) => void\n >();\n\n constructor(options: BroadcastChannelExternalChangeSignalOptions) {\n if (typeof BroadcastChannel !== \"undefined\") {\n this.channel = new BroadcastChannel(options.channelName);\n this.channel.addEventListener(\"message\", this.messageListener);\n }\n }\n\n subscribe(listener: (event: SyncoreExternalChangeEvent) => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n publish(event: SyncoreExternalChangeEvent): void {\n this.channel?.postMessage(event);\n }\n\n close(): void {\n this.channel?.removeEventListener(\"message\", this.messageListener);\n this.channel?.close();\n }\n\n private readonly messageListener = (event: MessageEvent<unknown>) => {\n if (!isExternalChangeEvent(event.data)) {\n return;\n }\n for (const listener of this.listeners) {\n listener(event.data);\n }\n };\n}\n\n/** Options for constructing a {@link SqlJsExternalChangeApplier}. */\nexport interface SqlJsExternalChangeApplierOptions {\n /** Logical name of the Syncore database, used to load the latest snapshot from persistence. */\n databaseName: string;\n /** The web persistence layer to read the updated database bytes from. */\n persistence: SyncoreWebPersistence;\n /**\n * Factory that creates a new sql.js `Database` instance from optional\n * initial bytes. Called whenever the database needs to be swapped after an\n * external change.\n */\n createDatabase: (bytes?: Uint8Array) => SqlJsDatabase;\n /** Callback invoked with the newly created database so the runtime can swap its reference. */\n replaceDatabase(database: SqlJsDatabase): void;\n}\n\n/**\n * A {@link SyncoreExternalChangeApplier} for sql.js (in-memory) databases.\n *\n * When another tab commits a mutation and broadcasts the change event, this\n * applier loads the latest database snapshot from web persistence (OPFS or\n * IndexedDB) and swaps the in-memory `sql.js` database instance so the current\n * tab reflects the new state.\n *\n * Constructed automatically by `createWebSyncoreRuntime` when using sql.js\n * persistence. Exposed for advanced setups.\n */\nexport class SqlJsExternalChangeApplier implements SyncoreExternalChangeApplier {\n private readonly databaseName: string;\n private readonly persistence: SyncoreWebPersistence;\n private readonly createDatabase: (bytes?: Uint8Array) => SqlJsDatabase;\n private readonly replaceDatabase: (database: SqlJsDatabase) => void;\n\n constructor(options: SqlJsExternalChangeApplierOptions) {\n this.databaseName = options.databaseName;\n this.persistence = options.persistence;\n this.createDatabase = (bytes) => options.createDatabase(bytes);\n this.replaceDatabase = (database) => options.replaceDatabase(database);\n }\n\n async applyExternalChange(event: SyncoreExternalChangeEvent) {\n const databaseChanged = event.scope === \"database\" || event.scope === \"all\";\n if (databaseChanged) {\n const bytes = await this.persistence.loadDatabase(this.databaseName);\n if (bytes) {\n this.replaceDatabase(this.createDatabase(bytes));\n }\n }\n return {\n databaseChanged,\n storageChanged: event.scope === \"storage\" || event.scope === \"all\",\n changedScopes:\n event.changedScopes ??\n ([\n ...(event.changedTables ?? []).map((tableName) => `table:${tableName}`),\n ...(event.storageIds ?? []).map((storageId) => `storage:${storageId}`)\n ] as ImpactScope[])\n };\n }\n}\n\n/**\n * Derive the canonical `BroadcastChannel` name for cross-tab sync from a\n * logical database name.\n *\n * All Syncore runtimes sharing the same `databaseName` will use the same\n * channel, ensuring mutations in one tab are visible to all others.\n */\nexport function createDefaultSyncChannelName(databaseName: string): string {\n return `syncore:external:${databaseName}`;\n}\n\nfunction isExternalChangeEvent(\n value: unknown\n): value is SyncoreExternalChangeEvent {\n return (\n typeof value === \"object\" &&\n value !== null &&\n \"sourceId\" in value &&\n \"scope\" in value &&\n \"reason\" in value &&\n \"timestamp\" in value\n );\n}\n"],"mappings":";;;;;;;;;;;;;AA6BA,IAAa,uCAAb,MAAyF;CACvF;CACA,4BAA6B,IAAI,IAE/B;CAEF,YAAY,SAAsD;EAChE,IAAI,OAAO,qBAAqB,aAAa;GAC3C,KAAK,UAAU,IAAI,iBAAiB,QAAQ,WAAW;GACvD,KAAK,QAAQ,iBAAiB,WAAW,KAAK,eAAe;EAC/D;CACF;CAEA,UAAU,UAAmE;EAC3E,KAAK,UAAU,IAAI,QAAQ;EAC3B,aAAa;GACX,KAAK,UAAU,OAAO,QAAQ;EAChC;CACF;CAEA,QAAQ,OAAyC;EAC/C,KAAK,SAAS,YAAY,KAAK;CACjC;CAEA,QAAc;EACZ,KAAK,SAAS,oBAAoB,WAAW,KAAK,eAAe;EACjE,KAAK,SAAS,MAAM;CACtB;CAEA,mBAAoC,UAAiC;EACnE,IAAI,CAAC,sBAAsB,MAAM,IAAI,GACnC;EAEF,KAAK,MAAM,YAAY,KAAK,WAC1B,SAAS,MAAM,IAAI;CAEvB;AACF;;;;;;;;;;;;AA6BA,IAAa,6BAAb,MAAgF;CAC9E;CACA;CACA;CACA;CAEA,YAAY,SAA4C;EACtD,KAAK,eAAe,QAAQ;EAC5B,KAAK,cAAc,QAAQ;EAC3B,KAAK,kBAAkB,UAAU,QAAQ,eAAe,KAAK;EAC7D,KAAK,mBAAmB,aAAa,QAAQ,gBAAgB,QAAQ;CACvE;CAEA,MAAM,oBAAoB,OAAmC;EAC3D,MAAM,kBAAkB,MAAM,UAAU,cAAc,MAAM,UAAU;EACtE,IAAI,iBAAiB;GACnB,MAAM,QAAQ,MAAM,KAAK,YAAY,aAAa,KAAK,YAAY;GACnE,IAAI,OACF,KAAK,gBAAgB,KAAK,eAAe,KAAK,CAAC;EAEnD;EACA,OAAO;GACL;GACA,gBAAgB,MAAM,UAAU,aAAa,MAAM,UAAU;GAC7D,eACE,MAAM,iBACL,CACC,IAAI,MAAM,iBAAiB,CAAC,GAAG,KAAK,cAAc,SAAS,WAAW,GACtE,IAAI,MAAM,cAAc,CAAC,GAAG,KAAK,cAAc,WAAW,WAAW,CACvE;EACJ;CACF;AACF;;;;;;;;AASA,SAAgB,6BAA6B,cAA8B;CACzE,OAAO,oBAAoB;AAC7B;AAEA,SAAS,sBACP,OACqC;CACrC,OACE,OAAO,UAAU,YACjB,UAAU,QACV,cAAc,SACd,WAAW,SACX,YAAY,SACZ,eAAe;AAEnB"}