jazz-tools 0.15.8 → 0.15.10

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 (47) hide show
  1. package/.turbo/turbo-build.log +45 -45
  2. package/CHANGELOG.md +21 -0
  3. package/dist/browser/createBrowserContext.d.ts.map +1 -1
  4. package/dist/browser/index.js +9 -7
  5. package/dist/browser/index.js.map +1 -1
  6. package/dist/{chunk-DGFPP22M.js → chunk-V54IPL5M.js} +31 -6
  7. package/dist/chunk-V54IPL5M.js.map +1 -0
  8. package/dist/index.js +1 -1
  9. package/dist/inspector/{custom-element-I7Q6H5E5.js → custom-element-TUXKXSZU.js} +18791 -18806
  10. package/dist/inspector/custom-element-TUXKXSZU.js.map +1 -0
  11. package/dist/inspector/register-custom-element.js +1 -1
  12. package/dist/react-native-core/index.d.ts +1 -1
  13. package/dist/react-native-core/index.d.ts.map +1 -1
  14. package/dist/react-native-core/index.js +15 -31
  15. package/dist/react-native-core/index.js.map +1 -1
  16. package/dist/react-native-core/platform.d.ts +1 -1
  17. package/dist/react-native-core/platform.d.ts.map +1 -1
  18. package/dist/testing.js +22 -3
  19. package/dist/testing.js.map +1 -1
  20. package/dist/tools/implementation/createContext.d.ts +8 -4
  21. package/dist/tools/implementation/createContext.d.ts.map +1 -1
  22. package/dist/tools/implementation/zodSchema/zodReExport.d.ts +1 -1
  23. package/dist/tools/implementation/zodSchema/zodReExport.d.ts.map +1 -1
  24. package/dist/tools/subscribe/SubscriptionScope.d.ts +2 -0
  25. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  26. package/dist/tools/testing.d.ts +3 -1
  27. package/dist/tools/testing.d.ts.map +1 -1
  28. package/package.json +7 -7
  29. package/src/browser/createBrowserContext.ts +8 -6
  30. package/src/react-core/tests/useCoState.test.ts +0 -12
  31. package/src/react-native-core/index.ts +1 -1
  32. package/src/react-native-core/platform.ts +13 -12
  33. package/src/tools/implementation/createContext.ts +16 -0
  34. package/src/tools/implementation/zodSchema/zodReExport.ts +1 -0
  35. package/src/tools/subscribe/SubscriptionScope.ts +21 -2
  36. package/src/tools/testing.ts +25 -2
  37. package/src/tools/tests/coFeed.test.ts +190 -239
  38. package/src/tools/tests/coMap.test.ts +0 -8
  39. package/src/tools/tests/coPlainText.test.ts +2 -1
  40. package/src/tools/tests/deepLoading.test.ts +6 -6
  41. package/src/tools/tests/load.test.ts +65 -30
  42. package/src/tools/tests/subscribe.test.ts +92 -0
  43. package/dist/chunk-DGFPP22M.js.map +0 -1
  44. package/dist/inspector/custom-element-I7Q6H5E5.js.map +0 -1
  45. package/dist/react-native-core/storage/sqlite-react-native.d.ts +0 -9
  46. package/dist/react-native-core/storage/sqlite-react-native.d.ts.map +0 -1
  47. package/src/react-native-core/storage/sqlite-react-native.ts +0 -19
package/package.json CHANGED
@@ -139,7 +139,7 @@
139
139
  },
140
140
  "type": "module",
141
141
  "license": "MIT",
142
- "version": "0.15.8",
142
+ "version": "0.15.10",
143
143
  "dependencies": {
144
144
  "@manuscripts/prosemirror-recreate-steps": "^0.1.4",
145
145
  "@scure/base": "1.2.1",
@@ -158,10 +158,9 @@
158
158
  "prosemirror-state": "^1.4.3",
159
159
  "prosemirror-transform": "^1.9.0",
160
160
  "zod": "3.25.28",
161
- "cojson": "0.15.8",
162
- "cojson-storage": "0.15.8",
163
- "cojson-storage-indexeddb": "0.15.8",
164
- "cojson-transport-ws": "0.15.8"
161
+ "cojson": "0.15.10",
162
+ "cojson-storage-indexeddb": "0.15.10",
163
+ "cojson-transport-ws": "0.15.10"
165
164
  },
