jazz-tools 0.20.17 → 0.20.18

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 (65) hide show
  1. package/.svelte-kit/__package__/client.d.ts.map +1 -1
  2. package/.svelte-kit/__package__/client.js +8 -1
  3. package/.svelte-kit/__package__/tests/client.test.js +67 -0
  4. package/.turbo/turbo-build.log +63 -63
  5. package/CHANGELOG.md +13 -0
  6. package/dist/better-auth/auth/client.d.ts.map +1 -1
  7. package/dist/better-auth/auth/client.js +12 -1
  8. package/dist/better-auth/auth/client.js.map +1 -1
  9. package/dist/browser/BrowserContextManager.d.ts +1 -0
  10. package/dist/browser/BrowserContextManager.d.ts.map +1 -1
  11. package/dist/browser/createBrowserContext.d.ts +1 -0
  12. package/dist/browser/createBrowserContext.d.ts.map +1 -1
  13. package/dist/browser/index.js +11 -4
  14. package/dist/browser/index.js.map +1 -1
  15. package/dist/{chunk-3BV3JUMV.js → chunk-MIPBSAS7.js} +57 -17
  16. package/dist/chunk-MIPBSAS7.js.map +1 -0
  17. package/dist/index.js +1 -1
  18. package/dist/react/index.js +11 -3
  19. package/dist/react/index.js.map +1 -1
  20. package/dist/react/provider.d.ts +1 -1
  21. package/dist/react/provider.d.ts.map +1 -1
  22. package/dist/react-native/index.js +22 -7
  23. package/dist/react-native/index.js.map +1 -1
  24. package/dist/react-native-core/ReactNativeContextManager.d.ts +1 -0
  25. package/dist/react-native-core/ReactNativeContextManager.d.ts.map +1 -1
  26. package/dist/react-native-core/index.js +22 -7
  27. package/dist/react-native-core/index.js.map +1 -1
  28. package/dist/react-native-core/platform.d.ts +1 -0
  29. package/dist/react-native-core/platform.d.ts.map +1 -1
  30. package/dist/react-native-core/provider.d.ts +1 -1
  31. package/dist/react-native-core/provider.d.ts.map +1 -1
  32. package/dist/svelte/Provider.svelte +3 -0
  33. package/dist/svelte/Provider.svelte.d.ts.map +1 -1
  34. package/dist/testing.js +1 -1
  35. package/dist/tools/coValues/schemaUnion.d.ts +9 -0
  36. package/dist/tools/coValues/schemaUnion.d.ts.map +1 -1
  37. package/dist/tools/implementation/createContext.d.ts +7 -3
  38. package/dist/tools/implementation/createContext.d.ts.map +1 -1
  39. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts.map +1 -1
  40. package/dist/tools/implementation/zodSchema/unionUtils.d.ts.map +1 -1
  41. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  42. package/dist/worker/index.d.ts +1 -0
  43. package/dist/worker/index.d.ts.map +1 -1
  44. package/dist/worker/index.js +6 -2
  45. package/dist/worker/index.js.map +1 -1
  46. package/package.json +4 -4
  47. package/src/better-auth/auth/client.ts +15 -1
  48. package/src/better-auth/auth/tests/client.test.ts +92 -0
  49. package/src/browser/BrowserContextManager.ts +5 -0
  50. package/src/browser/createBrowserContext.ts +8 -0
  51. package/src/react/provider.tsx +9 -1
  52. package/src/react-native-core/ReactNativeContextManager.ts +5 -0
  53. package/src/react-native-core/platform.ts +8 -0
  54. package/src/react-native-core/provider.tsx +9 -1
  55. package/src/svelte/Provider.svelte +3 -0
  56. package/src/tools/coValues/schemaUnion.ts +13 -0
  57. package/src/tools/implementation/createContext.ts +14 -0
  58. package/src/tools/implementation/zodSchema/schemaTypes/CoMapSchema.ts +24 -8
  59. package/src/tools/implementation/zodSchema/unionUtils.ts +2 -1
  60. package/src/tools/subscribe/SubscriptionScope.ts +23 -1
  61. package/src/tools/tests/coMap.test.ts +30 -0
  62. package/src/tools/tests/createContext.test.ts +64 -0
  63. package/src/tools/tests/schemaUnion.test.ts +19 -0
  64. package/src/worker/index.ts +6 -0
  65. package/dist/chunk-3BV3JUMV.js.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"SubscriptionScope.d.ts","sourceRoot":"","sources":["../../../src/tools/subscribe/SubscriptionScope.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SubscriptionScope.d.ts","sourceRoot":"","sources":["../../../src/tools/subscribe/SubscriptionScope.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAwC,MAAM,QAAQ,CAAC;AACzE,OAAO,EAIL,KAAK,OAAO,EACZ,WAAW,EACX,SAAS,EACT,KAAK,UAAU,EACf,KAAK,aAAa,EAMlB,aAAa,EACb,oBAAoB,EACrB,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,SAAS,EAGV,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EACV,gBAAgB,EAEhB,iBAAiB,EACjB,wBAAwB,EACzB,MAAM,YAAY,CAAC;AAOpB,OAAO,EAGL,iBAAiB,EAGlB,MAAM,YAAY,CAAC;AAGpB,qBAAa,iBAAiB,CAAC,CAAC,SAAS,OAAO;IA4DrC,IAAI,EAAE,SAAS;IAEf,EAAE,EAAE,MAAM;IACV,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC;IAC3B,SAAS;IACT,oBAAoB;IACpB,eAAe,CAAC,EAAE,gBAAgB;IAjE3C,MAAM,CAAC,kBAAkB,UAAS;IAElC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE,OAAO;IAI3C,MAAM,CAAC,eAAe;IAItB,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,iBAAiB,CAAqB;IAE9C,UAAU,0CAAiD;IAC3D,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,iBAAiB,CAAC,OAAO,CAAC,CAAC,CAAa;IACjE;;OAEG;IACH,qBAAqB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAa;IAC/C;;OAEG;IACH,OAAO,CAAC,yBAAyB,CAA0B;IAC3D,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,wBAAwB,CAAC;IACvD,OAAO,CAAC,WAAW,CAAqC;IACxD,OAAO,CAAC,gBAAgB,CAAqC;IAC7D,iBAAiB,EAAE,SAAS,GAAG,SAAS,CAAC;IACzC,OAAO,CAAC,YAAY,CAA0B;IAC9C,OAAO,CAAC,KAAK,CAAS;IACtB,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,aAAa,CAAqB;IAC1C,OAAO,CAAC,UAAU,CAAqB;IACvC,OAAO,CAAC,cAAc,CAAqB;IAC3C,OAAO,CAAC,eAAe,CAAqB;IAC5C,OAAO,CAAC,sBAAsB,CAAK;IACnC,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,eAAe,CAAS;IAChC,MAAM,UAAS;IAEf,OAAO,CAAC,OAAO,CAKD;IAEd,OAAO,CAAC,cAAc,CAAS;IAE/B;;;;OAIG;IACH,WAAW,EAAE,KAAK,GAAG,SAAS,CAAC;gBAGtB,IAAI,EAAE,SAAS,EACtB,OAAO,EAAE,aAAa,CAAC,GAAG,CAAC,EACpB,EAAE,EAAE,MAAM,EACV,MAAM,EAAE,UAAU,CAAC,OAAO,CAAC,EAC3B,SAAS,UAAQ,EACjB,oBAAoB,UAAQ,EAC5B,eAAe,CAAC,EAAE,gBAAgB,YAAA,EACzC,MAAM,CAAC,EACH,aAAa,GACb;QACE,OAAO,EAAE,aAAa,CAAC;QACvB,OAAO,EAAE,oBAAoB,CAAC;KAC/B;IAwGP,uBAAuB,CAAC,MAAM,EAAE,MAAM;IAgDtC,OAAO,CAAC,mBAAmB;IAuD3B,WAAW,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC;IAOvC,OAAO,CAAC,YAAY;IAyFpB,OAAO,CAAC,kBAAkB;IA8C1B,iBAAiB,CACf,EAAE,EAAE,MAAM,EACV,KAAK,EAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG,wBAAwB,EAC5D,GAAG,CAAC,EAAE,MAAM;IAqCd,OAAO,CAAC,iBAAiB;IASzB,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAExC,OAAO,CAAC,WAAW,CAAmC;IAEtD,OAAO,CAAC,YAAY;IAMpB,UAAU;IA+CV,gBAAgB;IA0BhB,OAAO,CAAC,gBAAgB;IAYxB,OAAO,CAAC,eAAe,CAAwB;IAE/C,eAAe,IAAI,WAAW,CAAC,CAAC,CAAC;IAgBjC,OAAO,CAAC,kBAAkB;IAwB1B,OAAO,CAAC,qBAAqB;IAmC7B,OAAO,CAAC,QAAQ;IAchB,OAAO,CAAC,QAAQ;IAuBhB,OAAO,CAAC,aAAa;IAkBrB,WAAW,cAAmB,iBAAiB,CAAC,CAAC,CAAC,KAAK,IAAI,EAAI;IAC/D,yBAAyB,cAAmB,MAAM,KAAK,IAAI,EAAI;IAE/D;;;;OAIG;IACH,kBAAkB,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,GAAG,MAAM,IAAI;IAQjE,OAAO,CAAC,sBAAsB;IAO9B,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,IAAI;IAUzD,WAAW,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,IAAI;IAU3D,cAAc,CAAC,GAAG,EAAE,MAAM;IA+C1B,gBAAgB,CAAC,EAAE,EAAE,MAAM;IAS3B;;;;OAIG;IACH,SAAS,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAAC,CAAC,KAAK,IAAI;IA0BzD,aAAa,CAAC,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,CAAC,GAAG,CAAC;IAmDrD,OAAO,CAAC,YAAY;IAqHpB,OAAO,CAAC,YAAY;IAkDpB,OAAO,CAAC,aAAa;IA0CrB,OAAO,CAAC,aAAa;IAkErB,OAAO,CAAE,wBAAwB;IAmBjC,IAAI,MAAM,oCAET;IAED,YAAY;IAUZ,OAAO;CAcR"}
@@ -22,6 +22,7 @@ type WorkerOptions<S extends (AccountClass<Account> & CoValueFromRaw<Account>) |
22
22
  */
23
23
  asActiveAccount?: boolean;
24
24
  storage?: StorageAPI;
25
+ experimental_clockSyncFromServerPings?: boolean;
25
26
  };
