jazz-tools 0.15.7 → 0.15.9

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 (56) hide show
  1. package/.turbo/turbo-build.log +42 -42
  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-AV3MKD64.js → chunk-5PFEKHX5.js} +30 -5
  7. package/dist/chunk-5PFEKHX5.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/index.js +1 -1
  13. package/dist/react/index.js.map +1 -1
  14. package/dist/react/testing.js +1 -1
  15. package/dist/react/testing.js.map +1 -1
  16. package/dist/react-native-core/index.d.ts +1 -1
  17. package/dist/react-native-core/index.d.ts.map +1 -1
  18. package/dist/react-native-core/index.js +15 -31
  19. package/dist/react-native-core/index.js.map +1 -1
  20. package/dist/react-native-core/platform.d.ts +1 -1
  21. package/dist/react-native-core/platform.d.ts.map +1 -1
  22. package/dist/testing.js +22 -3
  23. package/dist/testing.js.map +1 -1
  24. package/dist/tools/exports.d.ts +1 -1
  25. package/dist/tools/exports.d.ts.map +1 -1
  26. package/dist/tools/implementation/createContext.d.ts +8 -4
  27. package/dist/tools/implementation/createContext.d.ts.map +1 -1
  28. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts +12 -13
  29. package/dist/tools/implementation/zodSchema/schemaTypes/AccountSchema.d.ts.map +1 -1
  30. package/dist/tools/implementation/zodSchema/zodCo.d.ts +39 -9
  31. package/dist/tools/implementation/zodSchema/zodCo.d.ts.map +1 -1
  32. package/dist/tools/subscribe/SubscriptionScope.d.ts +2 -0
  33. package/dist/tools/subscribe/SubscriptionScope.d.ts.map +1 -1
  34. package/dist/tools/testing.d.ts +3 -1
  35. package/dist/tools/testing.d.ts.map +1 -1
  36. package/package.json +7 -7
  37. package/src/browser/createBrowserContext.ts +8 -6
  38. package/src/react-core/tests/useCoState.test.ts +0 -12
  39. package/src/react-native-core/index.ts +1 -1
  40. package/src/react-native-core/platform.ts +13 -12
  41. package/src/tools/exports.ts +2 -0
  42. package/src/tools/implementation/createContext.ts +16 -0
  43. package/src/tools/implementation/zodSchema/schemaTypes/AccountSchema.ts +17 -15
  44. package/src/tools/implementation/zodSchema/zodCo.ts +54 -32
  45. package/src/tools/subscribe/SubscriptionScope.ts +20 -0
  46. package/src/tools/testing.ts +25 -2
  47. package/src/tools/tests/coFeed.test.ts +190 -239
  48. package/src/tools/tests/coMap.test.ts +0 -8
  49. package/src/tools/tests/coPlainText.test.ts +2 -1
  50. package/src/tools/tests/load.test.ts +65 -30
  51. package/src/tools/tests/subscribe.test.ts +92 -0
  52. package/dist/chunk-AV3MKD64.js.map +0 -1
  53. package/dist/inspector/custom-element-I7Q6H5E5.js.map +0 -1
  54. package/dist/react-native-core/storage/sqlite-react-native.d.ts +0 -9
  55. package/dist/react-native-core/storage/sqlite-react-native.d.ts.map +0 -1
  56. package/src/react-native-core/storage/sqlite-react-native.ts +0 -19
@@ -1,5 +1,13 @@
1
1
  import { WasmCrypto } from "cojson/crypto/WasmCrypto";
