jazz-tools 0.20.17 → 0.20.19

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 (104) 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 +85 -85
  5. package/CHANGELOG.md +28 -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 +47 -13
  14. package/dist/browser/index.js.map +1 -1
  15. package/dist/browser/provideBrowserLockSession/BrowserSessionDurabilityMarker.d.ts +8 -0
  16. package/dist/browser/provideBrowserLockSession/BrowserSessionDurabilityMarker.d.ts.map +1 -0
  17. package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.d.ts +1 -0
  18. package/dist/browser/provideBrowserLockSession/BrowserSessionProvider.d.ts.map +1 -1
  19. package/dist/browser/provideBrowserLockSession/index.d.ts +1 -0
  20. package/dist/browser/provideBrowserLockSession/index.d.ts.map +1 -1
  21. package/dist/{chunk-3BV3JUMV.js → chunk-VPK4IGIA.js} +113 -17
  22. package/dist/chunk-VPK4IGIA.js.map +1 -0
  23. package/dist/expo/index.js +22 -7
  24. package/dist/expo/index.js.map +1 -1
  25. package/dist/expo/storage/expo-sqlite-adapter.d.ts +9 -0
  26. package/dist/expo/storage/expo-sqlite-adapter.d.ts.map +1 -1
  27. package/dist/index.js +5 -1
  28. package/dist/index.js.map +1 -1
  29. package/dist/react/index.js +11 -3
  30. package/dist/react/index.js.map +1 -1
  31. package/dist/react/provider.d.ts +1 -1
  32. package/dist/react/provider.d.ts.map +1 -1
  33. package/dist/react-native/index.js +64 -11
  34. package/dist/react-native/index.js.map +1 -1
  35. package/dist/react-native-core/ReactNativeContextManager.d.ts +1 -0
  36. package/dist/react-native-core/ReactNativeContextManager.d.ts.map +1 -1
  37. package/dist/react-native-core/ReactNativeSessionDurabilityMarker.d.ts +8 -0
  38. package/dist/react-native-core/ReactNativeSessionDurabilityMarker.d.ts.map +1 -0
  39. package/dist/react-native-core/ReactNativeSessionProvider.d.ts +1 -0
  40. package/dist/react-native-core/ReactNativeSessionProvider.d.ts.map +1 -1
  41. package/dist/react-native-core/index.d.ts +1 -0
  42. package/dist/react-native-core/index.d.ts.map +1 -1
  43. package/dist/react-native-core/index.js +64 -11
  44. package/dist/react-native-core/index.js.map +1 -1
  45. package/dist/react-native-core/platform.d.ts +1 -0
  46. package/dist/react-native-core/platform.d.ts.map +1 -1
  47. package/dist/react-native-core/provider.d.ts +1 -1
  48. package/dist/react-native-core/provider.d.ts.map +1 -1
  49. package/dist/svelte/Provider.svelte +3 -0
  50. package/dist/svelte/Provider.svelte.d.ts.map +1 -1
  51. package/dist/testing.js +1 -1
  52. package/dist/tools/coValues/schemaUnion.d.ts +9 -0
  53. package/dist/tools/coValues/schemaUnion.d.ts.map +1 -1
  54. package/dist/tools/exports.d.ts +1 -1
  55. package/dist/tools/exports.d.ts.map +1 -1
  56. package/dist/tools/implementation/createContext.d.ts +15 -3
  57. package/dist/tools/implementation/createContext.d.ts.map +1 -1
  58. package/dist/tools/implementation/sessionDurabilityMarker.d.ts +38 -0
  59. package/dist/tools/implementation/sessionDurabilityMarker.d.ts.map +1 -0
  60. package/dist/tools/implementation/zodSchema/schemaTypes/CoMapSchema.d.ts.map +1 -1
  61. package/dist/tools/implementation/zodSchema/unionUtils.d.ts.map +1 -1
  62. package/dist/tools/internal.d.ts +1 -0
  63. package/dist/tools/internal.d.ts.map +1 -1
  64. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  65. package/dist/tools/tests/sessionDurabilityMarker.test.d.ts +2 -0
  66. package/dist/tools/tests/sessionDurabilityMarker.test.d.ts.map +1 -0
  67. package/dist/worker/index.d.ts +1 -0
  68. package/dist/worker/index.d.ts.map +1 -1
  69. package/dist/worker/index.js +6 -2
  70. package/dist/worker/index.js.map +1 -1
  71. package/package.json +4 -4
  72. package/src/better-auth/auth/client.ts +15 -1
  73. package/src/better-auth/auth/tests/client.test.ts +92 -0
  74. package/src/browser/BrowserContextManager.ts +5 -0
  75. package/src/browser/createBrowserContext.ts +8 -0
  76. package/src/browser/provideBrowserLockSession/BrowserSessionDurabilityMarker.ts +20 -0
  77. package/src/browser/provideBrowserLockSession/BrowserSessionProvider.test.ts +97 -1
  78. package/src/browser/provideBrowserLockSession/BrowserSessionProvider.ts +37 -8
  79. package/src/browser/provideBrowserLockSession/index.ts +1 -0
  80. package/src/expo/storage/expo-sqlite-adapter.ts +24 -7
  81. package/src/expo/tests/expo-sqlite-adapter.test.ts +94 -0
  82. package/src/react/provider.tsx +9 -1
  83. package/src/react-native-core/ReactNativeContextManager.ts +5 -0
  84. package/src/react-native-core/ReactNativeSessionDurabilityMarker.ts +40 -0
  85. package/src/react-native-core/ReactNativeSessionProvider.ts +24 -1
  86. package/src/react-native-core/index.ts +1 -0
  87. package/src/react-native-core/platform.ts +8 -0
  88. package/src/react-native-core/provider.tsx +9 -1
  89. package/src/react-native-core/tests/ReactNativeSessionProvider.test.ts +42 -0
  90. package/src/svelte/Provider.svelte +3 -0
  91. package/src/tools/coValues/schemaUnion.ts +13 -0
  92. package/src/tools/exports.ts +3 -0
  93. package/src/tools/implementation/createContext.ts +44 -0
  94. package/src/tools/implementation/sessionDurabilityMarker.ts +93 -0
  95. package/src/tools/implementation/zodSchema/schemaTypes/CoMapSchema.ts +24 -8
  96. package/src/tools/implementation/zodSchema/unionUtils.ts +2 -1
  97. package/src/tools/internal.ts +1 -0
  98. package/src/tools/subscribe/SubscriptionScope.ts +23 -1
  99. package/src/tools/tests/coMap.test.ts +30 -0
  100. package/src/tools/tests/createContext.test.ts +64 -0
  101. package/src/tools/tests/schemaUnion.test.ts +19 -0
  102. package/src/tools/tests/sessionDurabilityMarker.test.ts +183 -0
  103. package/src/worker/index.ts +6 -0
  104. package/dist/chunk-3BV3JUMV.js.map +0 -1
@@ -219,12 +219,34 @@ var SessionIDStorage = class {
219
219
  }
220
220
  };
221
221
 
222
+ // src/browser/provideBrowserLockSession/BrowserSessionDurabilityMarker.ts
223
+ import { sessionDurabilityMarkerKey as markerKey } from "jazz-tools";
224
+ var BrowserSessionDurabilityMarker = {
225
+ set(sessionID) {
226
+ localStorage.setItem(markerKey(sessionID), "1");
227
+ },
228
+ clear(sessionID) {
229
+ localStorage.removeItem(markerKey(sessionID));
230
+ },
231
+ isSet(sessionID) {
232
+ return localStorage.getItem(markerKey(sessionID)) !== null;
233
+ }
234
+ };
235
+
222
236
  // src/browser/provideBrowserLockSession/BrowserSessionProvider.ts