26
27
  /** @category Context Creation */
27
28
  export declare function startWorker<S extends (AccountClass<Account> & CoValueFromRaw<Account>) | AnyAccountSchema>(options: WorkerOptions<S>): Promise<{
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,cAAc,EAEd,IAAI,EACJ,UAAU,EACX,MAAM,QAAQ,CAAC;AAChB,OAAO,EACL,KAAK,uBAAuB,EAE7B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,OAAO,EACP,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,KAAK,EAEL,MAAM,EAGP,MAAM,YAAY,CAAC;AAEpB,KAAK,aAAa,CAChB,CAAC,SACG,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,GACjD,gBAAgB,IAClB;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,uBAAuB,CAAC;IACpC,aAAa,CAAC,EAAE,CAAC,CAAC;IAClB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,UAAU,CAAC;CACtB,CAAC;AAEF,iCAAiC;AACjC,wBAAsB,WAAW,CAC/B,CAAC,SACG,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,GACjD,gBAAgB,EACpB,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;IAwFvB;;OAEG;YACwB,MAAM,CAAC,CAAC,CAAC;;QAElC;;;;WAIG;;uBAhByC,KAAK,CAAC,WAAW,CAAC;;;;;IAmBhE;;;;;OAKG;;0CAImC,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI;IAalE;;;;;;OAMG;;IAEH;;;;OAIG;;GAKN"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/worker/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,cAAc,EAEd,IAAI,EACJ,UAAU,EACX,MAAM,QAAQ,CAAC;AAChB,OAAO,EACL,KAAK,uBAAuB,EAE7B,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,OAAO,EACP,YAAY,EACZ,gBAAgB,EAChB,cAAc,EACd,KAAK,EAEL,MAAM,EAGP,MAAM,YAAY,CAAC;AAEpB,KAAK,aAAa,CAChB,CAAC,SACG,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,GACjD,gBAAgB,IAClB;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,uBAAuB,CAAC;IACpC,aAAa,CAAC,EAAE,CAAC,CAAC;IAClB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB;;OAEG;IACH,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;OAEG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,OAAO,CAAC,EAAE,UAAU,CAAC;IACrB,qCAAqC,CAAC,EAAE,OAAO,CAAC;CACjD,CAAC;AAEF,iCAAiC;AACjC,wBAAsB,WAAW,CAC/B,CAAC,SACG,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC,GACjD,gBAAgB,EACpB,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;IA6FvB;;OAEG;YACwB,MAAM,CAAC,CAAC,CAAC;;QAElC;;;;WAIG;;uBAhByC,KAAK,CAAC,WAAW,CAAC;;;;;IAmBhE;;;;;OAKG;;0CAImC,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI;IAalE;;;;;;OAMG;;IAEH;;;;OAIG;;GAKN"}
@@ -36,7 +36,10 @@ async function startWorker(options) {
36
36
  },