2
- import { describe, expect, expectTypeOf, test } from "vitest";
2
+ import { Channel } from "queueueue";
3
+ import {
4
+ assert,
5
+ beforeEach,
6
+ describe,
7
+ expect,
8
+ expectTypeOf,
9
+ test,
10
+ } from "vitest";
3
11
  import {
4
12
  Account,
5
13
  FileStream,
@@ -14,12 +22,24 @@ import {
14
22
  createJazzContextFromExistingCredentials,
15
23
  randomSessionProvider,
16
24
  } from "../internal.js";
17
- import { createJazzTestAccount } from "../testing.js";
25
+ import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
18
26
  import { setupTwoNodes } from "./utils.js";
19
27
 
20
28
  const Crypto = await WasmCrypto.create();
21
29
 
22
- const connectedPeers = cojsonInternals.connectedPeers;
30
+ let me = await Account.create({
31
+ creationProps: { name: "Hermes Puggington" },
32
+ crypto: Crypto,
33
+ });
34
+
35
+ beforeEach(async () => {
36
+ await setupJazzTestSync();
37
+
38
+ me = await createJazzTestAccount({
39
+ isCurrentActiveAccount: true,
40
+ creationProps: { name: "Hermes Puggington" },
41
+ });
42
+ });
23
43
 
24
44
  describe("Simple CoFeed operations", async () => {
25
45
  const me = await Account.create({
@@ -72,19 +92,20 @@ describe("CoFeed resolution", async () => {
72
92
  const TestStream = co.feed(NestedStream);
73
93
 
74
94
  const initNodeAndStream = async () => {
75
- const me = await Account.create({
76
- creationProps: { name: "Hermes Puggington" },
77
- crypto: Crypto,
95
+ const me = await createJazzTestAccount({
96
+ isCurrentActiveAccount: true,
78
97
  });
79
98
 
99
+ const group = Group.create(me);
100
+ group.makePublic();
80
101
  const stream = TestStream.create(
81
102
  [
82
103
  NestedStream.create(
83
- [TwiceNestedStream.create(["milk"], { owner: me })],
84
- { owner: me },
104
+ [TwiceNestedStream.create(["milk"], { owner: group })],
105
+ { owner: group },
85
106
  ),
86
107
  ],
87
- { owner: me },
108
+ { owner: group },
88
109
  );
89
110
 
90
111
  return { me, stream };
@@ -105,177 +126,86 @@ describe("CoFeed resolution", async () => {
105
126
 
106
127
  test("Loading and availability", async () => {
107
128
  const { me, stream } = await initNodeAndStream();
108
- const [initialAsPeer, secondPeer] = connectedPeers("initial", "second", {
109
- peer1role: "server",
110
- peer2role: "client",
111
- });
112
- if (!isControlledAccount(me)) {
113
- throw "me is not a controlled account";
114
- }
115
- me._raw.core.node.syncManager.addPeer(secondPeer);
116
- const { account: meOnSecondPeer } =
117
- await createJazzContextFromExistingCredentials({
118
- credentials: {
119
- accountID: me.id,
120
- secret: me._raw.core.node.getCurrentAgent().agentSecret,
121
- },
122
- sessionProvider: randomSessionProvider,
123
- peersToLoadFrom: [initialAsPeer],
124
- crypto: Crypto,
125
- });
129
+
130
+ const anotherAccount = await createJazzTestAccount();
126
131
 
127
132
  const loadedStream = await TestStream.load(stream.id, {
128
- loadAs: meOnSecondPeer,
133
+ loadAs: anotherAccount,
129
134
  });
130
135
 
131
- // TODO: fix this
132
- // expectTypeOf(loadedStream?.[me.id]).not.toBeAny();
136
+ assert(loadedStream);
133
137
 
134
- expect(loadedStream?.perAccount[me.id]?.value).toEqual(null);
135
- expect(loadedStream?.perAccount[me.id]?.ref?.id).toEqual(
136
- stream.perAccount[me.id]?.value?.id,
137
- );
138
+ const myStream = loadedStream.perAccount[me.id];
138
139
 
139
- const loadedNestedStream = await NestedStream.load(
140
- stream.perAccount[me.id]!.value!.id,
141
- { loadAs: meOnSecondPeer },
142
- );
140
+ assert(myStream);
143
141
 
144
- // expect(loadedStream?.[me.id]?.value).toEqual(loadedNestedStream);
145
- expect(loadedStream?.perAccount[me.id]?.value?.id).toEqual(
146
- loadedNestedStream?.id,
147
- );
148
- expect(
149
- loadedStream?.perAccount[me.id]?.value?.perAccount[me.id]?.value,
150
- ).toEqual(null);
151
- // expect(loadedStream?.[me.id]?.ref?.value).toEqual(loadedNestedStream);
152
- expect(loadedStream?.perAccount[me.id]?.ref?.value?.id).toEqual(
153
- loadedNestedStream?.id,
154
- );
155
- expect(
156
- loadedStream?.perAccount[me.id]?.value?.perAccount[me.id]?.ref?.id,
157
- ).toEqual(stream.perAccount[me.id]?.value?.perAccount[me.id]?.value?.id);
142
+ expect(myStream.value).toBeTruthy();
158
143
 
159
- const loadedTwiceNestedStream = await TwiceNestedStream.load(
160
- stream.perAccount[me.id]!.value!.perAccount[me.id]!.value!.id,
161
- { loadAs: meOnSecondPeer },
162
- );
144
+ assert(myStream.value);
163
145
 
164
- // expect(loadedStream?.[me.id]?.value?.[me.id]?.value).toEqual(
165
- // loadedTwiceNestedStream
166
- // );
167
- expect(
168
- loadedStream?.perAccount[me.id]?.value?.perAccount[me.id]?.value?.id,
169
- ).toEqual(loadedTwiceNestedStream?.id);
170
- // expect(loadedStream?.[me.id]?.ref?.value).toEqual(loadedNestedStream);
171
- expect(loadedStream?.perAccount[me.id]?.ref?.value?.id).toEqual(
172
- loadedNestedStream?.id,
173
- );
174
- expect(
175
- loadedStream?.perAccount[me.id]?.value?.perAccount[me.id]?.ref?.value?.id,
176
- ).toEqual(loadedTwiceNestedStream?.id);
146
+ const loadedNestedStreamByMe = myStream.value.perAccount[me.id];
177
147
 
178
- const otherNestedStream = NestedStream.create(
179
- [TwiceNestedStream.create(["butter"], { owner: meOnSecondPeer })],
180
- { owner: meOnSecondPeer },
181
- );
182
- loadedStream?.push(otherNestedStream);
183
- // expect(loadedStream?.[me.id]?.value).toEqual(otherNestedStream);
184
- expect(loadedStream?.perAccount[me.id]?.value?.id).toEqual(
185
- otherNestedStream?.id,
186
- );
187
- expect(loadedStream?.perAccount[me.id]?.ref?.value?.id).toEqual(
188
- otherNestedStream?.id,
189
- );
190
- expect(
191
- loadedStream?.perAccount[me.id]?.value?.perAccount[me.id]?.value?.id,
192
- ).toEqual(otherNestedStream.perAccount[me.id]?.value?.id);
148
+ assert(loadedNestedStreamByMe);
149
+
150
+ expect(loadedNestedStreamByMe.value).toBeTruthy();
151
+
152
+ assert(loadedNestedStreamByMe.value);
153
+
154
+ const loadedTwiceNestedStreamByMe =
155
+ loadedNestedStreamByMe.value.perAccount[me.id];
156
+
157
+ assert(loadedTwiceNestedStreamByMe);
158
+
159
+ expect(loadedTwiceNestedStreamByMe.value).toBe("milk");
160
+
161
+ assert(loadedTwiceNestedStreamByMe.value);
193
162
  });
194
163
 
195
164
  test("Subscription & auto-resolution", async () => {
196
165
  const { me, stream } = await initNodeAndStream();
197
166
 
198
- const [initialAsPeer, secondAsPeer] = connectedPeers("initial", "second", {
199
- peer1role: "server",
200
- peer2role: "client",
201
- });
167
+ const anotherAccount = await createJazzTestAccount();
202
168
 
203
- me._raw.core.node.syncManager.addPeer(secondAsPeer);
204
- if (!isControlledAccount(me)) {
205
- throw "me is not a controlled account";
206
- }
207
- const { account: meOnSecondPeer } =
208
- await createJazzContextFromExistingCredentials({
209
- credentials: {
210
- accountID: me.id,
211
- secret: me._raw.core.node.getCurrentAgent().agentSecret,
212
- },
213
- sessionProvider: randomSessionProvider,
214
- peersToLoadFrom: [initialAsPeer],
215
- crypto: Crypto,
216
- });
217
-
218
- const queue = new cojsonInternals.Channel();
169
+ const queue = new Channel();
219
170
 
220
171
  TestStream.subscribe(
221
172
  stream.id,
222
- { loadAs: meOnSecondPeer },
173
+ { loadAs: anotherAccount },
223
174
  (subscribedStream) => {
224
175
  void queue.push(subscribedStream);
225
176
  },
226
177
  );
227
178
 
228
179
  const update1 = (await queue.next()).value;
229
- expect(update1.perAccount[me.id]?.value).toEqual(null);
230
-
231
- const update2 = (await queue.next()).value;
232
- expect(update2.perAccount[me.id]?.value).toBeDefined();
233
- expect(update2.perAccount[me.id]?.value?.perAccount[me.id]?.value).toBe(
234
- null,
235
- );
236
-
237
- const update3 = (await queue.next()).value;
238
180
  expect(
239
- update3.perAccount[me.id]?.value?.perAccount[me.id]?.value,
240
- ).toBeDefined();
241
- expect(
242
- update3.perAccount[me.id]?.value?.perAccount[me.id]?.value?.perAccount[
181
+ update1.perAccount[me.id]?.value?.perAccount[me.id]?.value?.perAccount[
243
182
  me.id
244
183
  ]?.value,
245
184
  ).toBe("milk");
246
185
 
247
- update3.perAccount[me.id]!.value!.perAccount[me.id]!.value!.push("bread");
248
-
249
- const update4 = (await queue.next()).value;
250
- expect(
251
- update4.perAccount[me.id]?.value?.perAccount[me.id]?.value?.perAccount[
252
- me.id
253
- ]?.value,
254
- ).toBe("bread");
255
-
256
186
  // When assigning a new nested stream, we get an update
257
187
  const newTwiceNested = TwiceNestedStream.create(["butter"], {
258
- owner: meOnSecondPeer,
188
+ owner: stream._owner,
259
189
  });
260
190
 
261
191
  const newNested = NestedStream.create([newTwiceNested], {
262
- owner: meOnSecondPeer,
192
+ owner: stream._owner,
263
193
  });
264
194
 
265
- update4.push(newNested);
195
+ stream.push(newNested);
266
196
 
267
- const update5 = (await queue.next()).value;
197
+ const update2 = (await queue.next()).value;
268
198
  expect(
269
- update5.perAccount[me.id]?.value?.perAccount[me.id]?.value?.perAccount[
199
+ update2.perAccount[me.id]?.value?.perAccount[me.id]?.value?.perAccount[
270
200
  me.id
271
201
  ]?.value,
272
202
  ).toBe("butter");
273
203
 
274
204
  // we get updates when the new nested stream changes
275
205
  newTwiceNested.push("jam");
276
- const update6 = (await queue.next()).value;
206
+ const update3 = (await queue.next()).value;
277
207
  expect(
278
- update6.perAccount[me.id]?.value?.perAccount[me.id]?.value?.perAccount[
208
+ update3.perAccount[me.id]?.value?.perAccount[me.id]?.value?.perAccount[
279
209
  me.id
280
210
  ]?.value,
281
211
  ).toBe("jam");
@@ -284,54 +214,24 @@ describe("CoFeed resolution", async () => {
284
214
  test("Subscription without options", async () => {
285
215
  const { me, stream } = await initNodeAndStream();
286
216
 
287
- const [initialAsPeer, secondAsPeer] = connectedPeers("initial", "second", {
288
- peer1role: "server",
289
- peer2role: "client",
290
- });
291
- if (!isControlledAccount(me)) {
292
- throw "me is not a controlled account";
293
- }
294
- me._raw.core.node.syncManager.addPeer(secondAsPeer);
295
-
296
- await createJazzContextFromExistingCredentials({
297
- credentials: {
298
- accountID: me.id,
299
- secret: me._raw.core.node.getCurrentAgent().agentSecret,
300
- },
301
- sessionProvider: randomSessionProvider,
302
- peersToLoadFrom: [initialAsPeer],
303
- crypto: Crypto,
304
- });
305
-
306
- const queue = new cojsonInternals.Channel<Loaded<typeof TestStream>>();
217
+ const queue = new Channel();
307
218
 
308
219
  TestStream.subscribe(stream.id, (subscribedStream) => {
309
220
  void queue.push(subscribedStream);
310
221
  });
311
222
 
312
223
  const update1 = (await queue.next()).value;
313
- expect(update1.perAccount[me.id]?.value).toEqual(null);
314
-
315
- const update2 = (await queue.next()).value;
316
- expect(update2.perAccount[me.id]?.value?.perAccount[me.id]?.value).toEqual(
317
- null,
318
- );
319
-
320
- const update3 = (await queue.next()).value;
321
224
  expect(
322
- update3.perAccount[me.id]?.value?.perAccount[me.id]?.value,
323
- ).toBeDefined();
324
- expect(
325
- update3.perAccount[me.id]?.value?.perAccount[me.id]?.value?.perAccount[
225
+ update1.perAccount[me.id]?.value?.perAccount[me.id]?.value?.perAccount[
326
226
  me.id
327
227
  ]?.value,
328
228
  ).toBe("milk");
329
229
 
330
- update3.perAccount[me.id]!.value!.perAccount[me.id]!.value!.push("bread");
230
+ stream.perAccount[me.id]!.value!.perAccount[me.id]!.value!.push("bread");
331
231
 
332
- const update4 = (await queue.next()).value;
232
+ const update2 = (await queue.next()).value;
333
233
  expect(
334
- update4.perAccount[me.id]?.value?.perAccount[me.id]?.value?.perAccount[
234
+ update2.perAccount[me.id]?.value?.perAccount[me.id]?.value?.perAccount[
335
235
  me.id
336
236
  ]?.value,
337
237
  ).toBe("bread");
@@ -395,12 +295,13 @@ describe("Simple FileStream operations", async () => {
395
295
 
396
296
  describe("FileStream loading & Subscription", async () => {
397
297
  const initNodeAndStream = async () => {
398
- const me = await Account.create({
399
- creationProps: { name: "Hermes Puggington" },
400
- crypto: Crypto,
298
+ const me = await createJazzTestAccount({
299
+ isCurrentActiveAccount: true,
401
300
  });
402
301
 
403
- const stream = FileStream.create({ owner: me });
302
+ const group = Group.create(me);
303
+ group.makePublic();
304
+ const stream = FileStream.create({ owner: group });
404
305
 
405
306
  stream.start({ mimeType: "text/plain" });
406
307
  stream.push(new Uint8Array([1, 2, 3]));
@@ -420,28 +321,11 @@ describe("FileStream loading & Subscription", async () => {
420
321
  });
421
322
 
422
323
  test("Loading and availability", async () => {
423
- const { me, stream } = await initNodeAndStream();
424
- const [initialAsPeer, secondAsPeer] = connectedPeers("initial", "second", {
425
- peer1role: "server",
426
- peer2role: "client",
427
- });
428
- if (!isControlledAccount(me)) {
429
- throw "me is not a controlled account";
430
- }
431
- me._raw.core.node.syncManager.addPeer(secondAsPeer);
432
- const { account: meOnSecondPeer } =
433
- await createJazzContextFromExistingCredentials({
434
- credentials: {
435
- accountID: me.id,
436
- secret: me._raw.core.node.getCurrentAgent().agentSecret,
437
- },
438
- sessionProvider: randomSessionProvider,
439
- peersToLoadFrom: [initialAsPeer],
440
- crypto: Crypto,
441
- });
324
+ const { stream } = await initNodeAndStream();
325
+ const anotherAccount = await createJazzTestAccount();
442
326
 
443
327
  const loadedStream = await FileStream.load(stream.id, {
444
- loadAs: meOnSecondPeer,
328
+ loadAs: anotherAccount,
445
329
  });
446
330
 
447
331
  expect(loadedStream?.getChunks()).toEqual({
@@ -453,32 +337,17 @@ describe("FileStream loading & Subscription", async () => {
453
337
 
454
338
  test("Subscription", async () => {
455
339
  const { me } = await initNodeAndStream();
456
- const stream = FileStream.create({ owner: me });
340
+ const group = Group.create(me);
341
+ group.makePublic();
342
+ const stream = FileStream.create({ owner: group });
457
343
 
458
- const [initialAsPeer, secondAsPeer] = connectedPeers("initial", "second", {
459
- peer1role: "server",
460
- peer2role: "client",
461
- });
462
- me._raw.core.node.syncManager.addPeer(secondAsPeer);
463
- if (!isControlledAccount(me)) {
464
- throw "me is not a controlled account";
465
- }
466
- const { account: meOnSecondPeer } =
467
- await createJazzContextFromExistingCredentials({
468
- credentials: {
469
- accountID: me.id,
470
- secret: me._raw.core.node.getCurrentAgent().agentSecret,
471
- },
472
- sessionProvider: randomSessionProvider,
473
- peersToLoadFrom: [initialAsPeer],
474
- crypto: Crypto,
475
- });
476
-
477
- const queue = new cojsonInternals.Channel();
344
+ const anotherAccount = await createJazzTestAccount();
345
+
346
+ const queue = new Channel();
478
347
 
479
348
  FileStream.subscribe(
480
349
  stream.id,
481
- { loadAs: meOnSecondPeer },
350
+ { loadAs: anotherAccount },
482
351
  (subscribedStream) => {
483
352
  void queue.push(subscribedStream);
484
353
  },
@@ -534,27 +403,11 @@ describe("FileStream loading & Subscription", async () => {
534
403
 
535
404
  test("Subscription without options", async () => {
536
405
  const { me } = await initNodeAndStream();
537
- const stream = FileStream.create({ owner: me });
538
-
539
- const [initialAsPeer, secondAsPeer] = connectedPeers("initial", "second", {
540
- peer1role: "server",
541
- peer2role: "client",
542
- });
543
- me._raw.core.node.syncManager.addPeer(secondAsPeer);
544
- if (!isControlledAccount(me)) {
545
- throw "me is not a controlled account";
546
- }
547
- await createJazzContextFromExistingCredentials({
548
- credentials: {
549
- accountID: me.id,
550
- secret: me._raw.core.node.getCurrentAgent().agentSecret,
551
- },
552
- sessionProvider: randomSessionProvider,
553
- peersToLoadFrom: [initialAsPeer],
554
- crypto: Crypto,
555
- });
406
+ const group = Group.create(me);
407
+ group.makePublic();
408
+ const stream = FileStream.create({ owner: group });
556
409
 
557
- const queue = new cojsonInternals.Channel();
410
+ const queue = new Channel();
558
411
 
559
412
  FileStream.subscribe(stream.id, (subscribedStream) => {
560
413
  void queue.push(subscribedStream);
@@ -591,12 +444,13 @@ describe("FileStream loading & Subscription", async () => {
591
444
 
592
445
  describe("FileStream.load", async () => {
593
446
  async function setup() {
594
- const me = await Account.create({
595
- creationProps: { name: "Hermes Puggington" },
596
- crypto: Crypto,
447
+ const me = await createJazzTestAccount({
448
+ isCurrentActiveAccount: true,
597
449
  });
598
450
 
599
- const stream = FileStream.create({ owner: me });
451
+ const group = Group.create(me);
452
+ group.makePublic();
453
+ const stream = FileStream.create({ owner: group });
600
454
 
601
455
  stream.start({ mimeType: "text/plain" });
602
456
 
@@ -789,6 +643,103 @@ describe("FileStream progress tracking", async () => {
789
643
  });
790
644
  });
791
645
 
646
+ describe("FileStream large file loading", async () => {
647
+ test("load a large FileStream with allowUnfinished: true should return the loaded file before it's fully loaded", async () => {
648
+ const syncServer = await setupJazzTestSync({ asyncPeers: true });
649
+
650
+ const group = Group.create(syncServer);
651
+ const largeStream = FileStream.create({ owner: group });
652
+ group.addMember("everyone", "reader");
653
+
654
+ // Create a large file stream with multiple chunks
655
+ largeStream.start({ mimeType: "application/octet-stream" });
656
+
657
+ const dataSize = 100 * 1024; // 100KB total
658
+ const chunkSize = 1024; // 1KB chunks
659
+ const numChunks = dataSize / chunkSize;
660
+
661
+ // Create test data chunks
662
+ for (let i = 0; i < numChunks; i++) {
663
+ const chunk = new Uint8Array(chunkSize);
664
+ for (let j = 0; j < chunkSize; j++) {
665
+ chunk[j] = (i * chunkSize + j) % 256;
666
+ }
667
+ largeStream.push(chunk);
668
+ }
669
+
670
+ largeStream.end();
671
+
672
+ // Wait for the large FileStream to be fully synced
673
+ await largeStream.waitForSync();
674
+
675
+ const alice = await createJazzTestAccount();
676
+
677
+ // Test loading the large FileStream
678
+ const loadedStream = await FileStream.load(largeStream.id, {
679
+ loadAs: alice,
680
+ allowUnfinished: true,
681
+ });
682
+
683
+ assert(loadedStream);
684
+
685
+ const loadedChunks = loadedStream.getChunks({ allowUnfinished: true });
686
+ expect(loadedChunks).not.toBeNull();
687
+ expect(loadedChunks?.finished).toBe(undefined);
688
+
689
+ expect(loadedStream._raw.core.knownState()).not.toEqual(
690
+ largeStream._raw.core.knownState(),
691
+ );
692
+ });
693
+
694
+ test("load a large FileStream with allowUnfinished: false should return the loaded file only when it's fully loaded", async () => {
695
+ const syncServer = await setupJazzTestSync({ asyncPeers: true });
696
+
697
+ const group = Group.create(syncServer);
698
+ const largeStream = FileStream.create({ owner: group });
699
+ group.addMember("everyone", "reader");
700
+
701
+ // Create a large file stream with multiple chunks
702
+ largeStream.start({ mimeType: "application/octet-stream" });
703
+
704
+ const dataSize = 100 * 1024; // 100KB total
705
+ const chunkSize = 1024; // 1KB chunks
706
+ const numChunks = dataSize / chunkSize;
707
+
708
+ // Create test data chunks
709
+ for (let i = 0; i < numChunks; i++) {
710
+ const chunk = new Uint8Array(chunkSize);
711
+ for (let j = 0; j < chunkSize; j++) {
712
+ chunk[j] = (i * chunkSize + j) % 256;
713
+ }
714
+ largeStream.push(chunk);
715
+ }
716
+
717
+ largeStream.end();
718
+
719
+ // Wait for the large FileStream to be fully synced
720
+ await largeStream.waitForSync();
721
+
722
+ const alice = await createJazzTestAccount();
723
+
724
+ // Test loading the large FileStream
725
+ const loadedStream = await FileStream.load(largeStream.id, {
726
+ loadAs: alice,
727
+ allowUnfinished: false,
728
+ });
729
+
730
+ assert(loadedStream);
731
+
732
+ const loadedChunks = loadedStream.getChunks();
733
+ expect(loadedChunks).not.toBeNull();
734
+ expect(loadedChunks?.finished).toBe(true);
735
+ expect(loadedChunks?.chunks).toHaveLength(numChunks); // 100 chunks of 1KB each
736
+
737
+ expect(loadedStream._raw.core.knownState()).toEqual(
738
+ largeStream._raw.core.knownState(),
739
+ );
740
+ });
741
+ });
742
+
792
743
  describe("waitForSync", async () => {
793
744
  test("CoFeed: should resolve when the value is uploaded", async () => {
794
745
  const TestStream = co.feed(z.string());
@@ -543,10 +543,6 @@ describe("CoMap resolution", async () => {
543
543
  });
544
544
 
545
545
  assert(loadedPerson);
546
- expect(loadedPerson.dog).toBe(null);
547
-
548
- await waitFor(() => expect(loadedPerson.dog).toBeTruthy());
549
-
550
546
  expect(loadedPerson.dog?.name).toEqual("Rex");
551
547
  });
552
548
 
@@ -652,10 +648,6 @@ describe("CoMap resolution", async () => {
652
648
 
653
649
  expect(resolved).toBe(true);
654
650
  assert(loadedPerson);
655
- expect(loadedPerson.dog).toBe(null);
656
-
657
- await waitFor(() => expect(loadedPerson.dog).toBeTruthy());
658
-
659
651
  expect(loadedPerson.dog?.name).toEqual("Rex");
660
652
  });
661
653
 
@@ -1,4 +1,5 @@
1
1
  import { WasmCrypto } from "cojson/crypto/WasmCrypto";
2
+ import { Channel } from "queueueue";
2
3
  import { describe, expect, test } from "vitest";
3
4
  import {
4
5
  Account,
@@ -215,7 +216,7 @@ describe("CoPlainText", () => {
215
216
  crypto: Crypto,
216
217
  });
217
218
 
218
- const queue = new cojsonInternals.Channel();
219
+ const queue = new Channel();
219
220
 
220
221
  // Subscribe to text updates
221
222
  co.plainText().subscribe(