cojson 0.7.35-unique.2 → 0.7.35

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 (76) hide show
  1. package/.turbo/turbo-test.log +321 -253
  2. package/CHANGELOG.md +3 -2
  3. package/dist/PeerState.js +58 -0
  4. package/dist/PeerState.js.map +1 -0
  5. package/dist/PriorityBasedMessageQueue.js +51 -0
  6. package/dist/PriorityBasedMessageQueue.js.map +1 -0
  7. package/dist/base64url.js.map +1 -1
  8. package/dist/coValue.js.map +1 -1
  9. package/dist/coValueCore.js +3 -0
  10. package/dist/coValueCore.js.map +1 -1
  11. package/dist/coValues/account.js +2 -2
  12. package/dist/coValues/account.js.map +1 -1
  13. package/dist/coValues/coList.js.map +1 -1
  14. package/dist/coValues/coMap.js.map +1 -1
  15. package/dist/coValues/coStream.js +14 -15
  16. package/dist/coValues/coStream.js.map +1 -1
  17. package/dist/coValues/group.js +8 -8
  18. package/dist/coValues/group.js.map +1 -1
  19. package/dist/coreToCoValue.js.map +1 -1
  20. package/dist/crypto/PureJSCrypto.js.map +1 -1
  21. package/dist/crypto/WasmCrypto.js.map +1 -1
  22. package/dist/crypto/crypto.js.map +1 -1
  23. package/dist/index.js +2 -0
  24. package/dist/index.js.map +1 -1
  25. package/dist/jsonStringify.js.map +1 -1
  26. package/dist/localNode.js +2 -2
  27. package/dist/localNode.js.map +1 -1
  28. package/dist/permissions.js.map +1 -1
  29. package/dist/priority.js +31 -0
  30. package/dist/priority.js.map +1 -0
  31. package/dist/storage/FileSystem.js.map +1 -1
  32. package/dist/storage/chunksAndKnownStates.js +2 -0
  33. package/dist/storage/chunksAndKnownStates.js.map +1 -1
  34. package/dist/storage/index.js.map +1 -1
  35. package/dist/streamUtils.js.map +1 -1
  36. package/dist/sync.js +7 -18
  37. package/dist/sync.js.map +1 -1
  38. package/dist/tests/PeerState.test.js +80 -0
  39. package/dist/tests/PeerState.test.js.map +1 -0
  40. package/dist/tests/PriorityBasedMessageQueue.test.js +97 -0
  41. package/dist/tests/PriorityBasedMessageQueue.test.js.map +1 -0
  42. package/dist/tests/coMap.test.js.map +1 -1
  43. package/dist/tests/coStream.test.js +34 -1
  44. package/dist/tests/coStream.test.js.map +1 -1
  45. package/dist/tests/permissions.test.js.map +1 -1
  46. package/dist/tests/priority.test.js +61 -0
  47. package/dist/tests/priority.test.js.map +1 -0
  48. package/dist/tests/sync.test.js +323 -12
  49. package/dist/tests/sync.test.js.map +1 -1
  50. package/dist/tests/testUtils.js.map +1 -1
  51. package/dist/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
  52. package/dist/typeUtils/expectGroup.js.map +1 -1
  53. package/dist/typeUtils/isAccountID.js.map +1 -1
  54. package/package.json +3 -3
  55. package/src/PeerState.ts +74 -0
  56. package/src/PriorityBasedMessageQueue.ts +77 -0
  57. package/src/coValueCore.ts +10 -7
  58. package/src/coValues/account.ts +5 -5
  59. package/src/coValues/coList.ts +4 -4
  60. package/src/coValues/coMap.ts +3 -3
  61. package/src/coValues/coStream.ts +29 -26
  62. package/src/coValues/group.ts +11 -15
  63. package/src/ids.ts +2 -2
  64. package/src/index.ts +5 -5
  65. package/src/localNode.ts +11 -12
  66. package/src/permissions.ts +5 -5
  67. package/src/priority.ts +39 -0
  68. package/src/storage/chunksAndKnownStates.ts +2 -0
  69. package/src/sync.ts +19 -34
  70. package/src/tests/PeerState.test.ts +92 -0
  71. package/src/tests/PriorityBasedMessageQueue.test.ts +111 -0
  72. package/src/tests/coStream.test.ts +58 -1
  73. package/src/tests/priority.test.ts +75 -0
  74. package/src/tests/sync.test.ts +487 -25
  75. package/src/typeUtils/accountOrAgentIDfromSessionID.ts +3 -3
  76. package/src/typeUtils/isAccountID.ts +2 -2