37
37
  removePeer: () => {
38
38
  },
39
- WebSocketConstructor: options.WebSocket
39
+ WebSocketConstructor: options.WebSocket,
40
+ onPingReceived: (sample) => {
41
+ node?.clockOffset.addSample(sample);
42
+ }
40
43
  });
41
44
  wsPeer.enable();
42
45
  }
@@ -62,7 +65,8 @@ async function startWorker(options) {
62
65
  peers,
63
66
  crypto: options.crypto ?? await WasmCrypto.create(),
64
67
  asActiveAccount,
65
- storage: options.storage
68
+ storage: options.storage,
69
+ experimental_clockSyncFromServerPings: options.experimental_clockSyncFromServerPings
66
70
  });
67
71
  const account = context.account;
68
72
  node = account.$jazz.localNode;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/worker/index.ts"],"sourcesContent":["import {\n AgentSecret,\n CryptoProvider,\n LocalNode,\n Peer,\n StorageAPI,\n} from \"cojson\";\nimport {\n type AnyWebSocketConstructor,\n WebSocketPeerWithReconnection,\n} from \"cojson-transport-ws\";\nimport { WasmCrypto } from \"cojson/crypto/WasmCrypto\";\nimport {\n Account,\n AccountClass,\n AnyAccountSchema,\n CoValueFromRaw,\n Inbox,\n InstanceOfSchema,\n Loaded,\n createJazzContextFromExistingCredentials,\n MockSessionProvider,\n} from \"jazz-tools\";\n\ntype WorkerOptions<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n> = {\n accountID?: string;\n accountSecret?: string;\n /**\n * A peer to connect to for synchronization.\n * If provided, syncServer is ignored.\n */\n peer?: Peer;\n syncServer?: string;\n WebSocket?: AnyWebSocketConstructor;\n AccountSchema?: S;\n crypto?: CryptoProvider;\n /**\n * If true, the inbox will not be loaded.\n */\n skipInboxLoad?: boolean;\n /**\n * If false, the worker will not set in the global account context\n */\n asActiveAccount?: boolean;\n storage?: StorageAPI;\n};\n\n/** @category Context Creation */\nexport async function startWorker<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n>(options: WorkerOptions<S>) {\n const {\n accountID = process.env.JAZZ_WORKER_ACCOUNT,\n accountSecret = process.env.JAZZ_WORKER_SECRET,\n syncServer = \"wss://cloud.jazz.tools\",\n AccountSchema = Account as unknown as S,\n skipInboxLoad = false,\n asActiveAccount = true,\n } = options;\n\n let node: LocalNode | undefined = undefined;\n\n const peers: Peer[] = [];\n\n // If a peer is provided directly, use it instead of WebSocket\n let wsPeer: WebSocketPeerWithReconnection | undefined;\n\n if (options.peer) {\n peers.push(options.peer);\n } else {\n wsPeer = new WebSocketPeerWithReconnection({\n peer: syncServer,\n reconnectionTimeout: 100,\n addPeer: (peer) => {\n if (node) {\n node.syncManager.addPeer(peer);\n } else {\n peers.push(peer);\n }\n },\n removePeer: () => {},\n WebSocketConstructor: options.WebSocket,\n });\n\n wsPeer.enable();\n }\n\n if (!accountID) {\n throw new Error(\"No accountID provided\");\n }\n if (!accountSecret) {\n throw new Error(\"No accountSecret provided\");\n }\n if (!accountID.startsWith(\"co_\")) {\n throw new Error(\"Invalid accountID\");\n }\n if (!accountSecret?.startsWith(\"sealerSecret_\")) {\n throw new Error(\"Invalid accountSecret\");\n }\n\n const context = await createJazzContextFromExistingCredentials({\n credentials: {\n accountID: accountID,\n secret: accountSecret as AgentSecret,\n },\n AccountSchema,\n sessionProvider: new MockSessionProvider(),\n peers,\n crypto: options.crypto ?? (await WasmCrypto.create()),\n asActiveAccount,\n storage: options.storage,\n });\n\n const account = context.account as InstanceOfSchema<S>;\n node = account.$jazz.localNode;\n\n if (!account.$jazz.refs.profile?.id) {\n throw new Error(\"Account has no profile\");\n }\n\n const inbox = skipInboxLoad ? undefined : await Inbox.load(account);\n\n async function done() {\n await context.account.$jazz.waitForAllCoValuesSync();\n\n wsPeer?.disable();\n context.done();\n }\n\n const inboxPublicApi = inbox\n ? {\n subscribe: inbox.subscribe.bind(inbox) as Inbox[\"subscribe\"],\n }\n : {\n subscribe: () => {},\n };\n\n return {\n /**\n * The worker account instance.\n */\n worker: context.account as Loaded<S>,\n experimental: {\n /**\n * API to subscribe to the inbox messages.\n *\n * More info on the Inbox API: https://jazz.tools/docs/react/server-side/inbox\n */\n inbox: inboxPublicApi,\n },\n /**\n * Wait for the connection to the sync server to be established.\n *\n * If already connected, it will resolve immediately.\n * Returns immediately if using a custom peer.\n */\n waitForConnection() {\n return wsPeer?.waitUntilConnected() ?? Promise.resolve();\n },\n subscribeToConnectionChange(listener: (connected: boolean) => void) {\n if (!wsPeer) {\n // For custom peers, immediately notify as connected\n listener(true);\n return () => {};\n }\n\n wsPeer.subscribe(listener);\n\n return () => {\n wsPeer.unsubscribe(listener);\n };\n },\n /**\n * Waits for all CoValues to sync and then shuts down the worker.\n *\n * To only wait for sync use worker.$jazz.waitForAllCoValuesSync()\n *\n * @deprecated Use shutdownWorker\n */\n done,\n /**\n * Waits for all CoValues to sync and then shuts down the worker.\n *\n * To only wait for sync use worker.$jazz.waitForAllCoValuesSync()\n */\n shutdownWorker() {\n return done();\n },\n };\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/worker/index.ts"],"sourcesContent":["import {\n AgentSecret,\n CryptoProvider,\n LocalNode,\n Peer,\n StorageAPI,\n} from \"cojson\";\nimport {\n type AnyWebSocketConstructor,\n WebSocketPeerWithReconnection,\n} from \"cojson-transport-ws\";\nimport { WasmCrypto } from \"cojson/crypto/WasmCrypto\";\nimport {\n Account,\n AccountClass,\n AnyAccountSchema,\n CoValueFromRaw,\n Inbox,\n InstanceOfSchema,\n Loaded,\n createJazzContextFromExistingCredentials,\n MockSessionProvider,\n} from \"jazz-tools\";\n\ntype WorkerOptions<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n> = {\n accountID?: string;\n accountSecret?: string;\n /**\n * A peer to connect to for synchronization.\n * If provided, syncServer is ignored.\n */\n peer?: Peer;\n syncServer?: string;\n WebSocket?: AnyWebSocketConstructor;\n AccountSchema?: S;\n crypto?: CryptoProvider;\n /**\n * If true, the inbox will not be loaded.\n */\n skipInboxLoad?: boolean;\n /**\n * If false, the worker will not set in the global account context\n */\n asActiveAccount?: boolean;\n storage?: StorageAPI;\n experimental_clockSyncFromServerPings?: boolean;\n};\n\n/** @category Context Creation */\nexport async function startWorker<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n>(options: WorkerOptions<S>) {\n const {\n accountID = process.env.JAZZ_WORKER_ACCOUNT,\n accountSecret = process.env.JAZZ_WORKER_SECRET,\n syncServer = \"wss://cloud.jazz.tools\",\n AccountSchema = Account as unknown as S,\n skipInboxLoad = false,\n asActiveAccount = true,\n } = options;\n\n let node: LocalNode | undefined = undefined;\n\n const peers: Peer[] = [];\n\n // If a peer is provided directly, use it instead of WebSocket\n let wsPeer: WebSocketPeerWithReconnection | undefined;\n\n if (options.peer) {\n peers.push(options.peer);\n } else {\n wsPeer = new WebSocketPeerWithReconnection({\n peer: syncServer,\n reconnectionTimeout: 100,\n addPeer: (peer) => {\n if (node) {\n node.syncManager.addPeer(peer);\n } else {\n peers.push(peer);\n }\n },\n removePeer: () => {},\n WebSocketConstructor: options.WebSocket,\n onPingReceived: (sample) => {\n node?.clockOffset.addSample(sample);\n },\n });\n\n wsPeer.enable();\n }\n\n if (!accountID) {\n throw new Error(\"No accountID provided\");\n }\n if (!accountSecret) {\n throw new Error(\"No accountSecret provided\");\n }\n if (!accountID.startsWith(\"co_\")) {\n throw new Error(\"Invalid accountID\");\n }\n if (!accountSecret?.startsWith(\"sealerSecret_\")) {\n throw new Error(\"Invalid accountSecret\");\n }\n\n const context = await createJazzContextFromExistingCredentials({\n credentials: {\n accountID: accountID,\n secret: accountSecret as AgentSecret,\n },\n AccountSchema,\n sessionProvider: new MockSessionProvider(),\n peers,\n crypto: options.crypto ?? (await WasmCrypto.create()),\n asActiveAccount,\n storage: options.storage,\n experimental_clockSyncFromServerPings:\n options.experimental_clockSyncFromServerPings,\n });\n\n const account = context.account as InstanceOfSchema<S>;\n node = account.$jazz.localNode;\n\n if (!account.$jazz.refs.profile?.id) {\n throw new Error(\"Account has no profile\");\n }\n\n const inbox = skipInboxLoad ? undefined : await Inbox.load(account);\n\n async function done() {\n await context.account.$jazz.waitForAllCoValuesSync();\n\n wsPeer?.disable();\n context.done();\n }\n\n const inboxPublicApi = inbox\n ? {\n subscribe: inbox.subscribe.bind(inbox) as Inbox[\"subscribe\"],\n }\n : {\n subscribe: () => {},\n };\n\n return {\n /**\n * The worker account instance.\n */\n worker: context.account as Loaded<S>,\n experimental: {\n /**\n * API to subscribe to the inbox messages.\n *\n * More info on the Inbox API: https://jazz.tools/docs/react/server-side/inbox\n */\n inbox: inboxPublicApi,\n },\n /**\n * Wait for the connection to the sync server to be established.\n *\n * If already connected, it will resolve immediately.\n * Returns immediately if using a custom peer.\n */\n waitForConnection() {\n return wsPeer?.waitUntilConnected() ?? Promise.resolve();\n },\n subscribeToConnectionChange(listener: (connected: boolean) => void) {\n if (!wsPeer) {\n // For custom peers, immediately notify as connected\n listener(true);\n return () => {};\n }\n\n wsPeer.subscribe(listener);\n\n return () => {\n wsPeer.unsubscribe(listener);\n };\n },\n /**\n * Waits for all CoValues to sync and then shuts down the worker.\n *\n * To only wait for sync use worker.$jazz.waitForAllCoValuesSync()\n *\n * @deprecated Use shutdownWorker\n */\n done,\n /**\n * Waits for all CoValues to sync and then shuts down the worker.\n *\n * To only wait for sync use worker.$jazz.waitForAllCoValuesSync()\n */\n shutdownWorker() {\n return done();\n },\n };\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -206,7 +206,7 @@
206
206
  },