166
165
  "devDependencies": {
167
166
  "@scure/bip39": "^1.3.0",
@@ -169,9 +168,10 @@
169
168
  "@testing-library/dom": "^10.4.0",
170
169
  "@testing-library/jest-dom": "^6.6.3",
171
170
  "@testing-library/react": "16.2.0",
172
- "@types/react": "19.0.0",
173
- "@types/react-dom": "19.0.0",
171
+ "@types/react": "19.1.0",
172
+ "@types/react-dom": "19.1.0",
174
173
  "@vitest/browser": "^3.2.4",
174
+ "queueueue": "^4.1.2",
175
175
  "playwright": "^1.50.1",
176
176
  "tsup": "8.5.0",
177
177
  "typescript": "5.6.2",
@@ -1,5 +1,5 @@
1
1
  import { LocalNode, Peer, RawAccountID } from "cojson";
2
- import { IDBStorage } from "cojson-storage-indexeddb";
2
+ import { getIndexedDBStorage } from "cojson-storage-indexeddb";
3
3
  import { WebSocketPeerWithReconnection } from "cojson-transport-ws";
4
4
  import { WasmCrypto } from "cojson/crypto/WasmCrypto";
5
5
  import {
@@ -55,14 +55,13 @@ async function setupPeers(options: BaseBrowserContextOptions) {
55
55
 
56
56
  const peersToLoadFrom: Peer[] = [];
57
57
 
58
- if (useIndexedDB) {
59
- peersToLoadFrom.push(await IDBStorage.asPeer());
60
- }
58
+ const storage = useIndexedDB ? await getIndexedDBStorage() : undefined;
61
59
 
62
60
  if (options.sync.when === "never") {
63
61
  return {
64
62
  toggleNetwork: () => {},
65
63
  peersToLoadFrom,
64
+ storage,
66
65
  setNode: () => {},
67
66
  crypto,
68
67
  };
@@ -102,6 +101,7 @@ async function setupPeers(options: BaseBrowserContextOptions) {
102
101
  return {
103
102
  toggleNetwork,
104
103
  peersToLoadFrom,
104
+ storage,
105
105
  setNode,
106
106
  crypto,
107
107
  };
@@ -110,12 +110,13 @@ async function setupPeers(options: BaseBrowserContextOptions) {
110
110
  export async function createJazzBrowserGuestContext(
111
111
  options: BaseBrowserContextOptions,
112
112
  ) {
113
- const { toggleNetwork, peersToLoadFrom, setNode, crypto } =
113
+ const { toggleNetwork, peersToLoadFrom, setNode, crypto, storage } =
114
114
  await setupPeers(options);
115
115
 
116
116
  const context = await createAnonymousJazzContext({
117
117
  crypto,
118
118
  peersToLoadFrom,
119
+ storage,
119
120
  });
120
121
 
121
122
  setNode(context.agent.node);
@@ -152,7 +153,7 @@ export async function createJazzBrowserContext<
152
153
  | (AccountClass<Account> & CoValueFromRaw<Account>)
153
154
  | AnyAccountSchema,
154
155
  >(options: BrowserContextOptions<S>) {
155
- const { toggleNetwork, peersToLoadFrom, setNode, crypto } =
156
+ const { toggleNetwork, peersToLoadFrom, setNode, crypto, storage } =
156
157
  await setupPeers(options);
157
158
 
158
159
  let unsubscribeAuthUpdate = () => {};
@@ -177,6 +178,7 @@ export async function createJazzBrowserContext<
177
178
  credentials: options.credentials,
178
179
  newAccountProps: options.newAccountProps,
179
180
  peersToLoadFrom,
181
+ storage,
180
182
  crypto,
181
183
  defaultProfileName: options.defaultProfileName,
182
184
  AccountSchema: options.AccountSchema,
@@ -160,8 +160,6 @@ describe("useCoState", () => {
160
160
  account,
161
161
  });
162
162
 
163
- expect(result.current).toBeUndefined();
164
-
165
163
  await waitFor(() => {
166
164
  expect(result.current).toBeNull();
167
165
  });
@@ -191,8 +189,6 @@ describe("useCoState", () => {
191
189
  account,
192
190
  });
193
191
 
194
- expect(result.current).toBeUndefined();
195
-
196
192
  await waitFor(() => {
197
193
  expect(result.current).toBeNull();
198
194
  });
@@ -225,8 +221,6 @@ describe("useCoState", () => {
225
221
  account,
226
222
  });
227
223
 
228
- expect(result.current).toBeUndefined();
229
-
230
224
  await waitFor(() => {
231
225
  expect(result.current?.value).toBe("123");
232
226
  });
@@ -258,8 +252,6 @@ describe("useCoState", () => {
258
252
  account,
259
253
  });
260
254
 
261
- expect(result.current).toBeUndefined();
262
-
263
255
  await waitFor(() => {
264
256
  expect(result.current).toBeNull();
265
257
  });
@@ -303,8 +295,6 @@ describe("useCoState", () => {
303
295
  account,
304
296
  });
305
297
 
306
- expect(result.current).toBeUndefined();
307
-
308
298
  await waitFor(() => {
309
299
  expect(result.current).not.toBeUndefined();
310
300
  });
@@ -371,8 +361,6 @@ describe("useCoState", () => {
371
361
  },
372
362
  );
373
363
 
374
- expect(result.current).toBeUndefined();
375
-
376
364
  await waitFor(() => {
377
365
  expect(result.current).not.toBeUndefined();
378
366
  });
@@ -4,6 +4,6 @@ export * from "./media.js";
4
4
  export * from "./provider.js";
5
5
  export * from "./storage/kv-store-context.js";
6
6
 
7
- export { SQLiteDatabaseDriverAsync } from "cojson-storage";
7
+ export { SQLiteDatabaseDriverAsync } from "cojson";
8
8
  export { parseInviteLink } from "jazz-tools";
9
9
  export { createInviteLink, setupKvStore } from "./platform.js";
@@ -1,5 +1,5 @@
1
1
  import NetInfo from "@react-native-community/netinfo";
2
- import { LocalNode, Peer, RawAccountID } from "cojson";
2
+ import { LocalNode, Peer, RawAccountID, getSqliteStorageAsync } from "cojson";
3
3
  import { PureJSCrypto } from "cojson/dist/crypto/PureJSCrypto"; // Importing from dist to not rely on the exports field
4
4
  import {
5
5
  Account,
@@ -20,9 +20,8 @@ import {
20
20
  createJazzContext,
21
21
  } from "jazz-tools";
22
22
  import { KvStore, KvStoreContext } from "./storage/kv-store-context.js";
23
- import { SQLiteReactNative } from "./storage/sqlite-react-native.js";
24
23
 
25
- import { SQLiteDatabaseDriverAsync } from "cojson-storage";
24
+ import { SQLiteDatabaseDriverAsync } from "cojson";
26
25
  import { WebSocketPeerWithReconnection } from "cojson-transport-ws";
27
26
  import type { RNQuickCrypto } from "jazz-tools/react-native-core/crypto";
28
27
 
@@ -49,12 +48,10 @@ async function setupPeers(options: BaseReactNativeContextOptions) {
49
48
 
50
49
  const peersToLoadFrom: Peer[] = [];
51
50
 
52
- if (options.storage && options.storage !== "disabled") {
53
- const storage = await SQLiteReactNative.asPeer({
54
- adapter: options.storage,
55
- });
56
- peersToLoadFrom.push(storage);
57
- }
51
+ const storage =
52
+ options.storage && options.storage !== "disabled"
53
+ ? await getSqliteStorageAsync(options.storage)
54
+ : undefined;
58
55
 
59
56
  if (options.sync.when === "never") {
60
57
  return {
@@ -62,6 +59,7 @@ async function setupPeers(options: BaseReactNativeContextOptions) {
62
59
  peersToLoadFrom,
63
60
  setNode: () => {},
64
61
  crypto,
62
+ storage,
65
63
  };
66
64
  }
67
65
 
@@ -101,18 +99,20 @@ async function setupPeers(options: BaseReactNativeContextOptions) {
101
99
  peersToLoadFrom,
102
100
  setNode,
103
101
  crypto,
102
+ storage,
104
103
  };
105
104
  }
106
105
 
107
106
  export async function createJazzReactNativeGuestContext(
108
107
  options: BaseReactNativeContextOptions,
109
108
  ) {
110
- const { toggleNetwork, peersToLoadFrom, setNode, crypto } =
109
+ const { toggleNetwork, peersToLoadFrom, setNode, crypto, storage } =
111
110
  await setupPeers(options);
112
111
 
113
- const context = await createAnonymousJazzContext({
112
+ const context = createAnonymousJazzContext({
114
113
  crypto,
115
114
  peersToLoadFrom,
115
+ storage,
116
116
  });
117
117
 
118
118
  setNode(context.agent.node);
@@ -149,7 +149,7 @@ export async function createJazzReactNativeContext<
149
149
  | (AccountClass<Account> & CoValueFromRaw<Account>)
150
150
  | AnyAccountSchema,
151
151
  >(options: ReactNativeContextOptions<S>) {
152
- const { toggleNetwork, peersToLoadFrom, setNode, crypto } =
152
+ const { toggleNetwork, peersToLoadFrom, setNode, crypto, storage } =
153
153
  await setupPeers(options);
154
154
 
155
155
  let unsubscribeAuthUpdate = () => {};
@@ -182,6 +182,7 @@ export async function createJazzReactNativeContext<
182
182
  AccountSchema: options.AccountSchema,
183
183
  sessionProvider: provideLockSession,
184
184
  authSecretStorage: options.authSecretStorage,
185
+ storage,
185
186
  });
186
187
 
187
188
  setNode(context.node);
@@ -8,6 +8,7 @@ import {
8
8
  RawAccount,
9
9
  RawAccountID,
10
10
  SessionID,
11
+ StorageAPI,
11
12
  } from "cojson";
12
13
  import { AuthSecretStorage } from "../auth/AuthSecretStorage.js";
13
14
  import { type Account, type AccountClass } from "../coValues/account.js";
@@ -93,6 +94,7 @@ export async function createJazzContextFromExistingCredentials<
93
94
  credentials,
94
95
  peersToLoadFrom,
95
96
  crypto,
97
+ storage,
96
98
  AccountSchema: PropsAccountSchema,
97
99
  sessionProvider,
98
100
  onLogOut,
@@ -103,6 +105,7 @@ export async function createJazzContextFromExistingCredentials<
103
105
  AccountSchema?: S;
104
106
  sessionProvider: SessionProvider;
105
107
  onLogOut?: () => void;
108
+ storage?: StorageAPI;
106
109
  }): Promise<JazzContextWithAccount<InstanceOfSchema<S>>> {
107
110
  const { sessionID, sessionDone } = await sessionProvider(
108
111
  credentials.accountID,
@@ -120,6 +123,7 @@ export async function createJazzContextFromExistingCredentials<
120
123
  sessionID: sessionID,
121
124
  peersToLoadFrom: peersToLoadFrom,
122
125
  crypto: crypto,
126
+ storage,
123
127
  migration: async (rawAccount, _node, creationProps) => {
124
128
  const account = AccountClass.fromRaw(rawAccount) as InstanceOfSchema<S>;
125
129
  activeAccountContext.set(account);
@@ -157,6 +161,7 @@ export async function createJazzContextForNewAccount<
157
161
  crypto,
158
162
  AccountSchema: PropsAccountSchema,
159
163
  onLogOut,
164
+ storage,
160
165
  }: {
161
166
  creationProps: { name: string };
162
167
  initialAgentSecret?: AgentSecret;
@@ -164,6 +169,7 @@ export async function createJazzContextForNewAccount<
164
169
  crypto: CryptoProvider;
165
170
  AccountSchema?: S;
166
171
  onLogOut?: () => Promise<void>;
172
+ storage?: StorageAPI;
167
173
  }): Promise<JazzContextWithAccount<InstanceOfSchema<S>>> {
168
174
  const CurrentAccountSchema =
169
175
  PropsAccountSchema ?? (RegisteredSchemas["Account"] as unknown as S);
@@ -175,6 +181,7 @@ export async function createJazzContextForNewAccount<
175
181
  peersToLoadFrom,
176
182
  crypto,
177
183
  initialAgentSecret,
184
+ storage,
178
185
  migration: async (rawAccount, _node, creationProps) => {
179
186
  const account = AccountClass.fromRaw(rawAccount) as InstanceOfSchema<S>;
180
187
  activeAccountContext.set(account);
@@ -212,6 +219,7 @@ export async function createJazzContext<
212
219
  AccountSchema?: S;
213
220
  sessionProvider: SessionProvider;
214
221
  authSecretStorage: AuthSecretStorage;
222
+ storage?: StorageAPI;
215
223
  }) {
216
224
  const crypto = options.crypto;
217
225
 
@@ -236,6 +244,7 @@ export async function createJazzContext<
236
244
  onLogOut: () => {
237
245
  authSecretStorage.clearWithoutNotify();
238
246
  },
247
+ storage: options.storage,
239
248
  });
240
249
  } else {
241
250
  const secretSeed = options.crypto.newRandomSecretSeed();
@@ -257,6 +266,7 @@ export async function createJazzContext<
257
266
  onLogOut: async () => {
258
267
  await authSecretStorage.clearWithoutNotify();
259
268
  },
269
+ storage: options.storage,
260
270
  });
261
271
 
262
272
  if (!options.newAccountProps) {
@@ -278,9 +288,11 @@ export async function createJazzContext<
278
288
  export function createAnonymousJazzContext({
279
289
  peersToLoadFrom,
280
290
  crypto,
291
+ storage,
281
292
  }: {
282
293
  peersToLoadFrom: Peer[];
283
294
  crypto: CryptoProvider;
295
+ storage?: StorageAPI;
284
296
  }): JazzContextWithAgent {
285
297
  const agentSecret = crypto.newRandomAgentSecret();
286
298
 
@@ -294,6 +306,10 @@ export function createAnonymousJazzContext({
294
306
  node.syncManager.addPeer(peer);
295
307
  }
296
308
 
309
+ if (storage) {
310
+ node.setStorage(storage);
311
+ }
312
+
297
313
  activeAccountContext.setGuestMode();
298
314
 
299
315
  return {
@@ -39,5 +39,6 @@ export {
39
39
  type ZodDefault,
40
40
  type ZodCatch,
41
41
  type output as infer,
42
+ type ZodDiscriminatedUnion,
42
43
  z,
43
44
  } from "zod/v4";
@@ -126,8 +126,7 @@ export class SubscriptionScope<D extends CoValue> {
126
126
  new JazzError(this.id, "unauthorized", [
127
127
  {
128
128
  code: "unauthorized",
129
- message:
130
- "The current user is not authorized to access this value",
129
+ message: `The current user (${this.node.getCurrentAgent().id}) is not authorized to access this value`,
131
130
  params: {
132
131
  id: this.id,
133
132
  },
@@ -249,6 +248,10 @@ export class SubscriptionScope<D extends CoValue> {
249
248
  // If the value is in error, we send the update regardless of the children statuses
250
249
  if (this.value.type !== "loaded") return true;
251
250
 
251
+ if (this.isStreaming() && !this.isFileStream()) {
252
+ return false;
253
+ }
254
+
252
255
  for (const value of this.childValues.values()) {
253
256
  // We don't wait for autoloaded values to be loaded, in order to stream updates
254
257
  // on autoloaded lists or records
@@ -285,6 +288,22 @@ export class SubscriptionScope<D extends CoValue> {
285
288
  return undefined;
286
289
  }
287
290
 
291
+ isStreaming() {
292
+ if (this.value.type !== "loaded") {
293
+ return false;
294
+ }
295
+
296
+ return this.value.value._raw.core.verified.isStreaming();
297
+ }
298
+
299
+ isFileStream() {
300
+ if (this.value.type !== "loaded") {
301
+ return false;
302
+ }
303
+
304
+ return this.value.value._raw.core.verified.header.meta?.type === "binary";
305
+ }
306
+
288
307
  triggerUpdate() {
289
308
  if (!this.shouldSendUpdates()) return;
290
309
  if (!this.dirty) return;
@@ -19,7 +19,10 @@ import {
19
19
  randomSessionProvider,
20
20
  } from "./internal.js";
21
21
 
22
- const syncServer: { current: LocalNode | null } = { current: null };
22
+ const syncServer: { current: LocalNode | null; asyncPeers: boolean } = {
23
+ current: null,
24
+ asyncPeers: false,
25
+ };
23
26
 
24
27
  export class TestJSCrypto extends PureJSCrypto {
25
28
  static async create() {
@@ -57,6 +60,23 @@ export function getPeerConnectedToTestSyncServer() {
57
60
  peer2role: "server",
58
61
  },
59
62
  );
63
+
64
+ if (syncServer.asyncPeers) {
65
+ const push = aPeer.outgoing.push;
66
+
67
+ aPeer.outgoing.push = (message) => {
68
+ setTimeout(() => {
69
+ push.call(aPeer.outgoing, message);
70
+ });
71
+ };
72
+
73
+ bPeer.outgoing.push = (message) => {
74
+ setTimeout(() => {
75
+ push.call(bPeer.outgoing, message);
76
+ });
77
+ };
78
+ }
79
+
60
80
  syncServer.current.syncManager.addPeer(aPeer);
61
81
 
62
82
  return bPeer;
@@ -283,7 +303,9 @@ export async function linkAccounts(
283
303
  await b.waitForAllCoValuesSync();
284
304
  }
285
305
 
286
- export async function setupJazzTestSync() {
306
+ export async function setupJazzTestSync({
307
+ asyncPeers = false,
308
+ }: { asyncPeers?: boolean } = {}) {
287
309
  if (syncServer.current) {
288
310
  syncServer.current.gracefulShutdown();
289
311
  }
@@ -296,6 +318,7 @@ export async function setupJazzTestSync() {
296
318
  });
297
319
 
298
320
  syncServer.current = account._raw.core.node;
321
+ syncServer.asyncPeers = asyncPeers;
299
322
 
300
323
  return account;
301
324
  }