@@ -0,0 +1,92 @@
1
+ import { describe, test, expect, vi } from "vitest";
2
+ import { PeerState } from "../PeerState.js";
3
+ import { Peer, SyncMessage } from "../sync.js";
4
+ import { CO_VALUE_PRIORITY } from "../priority.js";
5
+
6
+ function setup() {
7
+ const mockPeer: Peer = {
8
+ id: "test-peer",
9
+ role: "peer",
10
+ priority: 1,
11
+ crashOnClose: false,
12
+ incoming: (async function* () {})(),
13
+ outgoing: {
14
+ push: vi.fn().mockResolvedValue(undefined),
15
+ close: vi.fn(),
16
+ },
17
+ };
18
+ const peerState = new PeerState(mockPeer);
19
+ return { mockPeer, peerState };
20
+ }
21
+
22
+ describe("PeerState", () => {
23
+ test("should initialize with correct properties", () => {
24
+ const { peerState } = setup();
25
+ expect(peerState.id).toBe("test-peer");
26
+ expect(peerState.role).toBe("peer");
27
+ expect(peerState.priority).toBe(1);
28
+ expect(peerState.crashOnClose).toBe(false);
29
+ expect(peerState.closed).toBe(false);
30
+ expect(peerState.optimisticKnownStates).toEqual({});
31
+ expect(peerState.toldKnownState).toEqual(new Set());
32
+ });
33
+
34
+ test("should push outgoing message to peer", async () => {
35
+ const { mockPeer, peerState } = setup();
36
+ const message: SyncMessage = { action: "load", id: "co_ztest-id", header: false, sessions: {} };
37
+ await peerState.pushOutgoingMessage(message);
38
+ expect(mockPeer.outgoing.push).toHaveBeenCalledWith(message);
39
+ });
40
+
41
+ test("should return peer's incoming when not closed", () => {
42
+ const { mockPeer, peerState } = setup();
43
+ expect(peerState.incoming).toBe(mockPeer.incoming);
44
+ });
45
+
46
+ test("should return Disconnected when closed", async () => {
47
+ const { peerState } = setup();
48
+ peerState.gracefulShutdown();
49
+ const incomingIterator = peerState.incoming[Symbol.asyncIterator]();
50
+ const { value, done } = await incomingIterator.next();
51
+ expect(value).toBe("Disconnected");
52
+ expect(done).toBe(false);
53
+ });
54
+
55
+ test("should perform graceful shutdown", () => {
56
+ const { mockPeer, peerState } = setup();
57
+ const consoleSpy = vi.spyOn(console, "debug").mockImplementation(() => {});
58
+ peerState.gracefulShutdown();
59
+ expect(mockPeer.outgoing.close).toHaveBeenCalled();
60
+ expect(peerState.closed).toBe(true);
61
+ expect(consoleSpy).toHaveBeenCalledWith("Gracefully closing", "test-peer");
62
+ consoleSpy.mockRestore();
63
+ });
64
+
65
+ test("should schedule outgoing messages based on their priority", async () => {
66
+ const { peerState } = setup();
67
+
68
+ const loadMessage: SyncMessage = { action: "load", id: "co_zhigh", header: false, sessions: {} };
69
+ const contentMessageHigh: SyncMessage = { action: "content", id: "co_zhigh", new: {}, priority: CO_VALUE_PRIORITY.HIGH };
70
+ const contentMessageMid: SyncMessage = { action: "content", id: "co_zmid", new: {}, priority: CO_VALUE_PRIORITY.MEDIUM };
71
+ const contentMessageLow: SyncMessage = { action: "content", id: "co_zlow", new: {}, priority: CO_VALUE_PRIORITY.LOW };
72
+
73
+ const promises = [
74
+ peerState.pushOutgoingMessage(contentMessageLow),
75
+ peerState.pushOutgoingMessage(contentMessageMid),
76
+ peerState.pushOutgoingMessage(contentMessageHigh),
77
+ peerState.pushOutgoingMessage(loadMessage),
78
+ ];
79
+
80
+ await Promise.all(promises);
81
+
82
+ // The first message is pushed directly, the other three are queued because are waiting
83
+ // for the first push to be completed.
84
+ expect(peerState["peer"].outgoing.push).toHaveBeenNthCalledWith(1, contentMessageLow);
85
+
86
+ // Load message are managed as high priority messages and having the same priority as the content message
87
+ // they follow the push order.
88
+ expect(peerState["peer"].outgoing.push).toHaveBeenNthCalledWith(2, contentMessageHigh);
89
+ expect(peerState["peer"].outgoing.push).toHaveBeenNthCalledWith(3, loadMessage);
90
+ expect(peerState["peer"].outgoing.push).toHaveBeenNthCalledWith(4, contentMessageMid);
91
+ });
92
+ });
@@ -0,0 +1,111 @@
1
+ import { describe, test, expect } from "vitest";
2
+ import { PriorityBasedMessageQueue } from "../PriorityBasedMessageQueue.js";
3
+ import { SyncMessage } from "../sync.js";
4
+ import { CO_VALUE_PRIORITY } from "../priority.js";
5
+
6
+ function setup() {
7
+ const queue = new PriorityBasedMessageQueue(CO_VALUE_PRIORITY.MEDIUM);
8
+ return { queue };
9
+ }
10
+
11
+ describe("PriorityBasedMessageQueue", () => {
12
+ test("should initialize with correct properties", () => {
13
+ const { queue } = setup();
14
+ expect(queue["defaultPriority"]).toBe(CO_VALUE_PRIORITY.MEDIUM);
15
+ expect(queue["queues"].length).toBe(8);
16
+ expect(queue["queues"].every((q) => q.length === 0)).toBe(true);
17
+ });
18
+
19
+ test("should push message with default priority", async () => {
20
+ const { queue } = setup();
21
+ const message: SyncMessage = {
22
+ action: "load",
23
+ id: "co_ztest-id",
24
+ header: false,
25
+ sessions: {},
26
+ };
27
+ void queue.push(message);
28
+ const pulledEntry = queue.pull();
29
+ expect(pulledEntry?.msg).toEqual(message);
30
+ });
31
+
32
+ test("should push message with specified priority", async () => {
33
+ const { queue } = setup();
34
+ const message: SyncMessage = {
35
+ action: "content",
36
+ id: "co_zhigh",
37
+ new: {},
38
+ priority: CO_VALUE_PRIORITY.HIGH,
39
+ };
40
+ void queue.push(message);
41
+ const pulledEntry = queue.pull();
42
+ expect(pulledEntry?.msg).toEqual(message);
43
+ });
44
+
45
+ test("should pull messages in priority order", async () => {
46
+ const { queue } = setup();
47
+ const lowPriorityMsg: SyncMessage = {
48
+ action: "content",
49
+ id: "co_zlow",
50
+ new: {},
51
+ priority: CO_VALUE_PRIORITY.LOW,
52
+ };
53
+ const mediumPriorityMsg: SyncMessage = {
54
+ action: "content",
55
+ id: "co_zmedium",
56
+ new: {},
57
+ priority: CO_VALUE_PRIORITY.MEDIUM,
58
+ };
59
+ const highPriorityMsg: SyncMessage = {
60
+ action: "content",
61
+ id: "co_zhigh",
62
+ new: {},
63
+ priority: CO_VALUE_PRIORITY.HIGH,
64
+ };
65
+
66
+ void queue.push(lowPriorityMsg);
67
+ void queue.push(mediumPriorityMsg);
68
+ void queue.push(highPriorityMsg);
69
+
70
+ expect(queue.pull()?.msg).toEqual(highPriorityMsg);
71
+ expect(queue.pull()?.msg).toEqual(mediumPriorityMsg);
72
+ expect(queue.pull()?.msg).toEqual(lowPriorityMsg);
73
+ });
74
+
75
+ test("should return undefined when pulling from empty queue", () => {
76
+ const { queue } = setup();
77
+ expect(queue.pull()).toBeUndefined();
78
+ });
79
+
80
+ test("should resolve promise when message is pulled", async () => {
81
+ const { queue } = setup();
82
+ const message: SyncMessage = {
83
+ action: "load",
84
+ id: "co_ztest-id",
85
+ header: false,
86
+ sessions: {},
87
+ };
88
+ const pushPromise = queue.push(message);
89
+
90
+ const pulledEntry = queue.pull();
91
+ pulledEntry?.resolve();
92
+
93
+ await expect(pushPromise).resolves.toBeUndefined();
94
+ });
95
+
96
+ test("should reject promise when message is rejected", async () => {
97
+ const { queue } = setup();
98
+ const message: SyncMessage = {
99
+ action: "load",
100
+ id: "co_ztest-id",
101
+ header: false,
102
+ sessions: {},
103
+ };
104
+ const pushPromise = queue.push(message);
105
+
106
+ const pulledEntry = queue.pull();
107
+ pulledEntry?.reject(new Error("Test error"));
108
+
109
+ await expect(pushPromise).rejects.toThrow("Test error");
110
+ });
111
+ });
@@ -1,4 +1,4 @@
1
- import { expect, test } from "vitest";
1
+ import { expect, test, describe } from "vitest";
2
2
  import { expectStream } from "../coValue.js";