207
207
  "type": "module",
208
208
  "license": "MIT",
209
- "version": "0.20.17",
209
+ "version": "0.20.18",
210
210
  "dependencies": {
211
211
  "@scure/base": "1.2.1",
212
212
  "@scure/bip39": "^1.3.0",
@@ -223,9 +223,9 @@
223
223
  "prosemirror-transform": "^1.9.0",
224
224
  "use-sync-external-store": "^1.5.0",
225
225
  "zod": "4.1.11",
226
- "cojson": "0.20.17",
227
- "cojson-storage-indexeddb": "0.20.17",
228
- "cojson-transport-ws": "0.20.17"
226
+ "cojson": "0.20.18",
227
+ "cojson-storage-indexeddb": "0.20.18",
228
+ "cojson-transport-ws": "0.20.18"
229
229
  },
230
230
  "devDependencies": {
231
231
  "@scure/bip39": "^1.3.0",
@@ -26,8 +26,10 @@ export const jazzPluginClient = () => {
26
26
  let jazzContext: JazzContextType<Account>;
27
27
  let authSecretStorage: AuthSecretStorage;
28
28
  let signOutUnsubscription: () => void;
29
+ let authGeneration = 0;
29
30
 
30
31
  const authenticateOnJazz = async (jazzAuth: AuthSetPayload) => {
32
+ authGeneration++;
31
33
  const parsedJazzAuth = {
32
34
  ...jazzAuth,
33
35
  secretSeed: jazzAuth.secretSeed
@@ -89,6 +91,12 @@ export const jazzPluginClient = () => {
89
91
  name: "jazz-plugin",
90
92
  hooks: {
91
93
  async onRequest(context) {
94
+ if (context.url.toString().includes("/get-session")) {
95
+ context.headers.set(
96
+ "x-jazz-auth-generation",
97
+ String(authGeneration),
98
+ );
99
+ }
92
100
  if (
93
101
  SIGNUP_URLS.some((url) => context.url.toString().includes(url))
94
102
  ) {
@@ -123,7 +131,13 @@ export const jazzPluginClient = () => {
123
131
 
124
132
  if (context.request.url.toString().includes("/get-session")) {
125
133
  if (context.data === null) {
126
- if (authSecretStorage.isAuthenticated === true) {
134
+ const requestAuthGeneration = Number(
135
+ context.request.headers.get("x-jazz-auth-generation") ?? "0",
136
+ );
137
+ if (
138
+ authSecretStorage.isAuthenticated === true &&
139
+ requestAuthGeneration === authGeneration
140
+ ) {
127
141
  console.info(
128
142
  "Jazz is authenticated, but the session is null. Logging out",
129
143
  );
@@ -610,6 +610,98 @@ describe("Better-Auth client plugin", () => {
610
610
  expect(customFetchImpl).toHaveBeenCalledTimes(3);
611
611
  });
612
612
 
613
+ it("should NOT logout from Jazz if a stale null get-session arrives after authentication", async () => {
614
+ const credentials = await authSecretStorage.get();
615
+ assert(credentials, "Jazz credentials are not available");
616
+
617
+ // This test validates race condition handling where a stale null response
618
+ // arrives after authentication. The authGeneration is tracked via the
619
+ // x-jazz-auth-generation header set in onRequest and read in onSuccess.
620
+ // Since authGeneration increments after sign-in, the stale response's
621
+ // generation number will be older, preventing an unintended logout.
622
+
623
+ const capturedHeaders: any[] = [];
624
+ let resolveStaleSession: (value: Response) => void;
625
+ const staleSessionPromise = new Promise<Response>((resolve) => {
626
+ resolveStaleSession = resolve;
627
+ });
628
+
629
+ customFetchImpl.mockImplementation(
630
+ (urlOrRequest: string | Request, init?: RequestInit) => {
631
+ const headers =
632
+ urlOrRequest instanceof Request
633
+ ? urlOrRequest.headers
634
+ : init?.headers || {};
635
+ capturedHeaders.push(headers);
636
+
637
+ const url =
638
+ urlOrRequest instanceof Request ? urlOrRequest.url : urlOrRequest;
639
+
640
+ if (url.toString().includes("/get-session")) {
641
+ return staleSessionPromise;
642
+ }
643
+ return Promise.resolve(
644
+ new Response(
645
+ JSON.stringify({
646
+ user: {
647
+ id: "user-1",
648
+ email: "test@jazz.dev",
649
+ name: "Matteo",
650
+ },
651
+ jazzAuth: {
652
+ accountID: credentials.accountID,
653
+ secretSeed: credentials.secretSeed,
654
+ accountSecret: credentials.accountSecret,
655
+ provider: "better-auth",
656
+ },
657
+ }),
658
+ ),
659
+ );
660
+ },
661
+ );
662
+
663
+ const staleSessionFetch = authClient.getSession();
664
+
665
+ // We need to wait a tick for onRequest to be called and customFetchImpl to be triggered
666
+ await new Promise((resolve) => setTimeout(resolve, 0));
667
+
668
+ const getHeader = (headers: any, name: string) => {
669
+ if (!headers) return null;
670
+ if (typeof headers.get === "function") return headers.get(name);
671
+ if (Array.isArray(headers)) {
672
+ const pair = (headers as string[][]).find(
673
+ (h) => h[0]?.toLowerCase() === name.toLowerCase(),
674
+ );
675
+ return pair ? pair[1] : null;
676
+ }
677
+ return headers[name] || headers[name.toLowerCase()] || null;
678
+ };
679
+
680
+ // Verify the stale request had authGeneration=0 in the header
681
+ expect(getHeader(capturedHeaders[0], "x-jazz-auth-generation")).toBe("0");
682
+
683
+ // Now sign in, which increments authGeneration to 1
684
+ await authClient.signIn.email({
685
+ email: "test@jazz.dev",
686
+ password: "password",
687
+ });
688
+
689
+ expect(authSecretStorage.isAuthenticated).toBe(true);
690
+
691
+ // Verify the sign-in request had no x-jazz-auth-generation header (it is gated to get-session)
692
+ expect(
693
+ getHeader(capturedHeaders[1], "x-jazz-auth-generation"),
694
+ ).toBeNull();
695
+
696
+ // Resolve the stale get-session response with null
697
+ resolveStaleSession!(new Response(JSON.stringify(null)));
698
+ await staleSessionFetch;
699
+
700
+ // The stale null response should not trigger a logout because its
701
+ // authGeneration (0) < current authGeneration (1), so >= comparison fails
702
+ expect(authSecretStorage.isAuthenticated).toBe(true);
703
+ });
704
+
613
705
  it("should deduplicate auth requests for the same account", async () => {
614
706
  const credentials = await authSecretStorage.get();
615
707
  assert(credentials, "Jazz credentials are not available");
@@ -31,6 +31,7 @@ export type JazzContextManagerProps<
31
31
  storage?: BaseBrowserContextOptions["storage"];
32
32
  AccountSchema?: S;
33
33
  defaultProfileName?: string;
34
+ experimental_clockSyncFromServerPings?: boolean;
34
35
  };
35
36
 
36
37
  export class JazzBrowserContextManager<
@@ -57,6 +58,8 @@ export class JazzBrowserContextManager<
57
58
  sync: props.sync,
58
59
  storage: props.storage,
59
60
  authSecretStorage: this.authSecretStorage,
61
+ experimental_clockSyncFromServerPings:
62
+ props.experimental_clockSyncFromServerPings,
60
63
  });
61
64
  } else {
62
65
  return createJazzBrowserContext<S>({
@@ -67,6 +70,8 @@ export class JazzBrowserContextManager<
67
70
  newAccountProps: authProps?.newAccountProps,
68
71
  defaultProfileName: props.defaultProfileName,
69
72
  authSecretStorage: this.authSecretStorage,
73
+ experimental_clockSyncFromServerPings:
74
+ props.experimental_clockSyncFromServerPings,
70
75
  });
71
76
  }
72
77
  }
@@ -30,6 +30,7 @@ export type BaseBrowserContextOptions = {
30
30
  storage?: "indexedDB";
31
31
  crypto?: CryptoProvider;
32
32
  authSecretStorage: AuthSecretStorage;
33
+ experimental_clockSyncFromServerPings?: boolean;
33
34
  };
34
35
 
35
36
  class BrowserWebSocketPeerWithReconnection extends WebSocketPeerWithReconnection {
@@ -79,6 +80,9 @@ async function setupPeers(options: BaseBrowserContextOptions) {
79
80
  removePeer: (peer) => {
80
81
  peers.splice(peers.indexOf(peer), 1);
81
82
  },
83
+ onPingReceived: (sample) => {
84
+ node?.clockOffset.addSample(sample);
85
+ },
82
86
  });
83
87
 
84
88
  function toggleNetwork(enabled: boolean) {
@@ -136,6 +140,8 @@ export async function createJazzBrowserGuestContext(
136
140
  peers,
137
141
  syncWhen,
138
142
  storage,
143
+ experimental_clockSyncFromServerPings:
144
+ options.experimental_clockSyncFromServerPings,
139
145
  });
140
146
 
141
147
  setNode(context.agent.node);
@@ -214,6 +220,8 @@ export async function createJazzBrowserContext<
214
220
  AccountSchema: options.AccountSchema,
215
221
  sessionProvider: getBrowserLockSessionProvider(),
216
222
  authSecretStorage: options.authSecretStorage,
223
+ experimental_clockSyncFromServerPings:
224
+ options.experimental_clockSyncFromServerPings,
217
225
  });
218
226
 
219
227
  setNode(context.node);
@@ -47,6 +47,7 @@ export function JazzReactProvider<
47
47
  enableSSR,
48
48
  fallback = null,
49
49
  authSecretStorageKey,
50
+ experimental_clockSyncFromServerPings,
50
51
  }: JazzProviderProps<S>) {
51
52
  if (useContext(JazzContext)) {
52
53
  throw new Error(
@@ -82,8 +83,15 @@ export function JazzReactProvider<
82
83
  onAnonymousAccountDiscarded: onAnonymousAccountDiscarded
83
84
  ? onAnonymousAccountDiscardedRefCallback
84
85
  : undefined,
86
+ experimental_clockSyncFromServerPings,
85
87
  } satisfies JazzContextManagerProps<S>;
86
- }, [guestMode, sync.peer, sync.when, storage]);
88
+ }, [
89
+ guestMode,
90
+ sync.peer,
91
+ sync.when,
92
+ storage,
93
+ experimental_clockSyncFromServerPings,
94
+ ]);
87
95
 
88
96
  if (contextManager.propsChanged(props) && typeof window !== "undefined") {
89
97
  contextManager.createContext(props).catch((error) => {
@@ -33,6 +33,7 @@ export type JazzContextManagerProps<
33
33
  onAnonymousAccountDiscarded?: (
34
34
  anonymousAccount: InstanceOfSchema<S>,
35
35
  ) => Promise<void>;
36
+ experimental_clockSyncFromServerPings?: boolean;
36
37
  };
37
38
 
38
39
  export class ReactNativeContextManager<
@@ -49,6 +50,8 @@ export class ReactNativeContextManager<
49
50
  sync: props.sync,
50
51
  storage: props.storage,
51
52
  authSecretStorage: this.authSecretStorage,
53
+ experimental_clockSyncFromServerPings:
54
+ props.experimental_clockSyncFromServerPings,
52
55
  });
53
56
  } else {
54
57
  return createJazzReactNativeContext<S>({
@@ -59,6 +62,8 @@ export class ReactNativeContextManager<
59
62
  newAccountProps: authProps?.newAccountProps,
60
63
  defaultProfileName: props.defaultProfileName,
61
64
  authSecretStorage: this.authSecretStorage,
65
+ experimental_clockSyncFromServerPings:
66
+ props.experimental_clockSyncFromServerPings,
62
67
  });
63
68
  }
64
69
  }
@@ -26,6 +26,7 @@ export type BaseReactNativeContextOptions = {
26
26
  reconnectionTimeout?: number;
27
27
  storage?: SQLiteDatabaseDriverAsync | "disabled";
28
28
  authSecretStorage: AuthSecretStorage;
29
+ experimental_clockSyncFromServerPings?: boolean;
29
30
  };
30
31
 
31
32
  class ReactNativeWebSocketPeerWithReconnection extends WebSocketPeerWithReconnection {
@@ -73,6 +74,9 @@ async function setupPeers(options: BaseReactNativeContextOptions) {
73
74
  removePeer: (peer) => {
74
75
  peers.splice(peers.indexOf(peer), 1);
75
76
  },
77
+ onPingReceived: (sample) => {
78
+ node?.clockOffset.addSample(sample);
79
+ },
76
80
  });
77
81
 
78
82
  function toggleNetwork(enabled: boolean) {
@@ -128,6 +132,8 @@ export async function createJazzReactNativeGuestContext(
128
132
  peers,
129
133
  syncWhen,
130
134
  storage,
135
+ experimental_clockSyncFromServerPings:
136
+ options.experimental_clockSyncFromServerPings,
131
137
  });
132
138
 
133
139
  setNode(context.agent.node);
@@ -211,6 +217,8 @@ export async function createJazzReactNativeContext<
211
217
  sessionProvider,
212
218
  authSecretStorage: options.authSecretStorage,
213
219
  storage,
220
+ experimental_clockSyncFromServerPings:
221
+ options.experimental_clockSyncFromServerPings,
214
222
  });