223
237
  var BrowserSessionProvider = class {
238
+ constructor() {
239
+ this.durabilityMarker = BrowserSessionDurabilityMarker;
240
+ }
224
241
  async acquireSession(accountID, crypto) {
225
242
  const { sessionPromise, resolveSession } = createSessionLockPromise();
226
243
  const sessionsList = SessionIDStorage.getSessionsList(accountID);
244
+ let dirtySlot;
227
245
  for (const [index, sessionID] of sessionsList.entries()) {
246
+ if (BrowserSessionDurabilityMarker.isSet(sessionID)) {
247
+ dirtySlot ??= { index, sessionID };
248
+ continue;
249
+ }
228
250
  const sessionAcquired = await tryToAcquireSession(
229
251
  sessionID,
230
252
  sessionPromise
@@ -240,7 +262,12 @@ var BrowserSessionProvider = class {
240
262
  const newSessionID = crypto.newRandomSessionID(
241
263
  accountID
242
264
  );
243
- await lockAndStoreSession(accountID, newSessionID, sessionPromise);
265
+ await lockAndStoreSession(
266
+ accountID,
267
+ newSessionID,
268
+ sessionPromise,
269
+ dirtySlot
270
+ );
244
271
  console.log("Created new session", newSessionID);
245
272
  return {
246
273
  sessionID: newSessionID,
@@ -254,12 +281,12 @@ var BrowserSessionProvider = class {
254
281
  return { sessionDone: resolveSession };
255
282
  }
256
283
  };
257
- async function lockAndStoreSession(accountID, sessionID, sessionPromise) {
284
+ async function lockAndStoreSession(accountID, sessionID, sessionPromise, replaceSlot) {
258
285
  const sessionAcquired = await tryToAcquireSession(sessionID, sessionPromise);
259
286
  if (!sessionAcquired) {
260
287
  throw new Error("Couldn't get lock on new session");
261
288
  }
262
- storeSessionID(accountID, sessionID);
289
+ storeSessionID(accountID, sessionID, replaceSlot);
263
290
  }
264
291
  function tryToAcquireSession(sessionID, sessionDonePromise) {
265
292
  return new Promise((resolve) => {
@@ -277,7 +304,7 @@ function tryToAcquireSession(sessionID, sessionDonePromise) {
277
304
  );
278
305
  });
279
306
  }
280
- function storeSessionID(accountID, sessionID) {
307
+ function storeSessionID(accountID, sessionID, replaceSlot) {
281
308
  return navigator.locks.request(
282
309
  `store_session_${accountID}`,
283
310
  { mode: "exclusive" },
@@ -286,11 +313,11 @@ function storeSessionID(accountID, sessionID) {
286
313
  console.error("Couldn't get lock to store session ID", accountID);
287
314
  }
288
315
  const sessionsList = SessionIDStorage.getSessionsList(accountID);
289
- SessionIDStorage.storeSessionID(
290
- accountID,
291
- sessionID,
292
- sessionsList.length
293
- );
316
+ const index = replaceSlot && sessionsList[replaceSlot.index] === replaceSlot.sessionID ? replaceSlot.index : sessionsList.length;
317
+ SessionIDStorage.storeSessionID(accountID, sessionID, index);
318
+ if (replaceSlot) {
319
+ BrowserSessionDurabilityMarker.clear(replaceSlot.sessionID);
320
+ }
294
321
  }
295
322
  );
296
323
  }
@@ -358,6 +385,9 @@ async function setupPeers(options) {
358
385
  },
359
386
  removePeer: (peer) => {
360
387
  peers.splice(peers.indexOf(peer), 1);
388
+ },
389
+ onPingReceived: (sample) => {
390
+ node?.clockOffset.addSample(sample);
361
391
  }
362
392
  });
363
393
  function toggleNetwork(enabled) {
@@ -406,7 +436,8 @@ async function createJazzBrowserGuestContext(options) {
406
436
  crypto,
407
437
  peers,
408
438
  syncWhen,
409
- storage
439
+ storage,
440
+ experimental_clockSyncFromServerPings: options.experimental_clockSyncFromServerPings
410
441
  });
411
442
  setNode(context.agent.node);
412
443
  options.authSecretStorage.emitUpdate(null);
@@ -461,7 +492,8 @@ async function createJazzBrowserContext(options) {
461
492
  defaultProfileName: options.defaultProfileName,
462
493
  AccountSchema: options.AccountSchema,
463
494
  sessionProvider: getBrowserLockSessionProvider(),
464
- authSecretStorage: options.authSecretStorage
495
+ authSecretStorage: options.authSecretStorage,
496
+ experimental_clockSyncFromServerPings: options.experimental_clockSyncFromServerPings
465
497
  });
466
498
  setNode(context.node);
467
499
  return {
@@ -521,7 +553,8 @@ var JazzBrowserContextManager = class extends JazzContextManager {
521
553
  return createJazzBrowserGuestContext({
522
554
  sync: props.sync,
523
555
  storage: props.storage,
524
- authSecretStorage: this.authSecretStorage
556
+ authSecretStorage: this.authSecretStorage,
557
+ experimental_clockSyncFromServerPings: props.experimental_clockSyncFromServerPings
525
558
  });
526
559
  } else {
527
560
  return createJazzBrowserContext({
@@ -531,7 +564,8 @@ var JazzBrowserContextManager = class extends JazzContextManager {
531
564
  credentials: authProps?.credentials,
532
565
  newAccountProps: authProps?.newAccountProps,
533
566
  defaultProfileName: props.defaultProfileName,
534
- authSecretStorage: this.authSecretStorage
567
+ authSecretStorage: this.authSecretStorage,
568
+ experimental_clockSyncFromServerPings: props.experimental_clockSyncFromServerPings
535
569
  });
536
570
  }
537
571
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/browser/index.ts","../../src/browser/utils/export-account-inspector.ts","../../src/browser/auth/PasskeyAuth.ts","../../src/browser/createBrowserContext.ts","../../src/browser/provideBrowserLockSession/index.ts","../../src/browser/provideBrowserLockSession/SessionIDStorage.ts","../../src/browser/provideBrowserLockSession/BrowserSessionProvider.ts","../../src/browser/BrowserContextManager.ts","../../src/browser/auth/LocalStorageKVStore.ts"],"sourcesContent":["import { AccountRole } from \"cojson\";\nimport {\n Account,\n CoValue,\n CoValueClassOrSchema,\n InviteSecret,\n createInviteLink as baseCreateInviteLink,\n consumeInviteLink,\n} from \"jazz-tools\";\nimport { setupInspector } from \"./utils/export-account-inspector.js\";\nexport { BrowserPasskeyAuth } from \"./auth/PasskeyAuth.js\";\n\nsetupInspector();\n\nexport * from \"./createBrowserContext.js\";\nexport * from \"./BrowserContextManager.js\";\n\nexport { LocalStorageKVStore } from \"./auth/LocalStorageKVStore.js\";\n\n/** @category Invite Links */\nexport function createInviteLink<C extends CoValue>(\n value: C,\n role: AccountRole,\n // default to same address as window.location, but without hash\n {\n baseURL = window.location.href.replace(/#.*$/, \"\"),\n valueHint,\n }: { baseURL?: string; valueHint?: string } = {},\n): string {\n return baseCreateInviteLink(value, role, { baseURL, valueHint });\n}\n\n/** @category Invite Links */\nexport { parseInviteLink } from \"jazz-tools\";\n\n/** @category Invite Links */\nexport async function consumeInviteLinkFromWindowLocation<\n S extends CoValueClassOrSchema,\n>({\n as,\n forValueHint,\n invitedObjectSchema,\n}: {\n as?: Account;\n forValueHint?: string;\n invitedObjectSchema: S;\n}): Promise<\n | {\n valueID: string;\n valueHint?: string;\n inviteSecret: InviteSecret;\n }\n | undefined\n> {\n const result = await consumeInviteLink({\n inviteURL: window.location.href,\n as,\n forValueHint,\n invitedObjectSchema,\n });\n\n if (result) {\n window.history.replaceState(\n {},\n \"\",\n window.location.href.replace(/#.*$/, \"\"),\n );\n }\n\n return result;\n}\n","import { AuthSecretStorage } from \"jazz-tools\";\n\nasync function exportAccountToInspector() {\n const authSecretStorage = new AuthSecretStorage();\n const localStorageData = await authSecretStorage.get();\n\n if (!localStorageData) {\n console.error(\"No account data found in localStorage\");\n return;\n }\n\n const encodedAccountSecret = btoa(localStorageData.accountSecret);\n window.open(\n new URL(\n `#/import/${localStorageData?.accountID}/${encodedAccountSecret}`,\n \"https://inspector.jazz.tools\",\n ).toString(),\n \"_blank\",\n );\n}\n\nfunction listenForCmdJ() {\n if (typeof window === \"undefined\") return;\n\n const cb = (e: any) => {\n if (e.metaKey && e.key === \"j\") {\n if (\n confirm(\n \"Are you sure you want to inspect your account using inspector.jazz.tools? This lets anyone with the secret inspector URL read your data and impersonate you.\",\n )\n ) {\n exportAccountToInspector();\n }\n }\n };\n\n window.addEventListener(\"keydown\", cb);\n\n return () => {\n window.removeEventListener(\"keydown\", cb);\n };\n}\n\n/**\n * Automatically sets up the Cmd+J listener if 'allowJazzInspector' is present in the URL\n * @returns A cleanup function if the listener was set up, undefined otherwise\n */\nexport function setupInspector() {\n if (typeof window === \"undefined\") return;\n\n const url = new URL(window.location.href);\n if (\n url.hash.includes(\"allowJazzInspector\") ||\n process.env.NODE_ENV === \"development\"\n ) {\n return listenForCmdJ();\n }\n}\n","import { CryptoProvider, RawAccountID, cojsonInternals } from \"cojson\";\nimport {\n Account,\n AuthSecretStorage,\n AuthenticateAccountFunction,\n ID,\n} from \"jazz-tools\";\n\n/**\n * `BrowserPasskeyAuth` provides a `JazzAuth` object for passkey authentication.\n *\n * ```ts\n * import { BrowserPasskeyAuth } from \"jazz-tools/browser\";\n *\n * const auth = new BrowserPasskeyAuth(driver, appName);\n * ```\n *\n * @category Auth Providers\n */\nexport class BrowserPasskeyAuth {\n constructor(\n protected crypto: CryptoProvider,\n protected authenticate: AuthenticateAccountFunction,\n protected authSecretStorage: AuthSecretStorage,\n public appName: string,\n public appHostname: string = window.location.hostname,\n ) {}\n\n static readonly id = \"passkey\";\n\n logIn = async () => {\n const { crypto, authenticate } = this;\n\n const webAuthNCredential = await this.getPasskeyCredentials();\n\n if (!webAuthNCredential) {\n return;\n }\n\n const webAuthNCredentialPayload = new Uint8Array(\n webAuthNCredential.response.userHandle,\n );\n const accountSecretSeed = webAuthNCredentialPayload.slice(\n 0,\n cojsonInternals.secretSeedLength,\n );\n\n const secret = crypto.agentSecretFromSecretSeed(accountSecretSeed);\n\n const accountID = cojsonInternals.rawCoIDfromBytes(\n webAuthNCredentialPayload.slice(\n cojsonInternals.secretSeedLength,\n cojsonInternals.secretSeedLength + cojsonInternals.shortHashLength,\n ),\n ) as ID<Account>;\n\n await authenticate({\n accountID,\n accountSecret: secret,\n });\n\n await this.authSecretStorage.set({\n accountID,\n secretSeed: accountSecretSeed,\n accountSecret: secret,\n provider: \"passkey\",\n });\n };\n\n signUp = async (username: string) => {\n const credentials = await this.authSecretStorage.get();\n\n if (!credentials?.secretSeed) {\n throw new Error(\n \"Not enough credentials to register the account with passkey\",\n );\n }\n\n await this.createPasskeyCredentials({\n accountID: credentials.accountID,\n secretSeed: credentials.secretSeed,\n username,\n });\n\n const currentAccount = await Account.getMe().$jazz.ensureLoaded({\n resolve: {\n profile: true,\n },\n });\n\n if (username.trim().length !== 0) {\n currentAccount.profile.$jazz.set(\"name\", username);\n }\n\n await this.authSecretStorage.set({\n accountID: credentials.accountID,\n secretSeed: credentials.secretSeed,\n accountSecret: credentials.accountSecret,\n provider: \"passkey\",\n });\n };\n\n private async createPasskeyCredentials({\n accountID,\n secretSeed,\n username,\n }: {\n accountID: ID<Account>;\n secretSeed: Uint8Array;\n username: string;\n }) {\n const webAuthNCredentialPayload = new Uint8Array(\n cojsonInternals.secretSeedLength + cojsonInternals.shortHashLength,\n );\n\n webAuthNCredentialPayload.set(secretSeed);\n webAuthNCredentialPayload.set(\n cojsonInternals.rawCoIDtoBytes(accountID as unknown as RawAccountID),\n cojsonInternals.secretSeedLength,\n );\n\n try {\n await navigator.credentials.create({\n publicKey: {\n challenge: new Uint8Array(this.crypto.randomBytes(20)),\n rp: {\n name: this.appName,\n id: this.appHostname,\n },\n user: {\n id: webAuthNCredentialPayload,\n name: username + ` (${new Date().toLocaleString()})`,\n displayName: username,\n },\n pubKeyCredParams: [\n { alg: -7, type: \"public-key\" },\n { alg: -8, type: \"public-key\" },\n { alg: -37, type: \"public-key\" },\n { alg: -257, type: \"public-key\" },\n ],\n authenticatorSelection: {\n requireResidentKey: true,\n residentKey: \"required\",\n userVerification: \"preferred\",\n },\n timeout: 60000,\n attestation: \"direct\",\n },\n });\n } catch (error) {\n throw new Error(\"Passkey creation aborted\", { cause: error });\n }\n }\n\n private async getPasskeyCredentials() {\n try {\n const value = await navigator.credentials.get({\n publicKey: {\n challenge: new Uint8Array(this.crypto.randomBytes(20)),\n rpId: this.appHostname,\n allowCredentials: [],\n timeout: 60000,\n userVerification: \"preferred\",\n },\n mediation: \"optional\",\n });\n\n return value as\n | (Credential & { response: { userHandle: ArrayBuffer } })\n | null;\n } catch (error) {\n throw new Error(\"Passkey creation aborted\", { cause: error });\n }\n }\n}\n","import { LocalNode, Peer } from \"cojson\";\nimport { getIndexedDBStorage } from \"cojson-storage-indexeddb\";\nimport { WebSocketPeerWithReconnection } from \"cojson-transport-ws\";\nimport { WasmCrypto } from \"cojson/crypto/WasmCrypto\";\nimport {\n Account,\n AccountClass,\n AgentID,\n AnyAccountSchema,\n AuthCredentials,\n AuthSecretStorage,\n CoValue,\n CoValueFromRaw,\n CryptoProvider,\n ID,\n NewAccountProps,\n SessionID,\n SyncConfig,\n createAnonymousJazzContext,\n} from \"jazz-tools\";\nimport { createJazzContext } from \"jazz-tools\";\nimport { setupInspector } from \"./utils/export-account-inspector.js\";\nimport { getBrowserLockSessionProvider } from \"./provideBrowserLockSession/index.js\";\n\nsetupInspector();\n\nexport type BaseBrowserContextOptions = {\n sync: SyncConfig;\n reconnectionTimeout?: number;\n storage?: \"indexedDB\";\n crypto?: CryptoProvider;\n authSecretStorage: AuthSecretStorage;\n};\n\nclass BrowserWebSocketPeerWithReconnection extends WebSocketPeerWithReconnection {\n onNetworkChange(callback: (connected: boolean) => void): () => void {\n const handler = () => callback(navigator.onLine);\n window.addEventListener(\"online\", handler);\n window.addEventListener(\"offline\", handler);\n\n return () => {\n window.removeEventListener(\"online\", handler);\n window.removeEventListener(\"offline\", handler);\n };\n }\n}\n\nasync function setupPeers(options: BaseBrowserContextOptions) {\n const crypto = options.crypto || (await WasmCrypto.create());\n let node: LocalNode | undefined = undefined;\n\n const peers: Peer[] = [];\n\n const storage = await getIndexedDBStorage();\n\n if (options.sync.when === \"never\") {\n return {\n addConnectionListener: () => () => {},\n connected: () => false,\n toggleNetwork: () => {},\n peers,\n syncWhen: options.sync.when,\n storage,\n setNode: () => {},\n crypto,\n };\n }\n\n const wsPeer = new BrowserWebSocketPeerWithReconnection({\n peer: options.sync.peer,\n reconnectionTimeout: options.reconnectionTimeout,\n addPeer: (peer) => {\n if (node) {\n node.syncManager.addPeer(peer);\n } else {\n peers.push(peer);\n }\n },\n removePeer: (peer) => {\n peers.splice(peers.indexOf(peer), 1);\n },\n });\n\n function toggleNetwork(enabled: boolean) {\n if (enabled) {\n wsPeer.enable();\n } else {\n wsPeer.disable();\n }\n }\n\n function setNode(value: LocalNode) {\n node = value;\n }\n\n if (options.sync.when === \"always\" || !options.sync.when) {\n toggleNetwork(true);\n }\n\n return {\n toggleNetwork,\n addConnectionListener(listener: (connected: boolean) => void) {\n wsPeer.subscribe(listener);\n\n return () => {\n wsPeer.unsubscribe(listener);\n };\n },\n connected() {\n return wsPeer.connected;\n },\n peers,\n syncWhen: options.sync.when,\n storage,\n setNode,\n crypto,\n };\n}\n\nexport async function createJazzBrowserGuestContext(\n options: BaseBrowserContextOptions,\n) {\n const {\n toggleNetwork,\n peers,\n syncWhen,\n setNode,\n crypto,\n storage,\n addConnectionListener,\n connected,\n } = await setupPeers(options);\n\n const context = await createAnonymousJazzContext({\n crypto,\n peers,\n syncWhen,\n storage,\n });\n\n setNode(context.agent.node);\n\n options.authSecretStorage.emitUpdate(null);\n\n return {\n guest: context.agent,\n node: context.agent.node,\n done: () => {\n // TODO: Sync all the covalues before closing the connection & context\n toggleNetwork(false);\n context.done();\n },\n logOut: () => {\n return context.logOut();\n },\n addConnectionListener,\n connected,\n };\n}\n\nexport type BrowserContextOptions<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n> = {\n credentials?: AuthCredentials;\n AccountSchema?: S;\n newAccountProps?: NewAccountProps;\n defaultProfileName?: string;\n} & BaseBrowserContextOptions;\n\nexport async function createJazzBrowserContext<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n>(options: BrowserContextOptions<S>) {\n const {\n toggleNetwork,\n peers,\n syncWhen,\n setNode,\n crypto,\n storage,\n addConnectionListener,\n connected,\n } = await setupPeers(options);\n\n let unsubscribeAuthUpdate = () => {};\n\n if (options.sync.when === \"signedUp\") {\n const authSecretStorage = options.authSecretStorage;\n const credentials = options.credentials ?? (await authSecretStorage.get());\n\n function handleAuthUpdate(isAuthenticated: boolean) {\n if (isAuthenticated) {\n toggleNetwork(true);\n } else {\n toggleNetwork(false);\n }\n }\n\n unsubscribeAuthUpdate = authSecretStorage.onUpdate(handleAuthUpdate);\n handleAuthUpdate(authSecretStorage.getIsAuthenticated(credentials));\n }\n\n const context = await createJazzContext({\n credentials: options.credentials,\n newAccountProps: options.newAccountProps,\n peers,\n syncWhen,\n storage,\n crypto,\n defaultProfileName: options.defaultProfileName,\n AccountSchema: options.AccountSchema,\n sessionProvider: getBrowserLockSessionProvider(),\n authSecretStorage: options.authSecretStorage,\n });\n\n setNode(context.node);\n\n return {\n me: context.account,\n node: context.node,\n authSecretStorage: context.authSecretStorage,\n done: () => {\n // TODO: Sync all the covalues before closing the connection & context\n toggleNetwork(false);\n unsubscribeAuthUpdate();\n context.done();\n },\n logOut: () => {\n unsubscribeAuthUpdate();\n return context.logOut();\n },\n addConnectionListener,\n connected,\n };\n}\n\n/** @category Auth Providers */\nexport type SessionProvider = (\n accountID: ID<Account> | AgentID,\n) => Promise<SessionID>;\n","import { MockSessionProvider } from \"jazz-tools\";\nimport { BrowserSessionProvider } from \"./BrowserSessionProvider\";\n\nexport function getBrowserLockSessionProvider() {\n if (typeof navigator === \"undefined\" || !navigator.locks?.request) {\n // Fallback to random session ID for each tab session\n return new MockSessionProvider();\n }\n\n return new BrowserSessionProvider();\n}\n","import { SessionID } from \"cojson\";\n\nfunction getSessionKey(accountID: string, index: number) {\n return accountID + \"_\" + index;\n}\n\nexport class SessionIDStorage {\n static getSessionsList(accountID: string) {\n let sessionsList: SessionID[] = [];\n let i = 0;\n let lastSessionID: SessionID | null;\n\n do {\n lastSessionID = localStorage.getItem(\n getSessionKey(accountID, i),\n ) as SessionID | null;\n if (lastSessionID) {\n sessionsList.push(lastSessionID);\n }\n i++;\n } while (lastSessionID);\n\n return sessionsList;\n }\n\n static storeSessionID(\n accountID: string,\n sessionID: SessionID,\n index: number,\n ) {\n localStorage.setItem(getSessionKey(accountID, index), sessionID);\n }\n}\n","import { AgentID, CryptoProvider, RawAccountID, SessionID } from \"cojson\";\nimport { ID, Account, SessionProvider } from \"jazz-tools\";\nimport { SessionIDStorage } from \"./SessionIDStorage\";\n\nexport class BrowserSessionProvider implements SessionProvider {\n async acquireSession(\n accountID: ID<Account> | AgentID,\n crypto: CryptoProvider,\n ): Promise<{ sessionID: SessionID; sessionDone: () => void }> {\n const { sessionPromise, resolveSession } = createSessionLockPromise();\n\n // Get the list of sessions for the account, to try to acquire an existing session\n const sessionsList = SessionIDStorage.getSessionsList(accountID);\n\n for (const [index, sessionID] of sessionsList.entries()) {\n const sessionAcquired = await tryToAcquireSession(\n sessionID,\n sessionPromise,\n );\n\n if (sessionAcquired) {\n console.log(\"Using existing session\", sessionID, \"at index\", index); // This log is used in the e2e tests to verify the correctness of the feature\n return {\n sessionID,\n sessionDone: resolveSession,\n };\n }\n }\n\n const newSessionID = crypto.newRandomSessionID(\n accountID as RawAccountID | AgentID,\n );\n\n // Acquire exclusively the session to store the new session ID for reuse in future sessions\n await lockAndStoreSession(accountID, newSessionID, sessionPromise);\n\n console.log(\"Created new session\", newSessionID); // This log is used in the e2e tests to verify the correctness of the feature\n\n return {\n sessionID: newSessionID,\n sessionDone: resolveSession,\n };\n }\n\n async persistSession(\n accountID: ID<Account> | AgentID,\n sessionID: SessionID,\n ): Promise<{ sessionDone: () => void }> {\n const { sessionPromise, resolveSession } = createSessionLockPromise();\n\n // Store the session id for future use and lock it until the session is done\n await lockAndStoreSession(accountID, sessionID, sessionPromise);\n\n console.log(\"Stored new session\", sessionID);\n\n return { sessionDone: resolveSession };\n }\n}\n\nasync function lockAndStoreSession(\n accountID: ID<Account> | AgentID,\n sessionID: SessionID,\n sessionPromise: Promise<void>,\n) {\n const sessionAcquired = await tryToAcquireSession(sessionID, sessionPromise);\n\n if (!sessionAcquired) {\n // This should never happen because the session has been randomly generated\n throw new Error(\"Couldn't get lock on new session\");\n }\n\n // We don't need to wait for this to finish, we only need to acquire the lock on the new session\n storeSessionID(accountID, sessionID);\n}\n\nfunction tryToAcquireSession(\n sessionID: SessionID,\n sessionDonePromise: Promise<void>,\n) {\n return new Promise<boolean>((resolve) => {\n // Acquire exclusively the session if available\n navigator.locks.request(\n `load_session_${sessionID}`,\n { mode: \"exclusive\", ifAvailable: true },\n async (lock) => {\n if (!lock) {\n resolve(false); // Session already in use\n return;\n }\n\n resolve(true); // Session is available\n\n // Return the promise to lock the session until sessionDone is called\n return sessionDonePromise;\n },\n );\n });\n}\n\nfunction storeSessionID(\n accountID: ID<Account> | AgentID,\n sessionID: SessionID,\n) {\n return navigator.locks.request(\n `store_session_${accountID}`,\n { mode: \"exclusive\" },\n async (lock) => {\n if (!lock) {\n console.error(\"Couldn't get lock to store session ID\", accountID);\n }\n\n const sessionsList = SessionIDStorage.getSessionsList(accountID);\n SessionIDStorage.storeSessionID(\n accountID,\n sessionID,\n sessionsList.length,\n );\n },\n );\n}\n\nfunction createSessionLockPromise() {\n let resolveSession: () => void;\n const sessionPromise = new Promise<void>((resolve) => {\n resolveSession = resolve;\n });\n\n return {\n sessionPromise,\n resolveSession: resolveSession!,\n };\n}\n","import {\n Account,\n AccountClass,\n AnyAccountSchema,\n CoValueFromRaw,\n InMemoryKVStore,\n InstanceOfSchema,\n JazzContextManager,\n SyncConfig,\n} from \"jazz-tools\";\nimport { JazzContextManagerAuthProps } from \"jazz-tools\";\nimport { LocalStorageKVStore } from \"./auth/LocalStorageKVStore.js\";\nimport {\n BaseBrowserContextOptions,\n createJazzBrowserContext,\n createJazzBrowserGuestContext,\n} from \"./createBrowserContext.js\";\n\nexport type JazzContextManagerProps<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n> = {\n guestMode?: boolean;\n sync: SyncConfig;\n onLogOut?: () => void;\n logOutReplacement?: () => void;\n onAnonymousAccountDiscarded?: (\n anonymousAccount: InstanceOfSchema<S>,\n ) => Promise<void>;\n storage?: BaseBrowserContextOptions[\"storage\"];\n AccountSchema?: S;\n defaultProfileName?: string;\n};\n\nexport class JazzBrowserContextManager<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n> extends JazzContextManager<InstanceOfSchema<S>, JazzContextManagerProps<S>> {\n // TODO: When the storage changes, if the user is changed, update the context\n getKvStore() {\n if (typeof window === \"undefined\") {\n // To handle running in SSR\n return new InMemoryKVStore();\n } else {\n return new LocalStorageKVStore();\n }\n }\n\n async getNewContext(\n props: JazzContextManagerProps<S>,\n authProps?: JazzContextManagerAuthProps,\n ) {\n if (props.guestMode) {\n return createJazzBrowserGuestContext({\n sync: props.sync,\n storage: props.storage,\n authSecretStorage: this.authSecretStorage,\n });\n } else {\n return createJazzBrowserContext<S>({\n sync: props.sync,\n storage: props.storage,\n AccountSchema: props.AccountSchema,\n credentials: authProps?.credentials,\n newAccountProps: authProps?.newAccountProps,\n defaultProfileName: props.defaultProfileName,\n authSecretStorage: this.authSecretStorage,\n });\n }\n }\n\n propsChanged(props: JazzContextManagerProps<S>) {\n if (!this.props) {\n return true;\n }\n\n return (\n this.props.sync.when !== props.sync.when ||\n this.props.sync.peer !== props.sync.peer ||\n this.props.guestMode !== props.guestMode\n );\n }\n}\n","import { KvStore } from \"jazz-tools\";\n\nexport class LocalStorageKVStore implements KvStore {\n constructor() {}\n\n async get(key: string) {\n return localStorage.getItem(key);\n }\n\n async set(key: string, value: string) {\n localStorage.setItem(key, value);\n }\n\n async delete(key: string) {\n localStorage.removeItem(key);\n }\n\n async clearAll() {\n localStorage.clear();\n }\n}\n"],"mappings":"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","names":["handleAuthUpdate"]}
1
+ {"version":3,"sources":["../../src/browser/index.ts","../../src/browser/utils/export-account-inspector.ts","../../src/browser/auth/PasskeyAuth.ts","../../src/browser/createBrowserContext.ts","../../src/browser/provideBrowserLockSession/index.ts","../../src/browser/provideBrowserLockSession/SessionIDStorage.ts","../../src/browser/provideBrowserLockSession/BrowserSessionDurabilityMarker.ts","../../src/browser/provideBrowserLockSession/BrowserSessionProvider.ts","../../src/browser/BrowserContextManager.ts","../../src/browser/auth/LocalStorageKVStore.ts"],"sourcesContent":["import { AccountRole } from \"cojson\";\nimport {\n Account,\n CoValue,\n CoValueClassOrSchema,\n InviteSecret,\n createInviteLink as baseCreateInviteLink,\n consumeInviteLink,\n} from \"jazz-tools\";\nimport { setupInspector } from \"./utils/export-account-inspector.js\";\nexport { BrowserPasskeyAuth } from \"./auth/PasskeyAuth.js\";\n\nsetupInspector();\n\nexport * from \"./createBrowserContext.js\";\nexport * from \"./BrowserContextManager.js\";\n\nexport { LocalStorageKVStore } from \"./auth/LocalStorageKVStore.js\";\n\n/** @category Invite Links */\nexport function createInviteLink<C extends CoValue>(\n value: C,\n role: AccountRole,\n // default to same address as window.location, but without hash\n {\n baseURL = window.location.href.replace(/#.*$/, \"\"),\n valueHint,\n }: { baseURL?: string; valueHint?: string } = {},\n): string {\n return baseCreateInviteLink(value, role, { baseURL, valueHint });\n}\n\n/** @category Invite Links */\nexport { parseInviteLink } from \"jazz-tools\";\n\n/** @category Invite Links */\nexport async function consumeInviteLinkFromWindowLocation<\n S extends CoValueClassOrSchema,\n>({\n as,\n forValueHint,\n invitedObjectSchema,\n}: {\n as?: Account;\n forValueHint?: string;\n invitedObjectSchema: S;\n}): Promise<\n | {\n valueID: string;\n valueHint?: string;\n inviteSecret: InviteSecret;\n }\n | undefined\n> {\n const result = await consumeInviteLink({\n inviteURL: window.location.href,\n as,\n forValueHint,\n invitedObjectSchema,\n });\n\n if (result) {\n window.history.replaceState(\n {},\n \"\",\n window.location.href.replace(/#.*$/, \"\"),\n );\n }\n\n return result;\n}\n","import { AuthSecretStorage } from \"jazz-tools\";\n\nasync function exportAccountToInspector() {\n const authSecretStorage = new AuthSecretStorage();\n const localStorageData = await authSecretStorage.get();\n\n if (!localStorageData) {\n console.error(\"No account data found in localStorage\");\n return;\n }\n\n const encodedAccountSecret = btoa(localStorageData.accountSecret);\n window.open(\n new URL(\n `#/import/${localStorageData?.accountID}/${encodedAccountSecret}`,\n \"https://inspector.jazz.tools\",\n ).toString(),\n \"_blank\",\n );\n}\n\nfunction listenForCmdJ() {\n if (typeof window === \"undefined\") return;\n\n const cb = (e: any) => {\n if (e.metaKey && e.key === \"j\") {\n if (\n confirm(\n \"Are you sure you want to inspect your account using inspector.jazz.tools? This lets anyone with the secret inspector URL read your data and impersonate you.\",\n )\n ) {\n exportAccountToInspector();\n }\n }\n };\n\n window.addEventListener(\"keydown\", cb);\n\n return () => {\n window.removeEventListener(\"keydown\", cb);\n };\n}\n\n/**\n * Automatically sets up the Cmd+J listener if 'allowJazzInspector' is present in the URL\n * @returns A cleanup function if the listener was set up, undefined otherwise\n */\nexport function setupInspector() {\n if (typeof window === \"undefined\") return;\n\n const url = new URL(window.location.href);\n if (\n url.hash.includes(\"allowJazzInspector\") ||\n process.env.NODE_ENV === \"development\"\n ) {\n return listenForCmdJ();\n }\n}\n","import { CryptoProvider, RawAccountID, cojsonInternals } from \"cojson\";\nimport {\n Account,\n AuthSecretStorage,\n AuthenticateAccountFunction,\n ID,\n} from \"jazz-tools\";\n\n/**\n * `BrowserPasskeyAuth` provides a `JazzAuth` object for passkey authentication.\n *\n * ```ts\n * import { BrowserPasskeyAuth } from \"jazz-tools/browser\";\n *\n * const auth = new BrowserPasskeyAuth(driver, appName);\n * ```\n *\n * @category Auth Providers\n */\nexport class BrowserPasskeyAuth {\n constructor(\n protected crypto: CryptoProvider,\n protected authenticate: AuthenticateAccountFunction,\n protected authSecretStorage: AuthSecretStorage,\n public appName: string,\n public appHostname: string = window.location.hostname,\n ) {}\n\n static readonly id = \"passkey\";\n\n logIn = async () => {\n const { crypto, authenticate } = this;\n\n const webAuthNCredential = await this.getPasskeyCredentials();\n\n if (!webAuthNCredential) {\n return;\n }\n\n const webAuthNCredentialPayload = new Uint8Array(\n webAuthNCredential.response.userHandle,\n );\n const accountSecretSeed = webAuthNCredentialPayload.slice(\n 0,\n cojsonInternals.secretSeedLength,\n );\n\n const secret = crypto.agentSecretFromSecretSeed(accountSecretSeed);\n\n const accountID = cojsonInternals.rawCoIDfromBytes(\n webAuthNCredentialPayload.slice(\n cojsonInternals.secretSeedLength,\n cojsonInternals.secretSeedLength + cojsonInternals.shortHashLength,\n ),\n ) as ID<Account>;\n\n await authenticate({\n accountID,\n accountSecret: secret,\n });\n\n await this.authSecretStorage.set({\n accountID,\n secretSeed: accountSecretSeed,\n accountSecret: secret,\n provider: \"passkey\",\n });\n };\n\n signUp = async (username: string) => {\n const credentials = await this.authSecretStorage.get();\n\n if (!credentials?.secretSeed) {\n throw new Error(\n \"Not enough credentials to register the account with passkey\",\n );\n }\n\n await this.createPasskeyCredentials({\n accountID: credentials.accountID,\n secretSeed: credentials.secretSeed,\n username,\n });\n\n const currentAccount = await Account.getMe().$jazz.ensureLoaded({\n resolve: {\n profile: true,\n },\n });\n\n if (username.trim().length !== 0) {\n currentAccount.profile.$jazz.set(\"name\", username);\n }\n\n await this.authSecretStorage.set({\n accountID: credentials.accountID,\n secretSeed: credentials.secretSeed,\n accountSecret: credentials.accountSecret,\n provider: \"passkey\",\n });\n };\n\n private async createPasskeyCredentials({\n accountID,\n secretSeed,\n username,\n }: {\n accountID: ID<Account>;\n secretSeed: Uint8Array;\n username: string;\n }) {\n const webAuthNCredentialPayload = new Uint8Array(\n cojsonInternals.secretSeedLength + cojsonInternals.shortHashLength,\n );\n\n webAuthNCredentialPayload.set(secretSeed);\n webAuthNCredentialPayload.set(\n cojsonInternals.rawCoIDtoBytes(accountID as unknown as RawAccountID),\n cojsonInternals.secretSeedLength,\n );\n\n try {\n await navigator.credentials.create({\n publicKey: {\n challenge: new Uint8Array(this.crypto.randomBytes(20)),\n rp: {\n name: this.appName,\n id: this.appHostname,\n },\n user: {\n id: webAuthNCredentialPayload,\n name: username + ` (${new Date().toLocaleString()})`,\n displayName: username,\n },\n pubKeyCredParams: [\n { alg: -7, type: \"public-key\" },\n { alg: -8, type: \"public-key\" },\n { alg: -37, type: \"public-key\" },\n { alg: -257, type: \"public-key\" },\n ],\n authenticatorSelection: {\n requireResidentKey: true,\n residentKey: \"required\",\n userVerification: \"preferred\",\n },\n timeout: 60000,\n attestation: \"direct\",\n },\n });\n } catch (error) {\n throw new Error(\"Passkey creation aborted\", { cause: error });\n }\n }\n\n private async getPasskeyCredentials() {\n try {\n const value = await navigator.credentials.get({\n publicKey: {\n challenge: new Uint8Array(this.crypto.randomBytes(20)),\n rpId: this.appHostname,\n allowCredentials: [],\n timeout: 60000,\n userVerification: \"preferred\",\n },\n mediation: \"optional\",\n });\n\n return value as\n | (Credential & { response: { userHandle: ArrayBuffer } })\n | null;\n } catch (error) {\n throw new Error(\"Passkey creation aborted\", { cause: error });\n }\n }\n}\n","import { LocalNode, Peer } from \"cojson\";\nimport { getIndexedDBStorage } from \"cojson-storage-indexeddb\";\nimport { WebSocketPeerWithReconnection } from \"cojson-transport-ws\";\nimport { WasmCrypto } from \"cojson/crypto/WasmCrypto\";\nimport {\n Account,\n AccountClass,\n AgentID,\n AnyAccountSchema,\n AuthCredentials,\n AuthSecretStorage,\n CoValue,\n CoValueFromRaw,\n CryptoProvider,\n ID,\n NewAccountProps,\n SessionID,\n SyncConfig,\n createAnonymousJazzContext,\n} from \"jazz-tools\";\nimport { createJazzContext } from \"jazz-tools\";\nimport { setupInspector } from \"./utils/export-account-inspector.js\";\nimport { getBrowserLockSessionProvider } from \"./provideBrowserLockSession/index.js\";\n\nsetupInspector();\n\nexport type BaseBrowserContextOptions = {\n sync: SyncConfig;\n reconnectionTimeout?: number;\n storage?: \"indexedDB\";\n crypto?: CryptoProvider;\n authSecretStorage: AuthSecretStorage;\n experimental_clockSyncFromServerPings?: boolean;\n};\n\nclass BrowserWebSocketPeerWithReconnection extends WebSocketPeerWithReconnection {\n onNetworkChange(callback: (connected: boolean) => void): () => void {\n const handler = () => callback(navigator.onLine);\n window.addEventListener(\"online\", handler);\n window.addEventListener(\"offline\", handler);\n\n return () => {\n window.removeEventListener(\"online\", handler);\n window.removeEventListener(\"offline\", handler);\n };\n }\n}\n\nasync function setupPeers(options: BaseBrowserContextOptions) {\n const crypto = options.crypto || (await WasmCrypto.create());\n let node: LocalNode | undefined = undefined;\n\n const peers: Peer[] = [];\n\n const storage = await getIndexedDBStorage();\n\n if (options.sync.when === \"never\") {\n return {\n addConnectionListener: () => () => {},\n connected: () => false,\n toggleNetwork: () => {},\n peers,\n syncWhen: options.sync.when,\n storage,\n setNode: () => {},\n crypto,\n };\n }\n\n const wsPeer = new BrowserWebSocketPeerWithReconnection({\n peer: options.sync.peer,\n reconnectionTimeout: options.reconnectionTimeout,\n addPeer: (peer) => {\n if (node) {\n node.syncManager.addPeer(peer);\n } else {\n peers.push(peer);\n }\n },\n removePeer: (peer) => {\n peers.splice(peers.indexOf(peer), 1);\n },\n onPingReceived: (sample) => {\n node?.clockOffset.addSample(sample);\n },\n });\n\n function toggleNetwork(enabled: boolean) {\n if (enabled) {\n wsPeer.enable();\n } else {\n wsPeer.disable();\n }\n }\n\n function setNode(value: LocalNode) {\n node = value;\n }\n\n if (options.sync.when === \"always\" || !options.sync.when) {\n toggleNetwork(true);\n }\n\n return {\n toggleNetwork,\n addConnectionListener(listener: (connected: boolean) => void) {\n wsPeer.subscribe(listener);\n\n return () => {\n wsPeer.unsubscribe(listener);\n };\n },\n connected() {\n return wsPeer.connected;\n },\n peers,\n syncWhen: options.sync.when,\n storage,\n setNode,\n crypto,\n };\n}\n\nexport async function createJazzBrowserGuestContext(\n options: BaseBrowserContextOptions,\n) {\n const {\n toggleNetwork,\n peers,\n syncWhen,\n setNode,\n crypto,\n storage,\n addConnectionListener,\n connected,\n } = await setupPeers(options);\n\n const context = await createAnonymousJazzContext({\n crypto,\n peers,\n syncWhen,\n storage,\n experimental_clockSyncFromServerPings:\n options.experimental_clockSyncFromServerPings,\n });\n\n setNode(context.agent.node);\n\n options.authSecretStorage.emitUpdate(null);\n\n return {\n guest: context.agent,\n node: context.agent.node,\n done: () => {\n // TODO: Sync all the covalues before closing the connection & context\n toggleNetwork(false);\n context.done();\n },\n logOut: () => {\n return context.logOut();\n },\n addConnectionListener,\n connected,\n };\n}\n\nexport type BrowserContextOptions<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n> = {\n credentials?: AuthCredentials;\n AccountSchema?: S;\n newAccountProps?: NewAccountProps;\n defaultProfileName?: string;\n} & BaseBrowserContextOptions;\n\nexport async function createJazzBrowserContext<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n>(options: BrowserContextOptions<S>) {\n const {\n toggleNetwork,\n peers,\n syncWhen,\n setNode,\n crypto,\n storage,\n addConnectionListener,\n connected,\n } = await setupPeers(options);\n\n let unsubscribeAuthUpdate = () => {};\n\n if (options.sync.when === \"signedUp\") {\n const authSecretStorage = options.authSecretStorage;\n const credentials = options.credentials ?? (await authSecretStorage.get());\n\n function handleAuthUpdate(isAuthenticated: boolean) {\n if (isAuthenticated) {\n toggleNetwork(true);\n } else {\n toggleNetwork(false);\n }\n }\n\n unsubscribeAuthUpdate = authSecretStorage.onUpdate(handleAuthUpdate);\n handleAuthUpdate(authSecretStorage.getIsAuthenticated(credentials));\n }\n\n const context = await createJazzContext({\n credentials: options.credentials,\n newAccountProps: options.newAccountProps,\n peers,\n syncWhen,\n storage,\n crypto,\n defaultProfileName: options.defaultProfileName,\n AccountSchema: options.AccountSchema,\n sessionProvider: getBrowserLockSessionProvider(),\n authSecretStorage: options.authSecretStorage,\n experimental_clockSyncFromServerPings:\n options.experimental_clockSyncFromServerPings,\n });\n\n setNode(context.node);\n\n return {\n me: context.account,\n node: context.node,\n authSecretStorage: context.authSecretStorage,\n done: () => {\n // TODO: Sync all the covalues before closing the connection & context\n toggleNetwork(false);\n unsubscribeAuthUpdate();\n context.done();\n },\n logOut: () => {\n unsubscribeAuthUpdate();\n return context.logOut();\n },\n addConnectionListener,\n connected,\n };\n}\n\n/** @category Auth Providers */\nexport type SessionProvider = (\n accountID: ID<Account> | AgentID,\n) => Promise<SessionID>;\n","import { MockSessionProvider } from \"jazz-tools\";\nimport { BrowserSessionProvider } from \"./BrowserSessionProvider\";\nexport { BrowserSessionDurabilityMarker } from \"./BrowserSessionDurabilityMarker\";\n\nexport function getBrowserLockSessionProvider() {\n if (typeof navigator === \"undefined\" || !navigator.locks?.request) {\n // Fallback to random session ID for each tab session\n return new MockSessionProvider();\n }\n\n return new BrowserSessionProvider();\n}\n","import { SessionID } from \"cojson\";\n\nfunction getSessionKey(accountID: string, index: number) {\n return accountID + \"_\" + index;\n}\n\nexport class SessionIDStorage {\n static getSessionsList(accountID: string) {\n let sessionsList: SessionID[] = [];\n let i = 0;\n let lastSessionID: SessionID | null;\n\n do {\n lastSessionID = localStorage.getItem(\n getSessionKey(accountID, i),\n ) as SessionID | null;\n if (lastSessionID) {\n sessionsList.push(lastSessionID);\n }\n i++;\n } while (lastSessionID);\n\n return sessionsList;\n }\n\n static storeSessionID(\n accountID: string,\n sessionID: SessionID,\n index: number,\n ) {\n localStorage.setItem(getSessionKey(accountID, index), sessionID);\n }\n}\n","import type { SessionID } from \"cojson\";\nimport type { SessionDurabilityMarker } from \"jazz-tools\";\nimport { sessionDurabilityMarkerKey as markerKey } from \"jazz-tools\";\n\n/**\n * localStorage-backed durability marker. Writes are synchronous, so a marker\n * set before a network send is (best-effort) durable before the server can\n * ever be ahead of local storage for that session.\n */\nexport const BrowserSessionDurabilityMarker: SessionDurabilityMarker = {\n set(sessionID: SessionID) {\n localStorage.setItem(markerKey(sessionID), \"1\");\n },\n clear(sessionID: SessionID) {\n localStorage.removeItem(markerKey(sessionID));\n },\n isSet(sessionID: SessionID) {\n return localStorage.getItem(markerKey(sessionID)) !== null;\n },\n};\n","import { AgentID, CryptoProvider, RawAccountID, SessionID } from \"cojson\";\nimport { ID, Account, SessionProvider } from \"jazz-tools\";\nimport { SessionIDStorage } from \"./SessionIDStorage\";\nimport { BrowserSessionDurabilityMarker } from \"./BrowserSessionDurabilityMarker\";\n\nexport class BrowserSessionProvider implements SessionProvider {\n readonly durabilityMarker = BrowserSessionDurabilityMarker;\n\n async acquireSession(\n accountID: ID<Account> | AgentID,\n crypto: CryptoProvider,\n ): Promise<{ sessionID: SessionID; sessionDone: () => void }> {\n const { sessionPromise, resolveSession } = createSessionLockPromise();\n\n // Get the list of sessions for the account, to try to acquire an existing session\n const sessionsList = SessionIDStorage.getSessionsList(accountID);\n let dirtySlot: { index: number; sessionID: SessionID } | undefined;\n\n for (const [index, sessionID] of sessionsList.entries()) {\n // If the session crashed while it had sent-but-unpersisted transactions,\n // reusing it could fork the session's hash chain on the server.\n if (BrowserSessionDurabilityMarker.isSet(sessionID)) {\n dirtySlot ??= { index, sessionID };\n continue;\n }\n\n const sessionAcquired = await tryToAcquireSession(\n sessionID,\n sessionPromise,\n );\n\n if (sessionAcquired) {\n console.log(\"Using existing session\", sessionID, \"at index\", index); // This log is used in the e2e tests to verify the correctness of the feature\n return {\n sessionID,\n sessionDone: resolveSession,\n };\n }\n }\n\n const newSessionID = crypto.newRandomSessionID(\n accountID as RawAccountID | AgentID,\n );\n\n // Acquire exclusively the session to store the new session ID for reuse in future sessions.\n // If a dirty slot was found, reclaim it so the list doesn't grow across crashes.\n await lockAndStoreSession(\n accountID,\n newSessionID,\n sessionPromise,\n dirtySlot,\n );\n\n console.log(\"Created new session\", newSessionID); // This log is used in the e2e tests to verify the correctness of the feature\n\n return {\n sessionID: newSessionID,\n sessionDone: resolveSession,\n };\n }\n\n async persistSession(\n accountID: ID<Account> | AgentID,\n sessionID: SessionID,\n ): Promise<{ sessionDone: () => void }> {\n const { sessionPromise, resolveSession } = createSessionLockPromise();\n\n // Store the session id for future use and lock it until the session is done\n await lockAndStoreSession(accountID, sessionID, sessionPromise);\n\n console.log(\"Stored new session\", sessionID);\n\n return { sessionDone: resolveSession };\n }\n}\n\nasync function lockAndStoreSession(\n accountID: ID<Account> | AgentID,\n sessionID: SessionID,\n sessionPromise: Promise<void>,\n replaceSlot?: { index: number; sessionID: SessionID },\n) {\n const sessionAcquired = await tryToAcquireSession(sessionID, sessionPromise);\n\n if (!sessionAcquired) {\n // This should never happen because the session has been randomly generated\n throw new Error(\"Couldn't get lock on new session\");\n }\n\n // We don't need to wait for this to finish, we only need to acquire the lock on the new session\n storeSessionID(accountID, sessionID, replaceSlot);\n}\n\nfunction tryToAcquireSession(\n sessionID: SessionID,\n sessionDonePromise: Promise<void>,\n) {\n return new Promise<boolean>((resolve) => {\n // Acquire exclusively the session if available\n navigator.locks.request(\n `load_session_${sessionID}`,\n { mode: \"exclusive\", ifAvailable: true },\n async (lock) => {\n if (!lock) {\n resolve(false); // Session already in use\n return;\n }\n\n resolve(true); // Session is available\n\n // Return the promise to lock the session until sessionDone is called\n return sessionDonePromise;\n },\n );\n });\n}\n\nfunction storeSessionID(\n accountID: ID<Account> | AgentID,\n sessionID: SessionID,\n replaceSlot?: { index: number; sessionID: SessionID },\n) {\n return navigator.locks.request(\n `store_session_${accountID}`,\n { mode: \"exclusive\" },\n async (lock) => {\n if (!lock) {\n console.error(\"Couldn't get lock to store session ID\", accountID);\n }\n\n const sessionsList = SessionIDStorage.getSessionsList(accountID);\n\n // Reclaim the abandoned dirty session's slot so the list doesn't grow\n // across crashes — unless a concurrent tab already took it, in which\n // case append. The stale marker is dropped only after the slot is\n // written, so a crash in between leaves the session safely marked.\n const index =\n replaceSlot && sessionsList[replaceSlot.index] === replaceSlot.sessionID\n ? replaceSlot.index\n : sessionsList.length;\n\n SessionIDStorage.storeSessionID(accountID, sessionID, index);\n\n if (replaceSlot) {\n BrowserSessionDurabilityMarker.clear(replaceSlot.sessionID);\n }\n },\n );\n}\n\nfunction createSessionLockPromise() {\n let resolveSession: () => void;\n const sessionPromise = new Promise<void>((resolve) => {\n resolveSession = resolve;\n });\n\n return {\n sessionPromise,\n resolveSession: resolveSession!,\n };\n}\n","import {\n Account,\n AccountClass,\n AnyAccountSchema,\n CoValueFromRaw,\n InMemoryKVStore,\n InstanceOfSchema,\n JazzContextManager,\n SyncConfig,\n} from \"jazz-tools\";\nimport { JazzContextManagerAuthProps } from \"jazz-tools\";\nimport { LocalStorageKVStore } from \"./auth/LocalStorageKVStore.js\";\nimport {\n BaseBrowserContextOptions,\n createJazzBrowserContext,\n createJazzBrowserGuestContext,\n} from \"./createBrowserContext.js\";\n\nexport type JazzContextManagerProps<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n> = {\n guestMode?: boolean;\n sync: SyncConfig;\n onLogOut?: () => void;\n logOutReplacement?: () => void;\n onAnonymousAccountDiscarded?: (\n anonymousAccount: InstanceOfSchema<S>,\n ) => Promise<void>;\n storage?: BaseBrowserContextOptions[\"storage\"];\n AccountSchema?: S;\n defaultProfileName?: string;\n experimental_clockSyncFromServerPings?: boolean;\n};\n\nexport class JazzBrowserContextManager<\n S extends\n | (AccountClass<Account> & CoValueFromRaw<Account>)\n | AnyAccountSchema,\n> extends JazzContextManager<InstanceOfSchema<S>, JazzContextManagerProps<S>> {\n // TODO: When the storage changes, if the user is changed, update the context\n getKvStore() {\n if (typeof window === \"undefined\") {\n // To handle running in SSR\n return new InMemoryKVStore();\n } else {\n return new LocalStorageKVStore();\n }\n }\n\n async getNewContext(\n props: JazzContextManagerProps<S>,\n authProps?: JazzContextManagerAuthProps,\n ) {\n if (props.guestMode) {\n return createJazzBrowserGuestContext({\n sync: props.sync,\n storage: props.storage,\n authSecretStorage: this.authSecretStorage,\n experimental_clockSyncFromServerPings:\n props.experimental_clockSyncFromServerPings,\n });\n } else {\n return createJazzBrowserContext<S>({\n sync: props.sync,\n storage: props.storage,\n AccountSchema: props.AccountSchema,\n credentials: authProps?.credentials,\n newAccountProps: authProps?.newAccountProps,\n defaultProfileName: props.defaultProfileName,\n authSecretStorage: this.authSecretStorage,\n experimental_clockSyncFromServerPings:\n props.experimental_clockSyncFromServerPings,\n });\n }\n }\n\n propsChanged(props: JazzContextManagerProps<S>) {\n if (!this.props) {\n return true;\n }\n\n return (\n this.props.sync.when !== props.sync.when ||\n this.props.sync.peer !== props.sync.peer ||\n this.props.guestMode !== props.guestMode\n );\n }\n}\n","import { KvStore } from \"jazz-tools\";\n\nexport class LocalStorageKVStore implements KvStore {\n constructor() {}\n\n async get(key: string) {\n return localStorage.getItem(key);\n }\n\n async set(key: string, value: string) {\n localStorage.setItem(key, value);\n }\n\n async delete(key: string) {\n localStorage.removeItem(key);\n }\n\n async clearAll() {\n localStorage.clear();\n }\n}\n"],"mappings":"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","names":["handleAuthUpdate"]}
@@ -0,0 +1,8 @@
1
+ import type { SessionDurabilityMarker } from "jazz-tools";
2
+ /**
3
+ * localStorage-backed durability marker. Writes are synchronous, so a marker
4
+ * set before a network send is (best-effort) durable before the server can
5
+ * ever be ahead of local storage for that session.
6
+ */
7
+ export declare const BrowserSessionDurabilityMarker: SessionDurabilityMarker;
8
+ //# sourceMappingURL=BrowserSessionDurabilityMarker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"BrowserSessionDurabilityMarker.d.ts","sourceRoot":"","sources":["../../../src/browser/provideBrowserLockSession/BrowserSessionDurabilityMarker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAG1D;;;;GAIG;AACH,eAAO,MAAM,8BAA8B,EAAE,uBAU5C,CAAC"}
@@ -1,6 +1,7 @@
1
1
  import { AgentID, CryptoProvider, SessionID } from "cojson";
2
2
  import { ID, Account, SessionProvider } from "jazz-tools";
3
3
  export declare class BrowserSessionProvider implements SessionProvider {
4
+ readonly durabilityMarker: import("jazz-tools").SessionDurabilityMarker;
4
5
  acquireSession(accountID: ID<Account> | AgentID, crypto: CryptoProvider): Promise<{
5
6
  sessionID: SessionID;
6
7
  sessionDone: () => void;
@@ -1 +1 @@
1
- {"version":3,"file":"BrowserSessionProvider.d.ts","sourceRoot":"","sources":["../../../src/browser/provideBrowserLockSession/BrowserSessionProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAgB,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC1E,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAG1D,qBAAa,sBAAuB,YAAW,eAAe;IACtD,cAAc,CAClB,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,OAAO,EAChC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC;QAAE,SAAS,EAAE,SAAS,CAAC;QAAC,WAAW,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IAoCvD,cAAc,CAClB,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,OAAO,EAChC,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;CAUxC"}
1
+ {"version":3,"file":"BrowserSessionProvider.d.ts","sourceRoot":"","sources":["../../../src/browser/provideBrowserLockSession/BrowserSessionProvider.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,cAAc,EAAgB,SAAS,EAAE,MAAM,QAAQ,CAAC;AAC1E,OAAO,EAAE,EAAE,EAAE,OAAO,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC;AAI1D,qBAAa,sBAAuB,YAAW,eAAe;IAC5D,QAAQ,CAAC,gBAAgB,+CAAkC;IAErD,cAAc,CAClB,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,OAAO,EAChC,MAAM,EAAE,cAAc,GACrB,OAAO,CAAC;QAAE,SAAS,EAAE,SAAS,CAAC;QAAC,WAAW,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;IAkDvD,cAAc,CAClB,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,OAAO,EAChC,SAAS,EAAE,SAAS,GACnB,OAAO,CAAC;QAAE,WAAW,EAAE,MAAM,IAAI,CAAA;KAAE,CAAC;CAUxC"}
@@ -1,4 +1,5 @@
1
1
  import { MockSessionProvider } from "jazz-tools";
2
2
  import { BrowserSessionProvider } from "./BrowserSessionProvider";
3
+ export { BrowserSessionDurabilityMarker } from "./BrowserSessionDurabilityMarker";
3
4
  export declare function getBrowserLockSessionProvider(): MockSessionProvider | BrowserSessionProvider;
4
5
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/browser/provideBrowserLockSession/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAElE,wBAAgB,6BAA6B,iDAO5C"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/browser/provideBrowserLockSession/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,YAAY,CAAC;AACjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,EAAE,8BAA8B,EAAE,MAAM,kCAAkC,CAAC;AAElF,wBAAgB,6BAA6B,iDAO5C"}
@@ -2929,7 +2929,7 @@ function schemaUnionDiscriminatorFor(schema) {
2929
2929
  return augmentedSchema.getCoValueClass();
2930
2930
  }
2931
2931
  }
2932
- throw new Error(
2932
+ throw new SchemaUnionNoMatchingVariantError(
2933
2933
  "co.discriminatedUnion() of collaborative types with no matching discriminator value found"
2934
2934
  );
2935
2935
  };
@@ -4991,6 +4991,12 @@ var CoRichText = class extends CoPlainText {
4991
4991
  };
4992
4992
 
4993
4993
  // src/tools/coValues/schemaUnion.ts
4994
+ var SchemaUnionNoMatchingVariantError = class extends Error {
4995
+ constructor(message) {
4996
+ super(message);
4997
+ this.name = "SchemaUnionNoMatchingVariantError";
4998
+ }
4999
+ };
4994
5000
  var SchemaUnion2 = class extends CoValueBase {
4995
5001
  static create(init, owner) {
4996
5002
  throw new Error("Not implemented");
@@ -6183,7 +6189,21 @@ var _SubscriptionScope = class _SubscriptionScope {
6183
6189
  return;
6184
6190
  }
6185
6191
  this.migrating = true;
6186
- const instance = instantiateRefEncodedFromRaw(this.schema, value);
6192
+ let instance;
6193
+ try {
6194
+ instance = instantiateRefEncodedFromRaw(this.schema, value);
6195
+ } catch (error) {
6196
+ if (!(error instanceof SchemaUnionNoMatchingVariantError)) {
6197
+ throw error;
6198
+ }
6199
+ this.migrationFailed = true;
6200
+ this.migrated = true;
6201
+ console.error(
6202
+ `Schema instantiation failed for ${this.id}: ${error.message}`
6203
+ );
6204
+ this.handleUpdate(CoValueLoadingState.UNAVAILABLE);
6205
+ return;
6206
+ }
6187
6207
  try {
6188
6208
  applyCoValueMigrations(instance);
6189
6209
  } catch (error) {
@@ -6995,6 +7015,48 @@ function accessChildById(parent, childId, schema) {
6995
7015
  import {
6996
7016
  LocalNode as LocalNode5
6997
7017
  } from "cojson";
7018
+
7019
+ // src/tools/implementation/sessionDurabilityMarker.ts
7020
+ function sessionDurabilityMarkerKey(sessionID) {
7021
+ return `jazz_session_dirty_${sessionID}`;
7022
+ }
7023
+ var SESSION_DURABILITY_CLEAR_DEBOUNCE_MS = 200;
7024
+ function makeDurabilityMarkerListener(marker, clearDebounceMs = SESSION_DURABILITY_CLEAR_DEBOUNCE_MS) {
7025
+ if (!marker) {
7026
+ return void 0;
7027
+ }
7028
+ let clearTimer;
7029
+ let markerIsSet = false;
7030
+ return (hasPending, sessionID) => {
7031
+ if (clearTimer !== void 0) {
7032
+ clearTimeout(clearTimer);
7033
+ clearTimer = void 0;
7034
+ }
7035
+ if (hasPending) {
7036
+ if (markerIsSet) {
7037
+ return;
7038
+ }
7039
+ try {
7040
+ marker.set(sessionID);
7041
+ markerIsSet = true;
7042
+ } catch (err) {
7043
+ console.warn("Failed to set session durability marker", err);
7044
+ }
7045
+ } else {
7046
+ clearTimer = setTimeout(() => {
7047
+ clearTimer = void 0;
7048
+ try {
7049
+ marker.clear(sessionID);
7050
+ markerIsSet = false;
7051
+ } catch (err) {
7052
+ console.warn("Failed to clear session durability marker", err);
7053
+ }
7054
+ }, clearDebounceMs);
7055
+ }
7056
+ };
7057
+ }
7058
+
7059
+ // src/tools/implementation/createContext.ts
6998
7060
  var MockSessionProvider = class {
6999
7061
  async acquireSession(accountID, crypto2) {
7000
7062
  return {
@@ -7021,12 +7083,19 @@ async function createJazzContextFromExistingCredentials({
7021
7083
  AccountSchema: PropsAccountSchema,
7022
7084
  sessionProvider,
7023
7085
  onLogOut,
7024
- asActiveAccount
7086
+ asActiveAccount,
7087
+ experimental_clockSyncFromServerPings
7025
7088
  }) {
7026
7089
  const { sessionID, sessionDone } = await sessionProvider.acquireSession(
7027
7090
  credentials.accountID,
7028
7091
  crypto2
7029
7092
  );
7093
+ if (sessionProvider.durabilityMarker && await sessionProvider.durabilityMarker.isSet(sessionID)) {
7094
+ console.warn(
7095
+ "Session provider returned a session still marked unsafe to reuse; a crash may fork it",
7096
+ sessionID
7097
+ );
7098
+ }
7030
7099
  const CurrentAccountSchema = PropsAccountSchema ?? RegisteredSchemas["Account"];
7031
7100
  const AccountClass = coValueClassFromCoValueClassOrSchema(CurrentAccountSchema);
7032
7101
  const node = await LocalNode5.withLoadedAccount({
@@ -7038,6 +7107,10 @@ async function createJazzContextFromExistingCredentials({
7038
7107
  crypto: crypto2,
7039
7108
  storage,
7040
7109
  enableFullStorageReconciliation: !!storage,
7110
+ experimental_clockSyncFromServerPings,
7111
+ onLocalStoreDurabilityChange: makeDurabilityMarkerListener(
7112
+ sessionProvider.durabilityMarker
7113
+ ),
7041
7114
  migration: async (rawAccount, _node, creationProps) => {
7042
7115
  const account2 = AccountClass.fromRaw(rawAccount);
7043
7116
  if (asActiveAccount) {
@@ -7073,7 +7146,8 @@ async function createJazzContextForNewAccount({
7073
7146
  AccountSchema: PropsAccountSchema,
7074
7147
  onLogOut,
7075
7148
  storage,
7076
- sessionProvider
7149
+ sessionProvider,
7150
+ experimental_clockSyncFromServerPings
7077
7151
  }) {
7078
7152
  const CurrentAccountSchema = PropsAccountSchema ?? RegisteredSchemas["Account"];
7079
7153
  const AccountClass = coValueClassFromCoValueClassOrSchema(CurrentAccountSchema);
@@ -7085,6 +7159,10 @@ async function createJazzContextForNewAccount({
7085
7159
  initialAgentSecret,
7086
7160
  storage,
7087
7161
  enableFullStorageReconciliation: !!storage,
7162
+ experimental_clockSyncFromServerPings,
7163
+ onLocalStoreDurabilityChange: makeDurabilityMarkerListener(
7164
+ sessionProvider.durabilityMarker
7165
+ ),
7088
7166
  migration: async (rawAccount, _node, creationProps2) => {
7089
7167
  const account2 = AccountClass.fromRaw(rawAccount);
7090
7168
  activeAccountContext.set(account2);
@@ -7134,7 +7212,8 @@ async function createJazzContext(options) {
7134
7212
  await authSecretStorage.clearWithoutNotify();
7135
7213
  },
7136
7214
  storage: options.storage,
7137
- asActiveAccount: true
7215
+ asActiveAccount: true,
7216
+ experimental_clockSyncFromServerPings: options.experimental_clockSyncFromServerPings
7138
7217
  });
7139
7218
  } else {
7140
7219
  const secretSeed = options.crypto.newRandomSecretSeed();
@@ -7153,7 +7232,8 @@ async function createJazzContext(options) {
7153
7232
  onLogOut: async () => {
7154
7233
  await authSecretStorage.clearWithoutNotify();
7155
7234
  },
7156
- storage: options.storage
7235
+ storage: options.storage,
7236
+ experimental_clockSyncFromServerPings: options.experimental_clockSyncFromServerPings
7157
7237
  });
7158
7238
  if (!options.newAccountProps) {
7159
7239
  await authSecretStorage.setWithoutNotify({
@@ -7173,7 +7253,8 @@ function createAnonymousJazzContext({
7173
7253
  peers,
7174
7254
  syncWhen,
7175
7255
  crypto: crypto2,
7176
- storage
7256
+ storage,
7257
+ experimental_clockSyncFromServerPings
7177
7258
  }) {
7178
7259
  const agentSecret = crypto2.newRandomAgentSecret();
7179
7260
  const node = new LocalNode5(
@@ -7181,7 +7262,8 @@ function createAnonymousJazzContext({
7181
7262
  crypto2.newRandomSessionID(crypto2.getAgentID(agentSecret)),
7182
7263
  crypto2,
7183
7264
  syncWhen,
7184
- !!storage
7265
+ !!storage,
7266
+ { experimental_clockSyncFromServerPings }
7185
7267
  );
7186
7268
  for (const peer of peers) {
7187
7269
  node.syncManager.addPeer(peer);
@@ -7507,11 +7589,17 @@ var CoMapSchema2 = class {
7507
7589
  * @returns A new CoMap schema with the additional fields.
7508
7590
  */
7509
7591
  extend(shape) {
7592
+ const extendedShape = {};
7593
+ Object.defineProperties(
7594
+ extendedShape,
7595
+ Object.getOwnPropertyDescriptors(this.shape)
7596
+ );
7597
+ Object.defineProperties(
7598
+ extendedShape,
7599
+ Object.getOwnPropertyDescriptors(shape)
7600
+ );
7510
7601
  return this.copy({
7511
- shape: {
7512
- ...this.shape,
7513
- ...shape
7514
- }
7602
+ shape: extendedShape
7515
7603
  });
7516
7604
  }
7517
7605
  /**
@@ -7523,11 +7611,17 @@ var CoMapSchema2 = class {
7523
7611
  * @returns A new CoMap schema with the additional fields.
7524
7612
  */
7525
7613
  safeExtend(shape) {
7614
+ const extendedShape = {};
7615
+ Object.defineProperties(
7616
+ extendedShape,
7617
+ Object.getOwnPropertyDescriptors(this.shape)
7618
+ );
7619
+ Object.defineProperties(
7620
+ extendedShape,
7621
+ Object.getOwnPropertyDescriptors(shape)
7622
+ );
7526
7623
  return this.copy({
7527
- shape: {
7528
- ...this.shape,
7529
- ...shape
7530
- }
7624
+ shape: extendedShape
7531
7625
  });
7532
7626
  }
7533
7627
  /**
@@ -9506,6 +9600,8 @@ export {
9506
9600
  setCustomErrorReporter,
9507
9601
  captureStack,
9508
9602
  SubscriptionScope,
9603
+ sessionDurabilityMarkerKey,
9604
+ makeDurabilityMarkerListener,
9509
9605
  MockSessionProvider,
9510
9606
  createJazzContextFromExistingCredentials,
9511
9607
  createJazzContextForNewAccount,
@@ -9519,4 +9615,4 @@ export {
9519
9615
  JazzContextManager
9520
9616
  };
9521
9617
  /* istanbul ignore file -- @preserve */
9522
- //# sourceMappingURL=chunk-3BV3JUMV.js.map
9618
+ //# sourceMappingURL=chunk-VPK4IGIA.js.map