3
3
  import { RawBinaryCoStream } from "../coValues/coStream.js";
4
4
  import { MAX_RECOMMENDED_TX_SIZE, WasmCrypto } from "../index.js";
@@ -244,3 +244,60 @@ test("When adding large transactions (bigger than MAX_RECOMMENDED_TX_SIZE), we s
244
244
  sessionEntry.lastSignature,
245
245
  );
246
246
  });
247
+
248
+ describe("isBinaryStreamEnded", () => {
249
+ function setup() {
250
+ const node = new LocalNode(
251
+ ...randomAnonymousAccountAndSessionID(),
252
+ Crypto,
253
+ );
254
+
255
+ const coValue = node.createCoValue({
256
+ type: "costream",
257
+ ruleset: { type: "unsafeAllowAll" },
258
+ meta: { type: "binary" },
259
+ ...Crypto.createdNowUnique(),
260
+ });
261
+
262
+ const content = coValue.getCurrentContent();
263
+
264
+ if (
265
+ content.type !== "costream" ||
266
+ content.headerMeta?.type !== "binary" ||
267
+ !(content instanceof RawBinaryCoStream)
268
+ ) {
269
+ throw new Error("Expected binary stream");
270
+ }
271
+
272
+ return content;
273
+ }
274
+
275
+ test("returns true when the last item is end", () => {
276
+ const stream = setup();
277
+
278
+ stream.startBinaryStream(
279
+ { mimeType: "text/plain", fileName: "test.txt" },
280
+ "trusting",
281
+ );
282
+ stream.endBinaryStream("trusting");
283
+
284
+ expect(stream.isBinaryStreamEnded()).toBe(true);
285
+ });
286
+
287
+ test("returns false if the stream isn't ended", () => {
288
+ const stream = setup();
289
+
290
+ stream.startBinaryStream(
291
+ { mimeType: "text/plain", fileName: "test.txt" },
292
+ "trusting",
293
+ );
294
+
295
+ expect(stream.isBinaryStreamEnded()).toBe(false);
296
+ });
297
+
298
+ test("returns false if the stream isn't started", () => {
299
+ const stream = setup();
300
+
301
+ expect(stream.isBinaryStreamEnded()).toBe(false);
302
+ });
303
+ });
@@ -0,0 +1,75 @@
1
+ import { expect, test, describe } from "vitest";
2
+ import { WasmCrypto } from "../index.js";
3
+ import { LocalNode } from "../localNode.js";
4
+ import { randomAnonymousAccountAndSessionID } from "./testUtils.js";
5
+ import { getPriorityFromHeader, CO_VALUE_PRIORITY } from "../priority.js";
6
+
7
+ const Crypto = await WasmCrypto.create();
8
+
9
+ describe("getPriorityFromHeader", () => {
10
+ test("returns MEDIUM priority for boolean or undefined headers", () => {
11
+ expect(getPriorityFromHeader(true)).toEqual(CO_VALUE_PRIORITY.MEDIUM);
12
+ expect(getPriorityFromHeader(false)).toEqual(CO_VALUE_PRIORITY.MEDIUM);
13
+ expect(getPriorityFromHeader(undefined)).toEqual(CO_VALUE_PRIORITY.MEDIUM);
14
+ });
15
+
16
+ test("returns MEDIUM priority for costream type", () => {
17
+ const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
18
+ const costream = node.createCoValue({
19
+ type: "costream",
20
+ ruleset: { type: "unsafeAllowAll" },
21
+ meta: null,
22
+ ...Crypto.createdNowUnique(),
23
+ });
24
+
25
+ expect(getPriorityFromHeader(costream.header)).toEqual(CO_VALUE_PRIORITY.MEDIUM);
26
+ });
27
+
28
+ test("returns LOW priority for binary costream type", () => {
29
+ const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
30
+ const costream = node.createCoValue({
31
+ type: "costream",
32
+ ruleset: { type: "unsafeAllowAll" },
33
+ meta: { type: "binary" },
34
+ ...Crypto.createdNowUnique(),
35
+ });
36
+
37
+ expect(getPriorityFromHeader(costream.header)).toEqual(CO_VALUE_PRIORITY.LOW);
38
+ });
39
+
40
+ test("returns HIGH priority for account type", async () => {
41
+ const node =new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
42
+
43
+ const account = node.createAccount(node.crypto.newRandomAgentSecret());
44
+
45
+ expect(getPriorityFromHeader(account.core.header)).toEqual(CO_VALUE_PRIORITY.HIGH);
46
+ });
47
+
48
+ test("returns HIGH priority for group type", () => {
49
+ const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
50
+ const group = node.createGroup();
51
+
52
+ expect(getPriorityFromHeader(group.core.header)).toEqual(CO_VALUE_PRIORITY.HIGH);
53
+ });
54
+
55
+ test("returns MEDIUM priority for other types", () => {
56
+ const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
57
+
58
+ const comap = node.createCoValue({
59
+ type: "comap",
60
+ ruleset: { type: "unsafeAllowAll" },
61
+ meta: null,
62
+ ...Crypto.createdNowUnique(),
63
+ });
64
+
65
+ const colist = node.createCoValue({
66
+ type: "colist",
67
+ ruleset: { type: "unsafeAllowAll" },
68
+ meta: null,
69
+ ...Crypto.createdNowUnique(),
70
+ });
71
+
72
+ expect(getPriorityFromHeader(comap.header)).toEqual(CO_VALUE_PRIORITY.MEDIUM);
73
+ expect(getPriorityFromHeader(colist.header)).toEqual(CO_VALUE_PRIORITY.MEDIUM);
74
+ });
75
+ });