215
223
 
216
224
  setNode(context.node);
@@ -47,6 +47,7 @@ export function JazzProviderCore<
47
47
  kvStore,
48
48
  authSecretStorageKey,
49
49
  fallback = null,
50
+ experimental_clockSyncFromServerPings,
50
51
  }: JazzProviderProps<S>) {
51
52
  if (useContext(JazzContext)) {
52
53
  throw new Error(
@@ -80,8 +81,15 @@ export function JazzProviderCore<
80
81
  onAnonymousAccountDiscarded: onAnonymousAccountDiscarded
81
82
  ? onAnonymousAccountDiscardedRefCallback
82
83
  : undefined,
84
+ experimental_clockSyncFromServerPings,
83
85
  } satisfies JazzContextManagerProps<S>;
84
- }, [guestMode, sync.peer, sync.when, storage]);
86
+ }, [
87
+ guestMode,
88
+ sync.peer,
89
+ sync.when,
90
+ storage,
91
+ experimental_clockSyncFromServerPings,
92
+ ]);
85
93
 
86
94
  if (contextManager.propsChanged(props)) {
87
95
  contextManager.createContext(props).catch((error) => {
@@ -77,6 +77,7 @@
77
77
  props.sync.peer;
78
78
  props.storage;
79
79
  props.guestMode;
80
+ props.experimental_clockSyncFromServerPings;
80
81
  return untrack(() => {
81
82
  if (!props.sync) return;
82
83
 
@@ -89,6 +90,8 @@
89
90
  defaultProfileName: props.defaultProfileName,
90
91
  onAnonymousAccountDiscarded: props.onAnonymousAccountDiscarded,
91
92
  onLogOut: props.onLogOut,
93
+ experimental_clockSyncFromServerPings:
94
+ props.experimental_clockSyncFromServerPings,
92
95
  })
93
96
  .catch((error) => {
94
97
  console.error("Error creating Jazz browser context:", error);
@@ -22,6 +22,19 @@ import {
22
22
  subscribeToCoValueWithoutMe,
23
23
  } from "../internal.js";
24
24
 
25
+ /**
26
+ * Thrown by a discriminated union's `fromRaw` when the stored value doesn't
27
+ * match any declared variant. Caught at the subscription layer so that
28
+ * `load()` can settle as unavailable instead of hanging, since the throw
29
+ * would otherwise disappear into cojson's async update loop.
30
+ */
31
+ export class SchemaUnionNoMatchingVariantError extends Error {
32
+ constructor(message: string) {
33
+ super(message);
34
+ this.name = "SchemaUnionNoMatchingVariantError";
35
+ }
36
+ }
37
+
25
38
  /**
26
39
  * Extends `SchemaUnion` with a non-abstract constructor.
27
40
  */
@@ -118,6 +118,7 @@ export async function createJazzContextFromExistingCredentials<
118
118
  sessionProvider,
119
119
  onLogOut,
120
120
  asActiveAccount,
121
+ experimental_clockSyncFromServerPings,
121
122
  }: {
122
123
  credentials: Credentials;
123
124
  peers: Peer[];
@@ -128,6 +129,7 @@ export async function createJazzContextFromExistingCredentials<
128
129
  onLogOut?: () => void;
129
130
  storage?: StorageAPI;
130
131
  asActiveAccount: boolean;
132
+ experimental_clockSyncFromServerPings?: boolean;
131
133
  }): Promise<JazzContextWithAccount<InstanceOfSchema<S>>> {
132
134
  const { sessionID, sessionDone } = await sessionProvider.acquireSession(
133
135
  credentials.accountID,
@@ -149,6 +151,7 @@ export async function createJazzContextFromExistingCredentials<
149
151
  crypto,
150
152
  storage,
151
153
  enableFullStorageReconciliation: !!storage,
154
+ experimental_clockSyncFromServerPings,
152
155
  migration: async (rawAccount, _node, creationProps) => {
153
156
  const account = AccountClass.fromRaw(rawAccount) as InstanceOfSchema<S>;
154
157
  if (asActiveAccount) {
@@ -193,6 +196,7 @@ export async function createJazzContextForNewAccount<
193
196
  onLogOut,
194
197
  storage,
195
198
  sessionProvider,
199
+ experimental_clockSyncFromServerPings,
196
200
  }: {
197
201
  creationProps: { name: string };
198
202
  initialAgentSecret?: AgentSecret;
@@ -203,6 +207,7 @@ export async function createJazzContextForNewAccount<
203
207
  onLogOut?: () => Promise<void>;
204
208
  storage?: StorageAPI;
205
209
  sessionProvider: SessionProvider;
210
+ experimental_clockSyncFromServerPings?: boolean;
206
211
  }): Promise<JazzContextWithAccount<InstanceOfSchema<S>>> {
207
212
  const CurrentAccountSchema =
208
213
  PropsAccountSchema ?? (RegisteredSchemas["Account"] as unknown as S);
@@ -218,6 +223,7 @@ export async function createJazzContextForNewAccount<
218
223
  initialAgentSecret,
219
224
  storage,
220
225
  enableFullStorageReconciliation: !!storage,
226
+ experimental_clockSyncFromServerPings,
221
227
  migration: async (rawAccount, _node, creationProps) => {
222
228
  const account = AccountClass.fromRaw(rawAccount) as InstanceOfSchema<S>;
223
229
  activeAccountContext.set(account);
@@ -263,6 +269,7 @@ export async function createJazzContext<
263
269
  sessionProvider: SessionProvider;
264
270
  authSecretStorage: AuthSecretStorage;
265
271
  storage?: StorageAPI;
272
+ experimental_clockSyncFromServerPings?: boolean;
266
273
  }) {
267
274
  const crypto = options.crypto;
268
275
 
@@ -294,6 +301,8 @@ export async function createJazzContext<
294
301
  },
295
302
  storage: options.storage,
296
303
  asActiveAccount: true,
304
+ experimental_clockSyncFromServerPings:
305
+ options.experimental_clockSyncFromServerPings,
297
306
  });
298
307
  } else {
299
308
  const secretSeed = options.crypto.newRandomSecretSeed();
@@ -318,6 +327,8 @@ export async function createJazzContext<
318
327
  await authSecretStorage.clearWithoutNotify();
319
328
  },
320
329
  storage: options.storage,
330
+ experimental_clockSyncFromServerPings:
331
+ options.experimental_clockSyncFromServerPings,
321
332
  });
