jazz-tools 0.18.5 → 0.18.6
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.
- package/.turbo/turbo-build.log +53 -53
- package/CHANGELOG.md +16 -0
- package/dist/better-auth/auth/client.d.ts.map +1 -1
- package/dist/better-auth/auth/client.js +7 -1
- package/dist/better-auth/auth/client.js.map +1 -1
- package/dist/better-auth/auth/react.d.ts +0 -2145
- package/dist/better-auth/auth/react.d.ts.map +1 -1
- package/dist/better-auth/auth/react.js +2 -14
- package/dist/better-auth/auth/react.js.map +1 -1
- package/dist/better-auth/auth/server.d.ts.map +1 -1
- package/dist/better-auth/auth/server.js +73 -22
- package/dist/better-auth/auth/server.js.map +1 -1
- package/dist/better-auth/auth/tests/react.test.d.ts +2 -0
- package/dist/better-auth/auth/tests/react.test.d.ts.map +1 -0
- package/dist/{chunk-3LE7N6TH.js → chunk-45VKEOXG.js} +123 -81
- package/dist/chunk-45VKEOXG.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/inspector/{custom-element-WCY6D3QJ.js → custom-element-IBHKHN27.js} +19 -69
- package/dist/inspector/custom-element-IBHKHN27.js.map +1 -0
- package/dist/inspector/index.d.ts +5 -1
- package/dist/inspector/index.d.ts.map +1 -1
- package/dist/inspector/index.js +18 -17
- package/dist/inspector/index.js.map +1 -1
- package/dist/inspector/register-custom-element.js +1 -1
- package/dist/inspector/viewer/new-app.d.ts +0 -3
- package/dist/inspector/viewer/new-app.d.ts.map +1 -1
- package/dist/react-core/index.js +3 -1
- package/dist/react-core/index.js.map +1 -1
- package/dist/testing.js +2 -2
- package/dist/testing.js.map +1 -1
- package/dist/tools/coValues/inbox.d.ts +5 -5
- package/dist/tools/coValues/inbox.d.ts.map +1 -1
- package/dist/worker/index.d.ts +8 -2
- package/dist/worker/index.d.ts.map +1 -1
- package/dist/worker/index.js +7 -3
- package/dist/worker/index.js.map +1 -1
- package/package.json +4 -4
- package/src/better-auth/auth/client.ts +8 -2
- package/src/better-auth/auth/react.tsx +2 -51
- package/src/better-auth/auth/server.ts +94 -24
- package/src/better-auth/auth/tests/client.test.ts +92 -4
- package/src/better-auth/auth/tests/react.test.tsx +43 -0
- package/src/better-auth/auth/tests/server.test.ts +276 -98
- package/src/inspector/custom-element.tsx +1 -1
- package/src/inspector/index.tsx +44 -0
- package/src/inspector/viewer/new-app.tsx +0 -18
- package/src/tools/coValues/inbox.ts +190 -108
- package/src/tools/testing.ts +1 -1
- package/src/tools/tests/coFeed.test.ts +33 -22
- package/src/tools/tests/coList.test.ts +6 -4
- package/src/tools/tests/coMap.test.ts +13 -5
- package/src/tools/tests/exportImport.test.ts +3 -1
- package/src/tools/tests/groupsAndAccounts.test.ts +56 -44
- package/src/tools/tests/inbox.test.ts +293 -31
- package/src/worker/index.ts +15 -5
- package/tsup.config.ts +1 -1
- package/dist/chunk-3LE7N6TH.js.map +0 -1
- package/dist/inspector/custom-element-WCY6D3QJ.js.map +0 -1
- package/src/inspector/index.ts +0 -23
@@ -1,9 +1,9 @@
|
|
1
1
|
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
2
2
|
import { assert, beforeEach, describe, expect, test } from "vitest";
|
3
3
|
import { CoMap, Group, z } from "../exports.js";
|
4
|
-
import { Loaded, Ref, co } from "../internal.js";
|
4
|
+
import { Account, Loaded, Ref, co } from "../internal.js";
|
5
5
|
import { createJazzTestAccount, setupJazzTestSync } from "../testing.js";
|
6
|
-
import { setupTwoNodes, waitFor } from "./utils.js";
|
6
|
+
import { loadCoValueOrFail, setupTwoNodes, waitFor } from "./utils.js";
|
7
7
|
|
8
8
|
const Crypto = await WasmCrypto.create();
|
9
9
|
|
@@ -272,14 +272,20 @@ describe("Group inheritance", () => {
|
|
272
272
|
const bob = await createJazzTestAccount({});
|
273
273
|
await bob.$jazz.waitForAllCoValuesSync();
|
274
274
|
|
275
|
+
const loadedAlice = await Account.load(alice.$jazz.id);
|
276
|
+
const loadedBob = await Account.load(bob.$jazz.id);
|
277
|
+
|
278
|
+
assert(loadedBob);
|
279
|
+
assert(loadedAlice);
|
280
|
+
|
275
281
|
const parentGroup = Group.create();
|
276
282
|
// `parentGroup` has `alice` as a writer
|
277
|
-
parentGroup.addMember(
|
283
|
+
parentGroup.addMember(loadedAlice, "writer");
|
278
284
|
expect(parentGroup.getRoleOf(alice.$jazz.id)).toBe("writer");
|
279
285
|
|
280
286
|
const group = Group.create();
|
281
287
|
// `group` has `bob` as a reader
|
282
|
-
group.addMember(
|
288
|
+
group.addMember(loadedBob, "reader");
|
283
289
|
expect(group.getRoleOf(bob.$jazz.id)).toBe("reader");
|
284
290
|
|
285
291
|
group.addMember(parentGroup);
|
@@ -817,24 +823,26 @@ describe("Group.members", () => {
|
|
817
823
|
childGroup.addMember(bob, "reader");
|
818
824
|
expect(childGroup.getRoleOf(bob.$jazz.id)).toBe("reader");
|
819
825
|
|
820
|
-
|
821
|
-
expect.
|
822
|
-
|
823
|
-
|
824
|
-
|
826
|
+
await waitFor(() => {
|
827
|
+
expect(childGroup.members).toEqual([
|
828
|
+
expect.objectContaining({
|
829
|
+
account: expect.objectContaining({
|
830
|
+
$jazz: expect.objectContaining({
|
831
|
+
id: co.account().getMe().$jazz.id,
|
832
|
+
}),
|
825
833
|
}),
|
834
|
+
role: "admin",
|
826
835
|
}),
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
id: bob.$jazz.id,
|
836
|
+
expect.objectContaining({
|
837
|
+
account: expect.objectContaining({
|
838
|
+
$jazz: expect.objectContaining({
|
839
|
+
id: bob.$jazz.id,
|
840
|
+
}),
|
833
841
|
}),
|
842
|
+
role: "reader",
|
834
843
|
}),
|
835
|
-
|
836
|
-
|
837
|
-
]);
|
844
|
+
]);
|
845
|
+
});
|
838
846
|
});
|
839
847
|
|
840
848
|
test("should return the members of the parent group", async () => {
|
@@ -849,24 +857,26 @@ describe("Group.members", () => {
|
|
849
857
|
|
850
858
|
expect(childGroup.getRoleOf(bob.$jazz.id)).toBe("reader");
|
851
859
|
|
852
|
-
|
853
|
-
expect.
|
854
|
-
|
855
|
-
|
856
|
-
|
860
|
+
await waitFor(() => {
|
861
|
+
expect(childGroup.members).toEqual([
|
862
|
+
expect.objectContaining({
|
863
|
+
account: expect.objectContaining({
|
864
|
+
$jazz: expect.objectContaining({
|
865
|
+
id: co.account().getMe().$jazz.id,
|
866
|
+
}),
|
857
867
|
}),
|
868
|
+
role: "admin",
|
858
869
|
}),
|
859
|
-
|
860
|
-
|
861
|
-
|
862
|
-
|
863
|
-
|
864
|
-
id: bob.$jazz.id,
|
870
|
+
expect.objectContaining({
|
871
|
+
account: expect.objectContaining({
|
872
|
+
$jazz: expect.objectContaining({
|
873
|
+
id: bob.$jazz.id,
|
874
|
+
}),
|
865
875
|
}),
|
876
|
+
role: "reader",
|
866
877
|
}),
|
867
|
-
|
868
|
-
|
869
|
-
]);
|
878
|
+
]);
|
879
|
+
});
|
870
880
|
});
|
871
881
|
|
872
882
|
test("should not return everyone", async () => {
|
@@ -926,22 +936,24 @@ describe("Group.getDirectMembers", () => {
|
|
926
936
|
childGroup.addMember(parentGroup);
|
927
937
|
|
928
938
|
// Child group should inherit bob through parent, but bob is not a direct member
|
929
|
-
|
930
|
-
expect.
|
931
|
-
|
932
|
-
|
933
|
-
|
939
|
+
await waitFor(() => {
|
940
|
+
expect(childGroup.members).toEqual([
|
941
|
+
expect.objectContaining({
|
942
|
+
account: expect.objectContaining({
|
943
|
+
$jazz: expect.objectContaining({
|
944
|
+
id: co.account().getMe().$jazz.id,
|
945
|
+
}),
|
934
946
|
}),
|
935
947
|
}),
|
936
|
-
|
937
|
-
|
938
|
-
|
939
|
-
|
940
|
-
|
948
|
+
expect.objectContaining({
|
949
|
+
account: expect.objectContaining({
|
950
|
+
$jazz: expect.objectContaining({
|
951
|
+
id: bob.$jazz.id,
|
952
|
+
}),
|
941
953
|
}),
|
942
954
|
}),
|
943
|
-
|
944
|
-
|
955
|
+
]);
|
956
|
+
});
|
945
957
|
|
946
958
|
// directMembers should only show the admin, not the inherited bob
|
947
959
|
expect(childGroup.getDirectMembers()).toEqual([
|
@@ -1,12 +1,29 @@
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
1
|
+
import { beforeEach, describe, expect, it, vi } from "vitest";
|
2
2
|
import { Group, Inbox, InboxSender, z } from "../exports";
|
3
|
-
import {
|
3
|
+
import {
|
4
|
+
Account,
|
5
|
+
Loaded,
|
6
|
+
co,
|
7
|
+
coValueClassFromCoValueClassOrSchema,
|
8
|
+
} from "../internal";
|
4
9
|
import { setupTwoNodes, waitFor } from "./utils";
|
10
|
+
import {
|
11
|
+
createJazzTestAccount,
|
12
|
+
getPeerConnectedToTestSyncServer,
|
13
|
+
setupJazzTestSync,
|
14
|
+
} from "../testing";
|
15
|
+
import { cojsonInternals, LocalNode } from "cojson";
|
16
|
+
import { WasmCrypto } from "cojson/crypto/WasmCrypto";
|
5
17
|
|
6
18
|
const Message = co.map({
|
7
19
|
text: z.string(),
|
8
20
|
});
|
9
21
|
|
22
|
+
beforeEach(async () => {
|
23
|
+
await setupJazzTestSync();
|
24
|
+
vi.useRealTimers();
|
25
|
+
});
|
26
|
+
|
10
27
|
describe("Inbox", () => {
|
11
28
|
describe("Private profile", () => {
|
12
29
|
it("Should throw if the inbox owner profile is private", async () => {
|
@@ -211,10 +228,7 @@ describe("Inbox", () => {
|
|
211
228
|
|
212
229
|
unsubscribe();
|
213
230
|
|
214
|
-
expect(errorLogSpy).toHaveBeenCalledWith(
|
215
|
-
"Error processing inbox message",
|
216
|
-
expect.any(Error),
|
217
|
-
);
|
231
|
+
expect(errorLogSpy).toHaveBeenCalledWith(new Error("Failed"));
|
218
232
|
|
219
233
|
errorLogSpy.mockRestore();
|
220
234
|
});
|
@@ -298,7 +312,7 @@ describe("Inbox", () => {
|
|
298
312
|
expect(receivedMessages[0]?.text).toBe("Hello");
|
299
313
|
});
|
300
314
|
|
301
|
-
it("should retry failed messages", async () => {
|
315
|
+
it("should not retry failed messages", async () => {
|
302
316
|
const { clientAccount: sender, serverAccount: receiver } =
|
303
317
|
await setupTwoNodes();
|
304
318
|
|
@@ -320,25 +334,18 @@ describe("Inbox", () => {
|
|
320
334
|
let failures = 0;
|
321
335
|
|
322
336
|
// Subscribe to inbox messages
|
323
|
-
const unsubscribe = receiverInbox.subscribe(
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
throw new Error("Failed");
|
328
|
-
},
|
329
|
-
{ retries: 2 },
|
330
|
-
);
|
337
|
+
const unsubscribe = receiverInbox.subscribe(Message, async () => {
|
338
|
+
failures++;
|
339
|
+
throw new Error("Failed");
|
340
|
+
});
|
331
341
|
|
332
342
|
await expect(promise).rejects.toThrow();
|
333
|
-
expect(failures).toBe(
|
343
|
+
expect(failures).toBe(1);
|
334
344
|
const [failed] = Object.values(receiverInbox.failed.items).flat();
|
335
|
-
expect(failed?.value.errors.length).toBe(
|
345
|
+
expect(failed?.value.errors.length).toBe(1);
|
336
346
|
unsubscribe();
|
337
347
|
|
338
|
-
expect(errorLogSpy).toHaveBeenCalledWith(
|
339
|
-
"Error processing inbox message",
|
340
|
-
expect.any(Error),
|
341
|
-
);
|
348
|
+
expect(errorLogSpy).toHaveBeenCalledWith(new Error("Failed"));
|
342
349
|
|
343
350
|
errorLogSpy.mockRestore();
|
344
351
|
});
|
@@ -357,28 +364,283 @@ describe("Inbox", () => {
|
|
357
364
|
const errorLogSpy = vi.spyOn(console, "error").mockImplementation(() => {});
|
358
365
|
|
359
366
|
// Subscribe to inbox messages
|
360
|
-
const unsubscribe = receiverInbox.subscribe(
|
361
|
-
|
362
|
-
|
363
|
-
spy();
|
364
|
-
},
|
365
|
-
{ retries: 2 },
|
366
|
-
);
|
367
|
+
const unsubscribe = receiverInbox.subscribe(Message, async () => {
|
368
|
+
spy();
|
369
|
+
});
|
367
370
|
|
368
371
|
await waitFor(() => {
|
369
372
|
const [failed] = Object.values(receiverInbox.failed.items).flat();
|
370
373
|
|
371
|
-
expect(failed?.value.errors.length).toBe(
|
374
|
+
expect(failed?.value.errors.length).toBe(1);
|
372
375
|
});
|
373
376
|
|
374
377
|
expect(spy).not.toHaveBeenCalled();
|
375
378
|
unsubscribe();
|
376
379
|
|
377
380
|
expect(errorLogSpy).toHaveBeenCalledWith(
|
378
|
-
"
|
379
|
-
expect.any(Error),
|
381
|
+
new Error("Inbox: message co_z123234 is unavailable"),
|
380
382
|
);
|
381
383
|
|
382
384
|
errorLogSpy.mockRestore();
|
383
385
|
});
|
386
|
+
|
387
|
+
it("should skip processed messages when a large inbox is restarted", async () => {
|
388
|
+
cojsonInternals.setMaxRecommendedTxSize(100);
|
389
|
+
|
390
|
+
const sender = await createJazzTestAccount();
|
391
|
+
const receiver = await createJazzTestAccount();
|
392
|
+
|
393
|
+
await receiver.$jazz.waitForAllCoValuesSync();
|
394
|
+
|
395
|
+
const receiverInbox = await Inbox.load(receiver);
|
396
|
+
const inboxSender = await InboxSender.load(receiver.$jazz.id, sender);
|
397
|
+
|
398
|
+
const group = Group.create({ owner: receiver });
|
399
|
+
|
400
|
+
// This generates 4 chunks on the processed stream
|
401
|
+
for (let i = 0; i < 5; i++) {
|
402
|
+
inboxSender.sendMessage(Message.create({ text: `Hello ${i}` }, group));
|
403
|
+
}
|
404
|
+
|
405
|
+
inboxSender.sendMessage(Message.create({ text: `done` }, group));
|
406
|
+
|
407
|
+
await new Promise((resolve) => {
|
408
|
+
const unsubscribe = receiverInbox.subscribe(Message, async (message) => {
|
409
|
+
if (message.text === "done") {
|
410
|
+
resolve(true);
|
411
|
+
unsubscribe();
|
412
|
+
}
|
413
|
+
});
|
414
|
+
});
|
415
|
+
|
416
|
+
const accountId = receiver.$jazz.id;
|
417
|
+
const accountSecret =
|
418
|
+
receiver.$jazz.localNode.getCurrentAgent().agentSecret;
|
419
|
+
const sessionID = receiver.$jazz.localNode.currentSessionID;
|
420
|
+
|
421
|
+
await receiver.$jazz.waitForAllCoValuesSync();
|
422
|
+
await receiver.$jazz.localNode.gracefulShutdown();
|
423
|
+
|
424
|
+
const node = await LocalNode.withLoadedAccount({
|
425
|
+
accountID: accountId as any,
|
426
|
+
accountSecret: accountSecret,
|
427
|
+
peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
|
428
|
+
crypto: receiver.$jazz.localNode.crypto,
|
429
|
+
sessionID: sessionID,
|
430
|
+
});
|
431
|
+
|
432
|
+
const reloadedInbox = await Inbox.load(Account.fromNode(node));
|
433
|
+
|
434
|
+
const subscribeEmitted = await new Promise((resolve) => {
|
435
|
+
const unsubscribe = reloadedInbox.subscribe(Message, async (message) => {
|
436
|
+
// Got a message
|
437
|
+
resolve(true);
|
438
|
+
});
|
439
|
+
setTimeout(() => {
|
440
|
+
resolve(false);
|
441
|
+
unsubscribe();
|
442
|
+
}, 100);
|
443
|
+
});
|
444
|
+
|
445
|
+
expect(subscribeEmitted).toBe(false);
|
446
|
+
});
|
447
|
+
|
448
|
+
it("should skip failed messages when a large inbox is restarted", async () => {
|
449
|
+
cojsonInternals.setMaxRecommendedTxSize(100);
|
450
|
+
|
451
|
+
const sender = await createJazzTestAccount();
|
452
|
+
const receiver = await createJazzTestAccount();
|
453
|
+
|
454
|
+
await receiver.$jazz.waitForAllCoValuesSync();
|
455
|
+
|
456
|
+
const receiverInbox = await Inbox.load(receiver);
|
457
|
+
const inboxSender = await InboxSender.load(receiver.$jazz.id, sender);
|
458
|
+
|
459
|
+
const group = Group.create({ owner: receiver });
|
460
|
+
|
461
|
+
// This generates 4 chunks on the processed stream
|
462
|
+
for (let i = 0; i < 5; i++) {
|
463
|
+
inboxSender
|
464
|
+
.sendMessage(Message.create({ text: `Hello ${i}` }, group))
|
465
|
+
.catch(() => {});
|
466
|
+
}
|
467
|
+
|
468
|
+
inboxSender
|
469
|
+
.sendMessage(Message.create({ text: `done` }, group))
|
470
|
+
.catch(() => {});
|
471
|
+
|
472
|
+
await new Promise((resolve) => {
|
473
|
+
const unsubscribe = receiverInbox.subscribe(Message, async (message) => {
|
474
|
+
if (message.text === "done") {
|
475
|
+
resolve(true);
|
476
|
+
unsubscribe();
|
477
|
+
}
|
478
|
+
|
479
|
+
throw new Error("Failed");
|
480
|
+
});
|
481
|
+
});
|
482
|
+
|
483
|
+
await waitFor(() => {
|
484
|
+
expect(Object.values(receiverInbox.processed.items).length).toBe(1);
|
485
|
+
});
|
486
|
+
|
487
|
+
const accountId = receiver.$jazz.id;
|
488
|
+
const accountSecret =
|
489
|
+
receiver.$jazz.localNode.getCurrentAgent().agentSecret;
|
490
|
+
const sessionID = receiver.$jazz.localNode.currentSessionID;
|
491
|
+
|
492
|
+
await receiver.$jazz.waitForAllCoValuesSync();
|
493
|
+
await receiver.$jazz.localNode.gracefulShutdown();
|
494
|
+
|
495
|
+
const node = await LocalNode.withLoadedAccount({
|
496
|
+
accountID: accountId as any,
|
497
|
+
accountSecret: accountSecret,
|
498
|
+
peersToLoadFrom: [getPeerConnectedToTestSyncServer()],
|
499
|
+
crypto: receiver.$jazz.localNode.crypto,
|
500
|
+
sessionID: sessionID,
|
501
|
+
});
|
502
|
+
|
503
|
+
const reloadedInbox = await Inbox.load(Account.fromNode(node));
|
504
|
+
|
505
|
+
const subscribeEmitted = await new Promise((resolve) => {
|
506
|
+
const unsubscribe = reloadedInbox.subscribe(Message, async (message) => {
|
507
|
+
// Got a message
|
508
|
+
resolve(true);
|
509
|
+
});
|
510
|
+
setTimeout(() => {
|
511
|
+
resolve(false);
|
512
|
+
unsubscribe();
|
513
|
+
}, 100);
|
514
|
+
});
|
515
|
+
|
516
|
+
expect(subscribeEmitted).toBe(false);
|
517
|
+
});
|
518
|
+
|
519
|
+
describe("Concurrency control", () => {
|
520
|
+
it("should respect concurrency limit of 1", async () => {
|
521
|
+
const { clientAccount: sender, serverAccount: receiver } =
|
522
|
+
await setupTwoNodes();
|
523
|
+
|
524
|
+
const receiverInbox = await Inbox.load(receiver);
|
525
|
+
const inboxSender = await InboxSender.load(receiver.$jazz.id, sender);
|
526
|
+
|
527
|
+
const group = Group.create({ owner: sender });
|
528
|
+
|
529
|
+
const processingOrder: string[] = [];
|
530
|
+
|
531
|
+
const Message = co.map({
|
532
|
+
text: z.string(),
|
533
|
+
value: z.number(),
|
534
|
+
});
|
535
|
+
|
536
|
+
// Create messages that take time to process
|
537
|
+
const messages = Array.from({ length: 5 }, (_, i) =>
|
538
|
+
Message.create({ text: `Message ${i}`, value: i }, group),
|
539
|
+
);
|
540
|
+
|
541
|
+
// Subscribe with concurrency limit of 1
|
542
|
+
const unsubscribe = receiverInbox.subscribe(
|
543
|
+
Message,
|
544
|
+
async (message) => {
|
545
|
+
const messageText = message.text;
|
546
|
+
processingOrder.push(`start-${messageText}`);
|
547
|
+
|
548
|
+
// Simulate processing time
|
549
|
+
await new Promise((resolve) =>
|
550
|
+
setTimeout(resolve, 20 - message.value * 10),
|
551
|
+
);
|
552
|
+
|
553
|
+
processingOrder.push(`end-${messageText}`);
|
554
|
+
},
|
555
|
+
{ concurrencyLimit: 1 },
|
556
|
+
);
|
557
|
+
|
558
|
+
// Send all messages at once
|
559
|
+
messages.forEach((message) => {
|
560
|
+
inboxSender.sendMessage(message);
|
561
|
+
});
|
562
|
+
|
563
|
+
// Wait for all messages to be processed
|
564
|
+
await waitFor(() => processingOrder.length === 10); // 5 start + 5 end
|
565
|
+
|
566
|
+
expect(processingOrder).toMatchInlineSnapshot(`
|
567
|
+
[
|
568
|
+
"start-Message 0",
|
569
|
+
"end-Message 0",
|
570
|
+
"start-Message 1",
|
571
|
+
"end-Message 1",
|
572
|
+
"start-Message 2",
|
573
|
+
"end-Message 2",
|
574
|
+
"start-Message 3",
|
575
|
+
"end-Message 3",
|
576
|
+
"start-Message 4",
|
577
|
+
"end-Message 4",
|
578
|
+
]
|
579
|
+
`);
|
580
|
+
unsubscribe();
|
581
|
+
});
|
582
|
+
|
583
|
+
it("should allow concurrent processing with higher concurrency limit", async () => {
|
584
|
+
const { clientAccount: sender, serverAccount: receiver } =
|
585
|
+
await setupTwoNodes();
|
586
|
+
|
587
|
+
const receiverInbox = await Inbox.load(receiver);
|
588
|
+
const inboxSender = await InboxSender.load(receiver.$jazz.id, sender);
|
589
|
+
|
590
|
+
const group = Group.create({ owner: sender });
|
591
|
+
|
592
|
+
// Track processing order and timing
|
593
|
+
const processingOrder: string[] = [];
|
594
|
+
|
595
|
+
const Message = co.map({
|
596
|
+
text: z.string(),
|
597
|
+
value: z.number(),
|
598
|
+
});
|
599
|
+
|
600
|
+
// Create messages that take time to process
|
601
|
+
const messages = Array.from({ length: 5 }, (_, i) =>
|
602
|
+
Message.create({ text: `Message ${i}`, value: i }, group),
|
603
|
+
);
|
604
|
+
|
605
|
+
// Subscribe with concurrency limit of 1
|
606
|
+
const unsubscribe = receiverInbox.subscribe(
|
607
|
+
Message,
|
608
|
+
async (message) => {
|
609
|
+
const messageText = message.text;
|
610
|
+
processingOrder.push(`start-${messageText}`);
|
611
|
+
|
612
|
+
// Simulate processing time
|
613
|
+
await new Promise((resolve) =>
|
614
|
+
setTimeout(resolve, 20 - message.value * 10),
|
615
|
+
);
|
616
|
+
|
617
|
+
processingOrder.push(`end-${messageText}`);
|
618
|
+
},
|
619
|
+
{ concurrencyLimit: 3 },
|
620
|
+
);
|
621
|
+
|
622
|
+
// Send all messages at once
|
623
|
+
messages.forEach((message) => {
|
624
|
+
inboxSender.sendMessage(message);
|
625
|
+
});
|
626
|
+
|
627
|
+
await waitFor(() => processingOrder.length === 10);
|
628
|
+
|
629
|
+
expect(processingOrder).toMatchInlineSnapshot(`
|
630
|
+
[
|
631
|
+
"start-Message 0",
|
632
|
+
"start-Message 1",
|
633
|
+
"start-Message 2",
|
634
|
+
"end-Message 2",
|
635
|
+
"start-Message 3",
|
636
|
+
"end-Message 3",
|
637
|
+
"start-Message 4",
|
638
|
+
"end-Message 4",
|
639
|
+
"end-Message 1",
|
640
|
+
"end-Message 0",
|
641
|
+
]
|
642
|
+
`);
|
643
|
+
unsubscribe();
|
644
|
+
});
|
645
|
+
});
|
384
646
|
});
|
package/src/worker/index.ts
CHANGED
@@ -11,6 +11,7 @@ import {
|
|
11
11
|
CoValueFromRaw,
|
12
12
|
Inbox,
|
13
13
|
InstanceOfSchema,
|
14
|
+
Loaded,
|
14
15
|
createJazzContextFromExistingCredentials,
|
15
16
|
randomSessionProvider,
|
16
17
|
} from "jazz-tools";
|
@@ -26,6 +27,10 @@ type WorkerOptions<
|
|
26
27
|
WebSocket?: AnyWebSocketConstructor;
|
27
28
|
AccountSchema?: S;
|
28
29
|
crypto?: CryptoProvider;
|
30
|
+
/**
|
31
|
+
* If true, the inbox will not be loaded.
|
32
|
+
*/
|
33
|
+
skipInboxLoad?: boolean;
|
29
34
|
};
|
30
35
|
|
31
36
|
/** @category Context Creation */
|
@@ -39,6 +44,7 @@ export async function startWorker<
|
|
39
44
|
accountSecret = process.env.JAZZ_WORKER_SECRET,
|
40
45
|
syncServer = "wss://cloud.jazz.tools",
|
41
46
|
AccountSchema = Account as unknown as S,
|
47
|
+
skipInboxLoad = false,
|
42
48
|
} = options;
|
43
49
|
|
44
50
|
let node: LocalNode | undefined = undefined;
|
@@ -93,7 +99,7 @@ export async function startWorker<
|
|
93
99
|
throw new Error("Account has no profile");
|
94
100
|
}
|
95
101
|
|
96
|
-
const inbox = await Inbox.load(account);
|
102
|
+
const inbox = skipInboxLoad ? undefined : await Inbox.load(account);
|
97
103
|
|
98
104
|
async function done() {
|
99
105
|
await context.account.$jazz.waitForAllCoValuesSync();
|
@@ -102,12 +108,16 @@ export async function startWorker<
|
|
102
108
|
context.done();
|
103
109
|
}
|
104
110
|
|
105
|
-
const inboxPublicApi =
|
106
|
-
|
107
|
-
|
111
|
+
const inboxPublicApi = inbox
|
112
|
+
? {
|
113
|
+
subscribe: inbox.subscribe.bind(inbox) as Inbox["subscribe"],
|
114
|
+
}
|
115
|
+
: {
|
116
|
+
subscribe: () => {},
|
117
|
+
};
|
108
118
|
|
109
119
|
return {
|
110
|
-
worker: context.account as
|
120
|
+
worker: context.account as Loaded<S>,
|
111
121
|
experimental: {
|
112
122
|
inbox: inboxPublicApi,
|
113
123
|
},
|