322
333
 
323
334
  if (!options.newAccountProps) {
@@ -341,11 +352,13 @@ export function createAnonymousJazzContext({
341
352
  syncWhen,
342
353
  crypto,
343
354
  storage,
355
+ experimental_clockSyncFromServerPings,
344
356
  }: {
345
357
  peers: Peer[];
346
358
  syncWhen?: SyncWhen;
347
359
  crypto: CryptoProvider;
348
360
  storage?: StorageAPI;
361
+ experimental_clockSyncFromServerPings?: boolean;
349
362
  }): JazzContextWithAgent {
350
363
  const agentSecret = crypto.newRandomAgentSecret();
351
364
 
@@ -355,6 +368,7 @@ export function createAnonymousJazzContext({
355
368
  crypto,
356
369
  syncWhen,
357
370
  !!storage,
371
+ { experimental_clockSyncFromServerPings },
358
372
  );
359
373
 
360
374
  for (const peer of peers) {
@@ -524,11 +524,19 @@ export class CoMapSchema<
524
524
  Owner,
525
525
  DefaultResolveQuery
526
526
  > {
527
+ const extendedShape = {} as z.core.util.Extend<Shape, ExtendShape>;
528
+
529
+ Object.defineProperties(
530
+ extendedShape,
531
+ Object.getOwnPropertyDescriptors(this.shape),
532
+ );
533
+ Object.defineProperties(
534
+ extendedShape,
535
+ Object.getOwnPropertyDescriptors(shape),
536
+ );
537
+
527
538
  return this.copy({
528
- shape: {
529
- ...this.shape,
530
- ...shape,
531
- } as z.core.util.Extend<Shape, ExtendShape>,
539
+ shape: extendedShape,
532
540
  });
533
541
  }
534
542
 
@@ -549,11 +557,19 @@ export class CoMapSchema<
549
557
  Owner,
550
558
  DefaultResolveQuery
551
559
  > {
560
+ const extendedShape = {} as z.core.util.Extend<Shape, ExtendShape>;
561
+
562
+ Object.defineProperties(
563
+ extendedShape,
564
+ Object.getOwnPropertyDescriptors(this.shape),
565
+ );
566
+ Object.defineProperties(
567
+ extendedShape,
568
+ Object.getOwnPropertyDescriptors(shape),
569
+ );
570
+
552
571
  return this.copy({
553
- shape: {
554
- ...this.shape,
555
- ...shape,
556
- } as z.core.util.Extend<Shape, ExtendShape>,
572
+ shape: extendedShape,
557
573
  });
558
574
  }
559
575
 
@@ -8,6 +8,7 @@ import {
8
8
  DiscriminableCoValueSchemas,
9
9
  DiscriminableCoreCoValueSchema,
10
10
  SchemaUnionDiscriminator,
11
+ SchemaUnionNoMatchingVariantError,
11
12
  createCoreCoMapSchema,
12
13
  } from "../../internal.js";
13
14
  import {
@@ -118,7 +119,7 @@ export function schemaUnionDiscriminatorFor(
118
119
  }
119
120
  }
120
121
 
121
- throw new Error(
122
+ throw new SchemaUnionNoMatchingVariantError(
122
123
  "co.discriminatedUnion() of collaborative types with no matching discriminator value found",
123
124
  );
124
125
  };
@@ -8,6 +8,7 @@ import {
8
8
  NotLoaded,
9
9
  type RefEncoded,
10
10
  type RefsToResolve,
11
+ SchemaUnionNoMatchingVariantError,
11
12
  TypeSym,
12
13
  createUnloadedCoValue,
13
14
  instantiateRefEncodedFromRaw,
@@ -173,7 +174,28 @@ export class SubscriptionScope<D extends CoValue> {
173
174
  }
174
175
 
175
176
  this.migrating = true;
176
- const instance = instantiateRefEncodedFromRaw(this.schema, value);
177
+ let instance: CoValue;
178
+ try {
179
+ instance = instantiateRefEncodedFromRaw(this.schema, value);
180
+ } catch (error) {
181
+ // A discriminated union whose stored value doesn't match any
182
+ // declared variant would otherwise hang load() — the throw
183
+ // escapes into cojson's async update loop and nothing settles.
184
+ // Treat it as unavailable so load() resolves. Other
185
+ // instantiation errors (e.g. CoVector dimension mismatch) keep
186
+ // throwing loudly, as they indicate schema drift that callers
187
+ // should surface.
188
+ if (!(error instanceof SchemaUnionNoMatchingVariantError)) {
189
+ throw error;
190
+ }
191
+ this.migrationFailed = true;
192
+ this.migrated = true;
193
+ console.error(
194
+ `Schema instantiation failed for ${this.id}: ${error.message}`,
195
+ );
196
+ this.handleUpdate(CoValueLoadingState.UNAVAILABLE);
197
+ return;
198
+ }
177
199
  try {
178
200
  applyCoValueMigrations(instance);
179
201
  } catch (error) {