cojson 0.18.28 → 0.18.30

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 (144) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +16 -0
  3. package/dist/PeerState.d.ts +23 -14
  4. package/dist/PeerState.d.ts.map +1 -1
  5. package/dist/PeerState.js +74 -23
  6. package/dist/PeerState.js.map +1 -1
  7. package/dist/SyncStateManager.d.ts +3 -3
  8. package/dist/SyncStateManager.d.ts.map +1 -1
  9. package/dist/SyncStateManager.js +18 -44
  10. package/dist/SyncStateManager.js.map +1 -1
  11. package/dist/coValueContentMessage.d.ts.map +1 -1
  12. package/dist/coValueContentMessage.js +2 -1
  13. package/dist/coValueContentMessage.js.map +1 -1
  14. package/dist/coValueCore/PeerKnownState.d.ts +21 -0
  15. package/dist/coValueCore/PeerKnownState.d.ts.map +1 -0
  16. package/dist/coValueCore/PeerKnownState.js +52 -0
  17. package/dist/coValueCore/PeerKnownState.js.map +1 -0
  18. package/dist/coValueCore/coValueCore.d.ts +39 -8
  19. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  20. package/dist/coValueCore/coValueCore.js +139 -40
  21. package/dist/coValueCore/coValueCore.js.map +1 -1
  22. package/dist/coValueCore/decryptTransactionChangesAndMeta.d.ts.map +1 -1
  23. package/dist/coValueCore/decryptTransactionChangesAndMeta.js +0 -5
  24. package/dist/coValueCore/decryptTransactionChangesAndMeta.js.map +1 -1
  25. package/dist/coValueCore/verifiedState.d.ts +0 -14
  26. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  27. package/dist/coValueCore/verifiedState.js +2 -32
  28. package/dist/coValueCore/verifiedState.js.map +1 -1
  29. package/dist/coValues/coList.d.ts +3 -4
  30. package/dist/coValues/coList.d.ts.map +1 -1
  31. package/dist/coValues/coList.js +4 -4
  32. package/dist/coValues/coList.js.map +1 -1
  33. package/dist/coValues/coMap.d.ts +3 -4
  34. package/dist/coValues/coMap.d.ts.map +1 -1
  35. package/dist/coValues/coMap.js +5 -4
  36. package/dist/coValues/coMap.js.map +1 -1
  37. package/dist/coValues/coStream.d.ts +3 -3
  38. package/dist/coValues/coStream.d.ts.map +1 -1
  39. package/dist/coValues/coStream.js +3 -4
  40. package/dist/coValues/coStream.js.map +1 -1
  41. package/dist/coValues/group.d.ts +3 -3
  42. package/dist/coValues/group.d.ts.map +1 -1
  43. package/dist/coValues/group.js +74 -52
  44. package/dist/coValues/group.js.map +1 -1
  45. package/dist/exports.d.ts +2 -2
  46. package/dist/exports.d.ts.map +1 -1
  47. package/dist/exports.js +2 -2
  48. package/dist/exports.js.map +1 -1
  49. package/dist/localNode.d.ts.map +1 -1
  50. package/dist/localNode.js +7 -5
  51. package/dist/localNode.js.map +1 -1
  52. package/dist/permissions.d.ts +5 -1
  53. package/dist/permissions.d.ts.map +1 -1
  54. package/dist/permissions.js +173 -109
  55. package/dist/permissions.js.map +1 -1
  56. package/dist/sync.d.ts.map +1 -1
  57. package/dist/sync.js +33 -44
  58. package/dist/sync.js.map +1 -1
  59. package/dist/tests/PeerKnownState.test.d.ts +2 -0
  60. package/dist/tests/PeerKnownState.test.d.ts.map +1 -0
  61. package/dist/tests/PeerKnownState.test.js +342 -0
  62. package/dist/tests/PeerKnownState.test.js.map +1 -0
  63. package/dist/tests/PeerState.test.js +17 -16
  64. package/dist/tests/PeerState.test.js.map +1 -1
  65. package/dist/tests/StorageApiAsync.test.js +12 -12
  66. package/dist/tests/StorageApiAsync.test.js.map +1 -1
  67. package/dist/tests/StorageApiSync.test.js +11 -11
  68. package/dist/tests/StorageApiSync.test.js.map +1 -1
  69. package/dist/tests/SyncStateManager.test.js +16 -21
  70. package/dist/tests/SyncStateManager.test.js.map +1 -1
  71. package/dist/tests/coValueCore.dependencies.test.js +59 -0
  72. package/dist/tests/coValueCore.dependencies.test.js.map +1 -1
  73. package/dist/tests/coValueCore.test.js +41 -21
  74. package/dist/tests/coValueCore.test.js.map +1 -1
  75. package/dist/tests/group.addMember.test.js +266 -219
  76. package/dist/tests/group.addMember.test.js.map +1 -1
  77. package/dist/tests/group.inheritance.test.js +12 -0
  78. package/dist/tests/group.inheritance.test.js.map +1 -1
  79. package/dist/tests/group.invite.test.js +77 -0
  80. package/dist/tests/group.invite.test.js.map +1 -1
  81. package/dist/tests/group.removeMember.test.js +64 -7
  82. package/dist/tests/group.removeMember.test.js.map +1 -1
  83. package/dist/tests/group.roleOf.test.js +14 -4
  84. package/dist/tests/group.roleOf.test.js.map +1 -1
  85. package/dist/tests/permissions.test.js +51 -202
  86. package/dist/tests/permissions.test.js.map +1 -1
  87. package/dist/tests/sync.content.test.js +2 -2
  88. package/dist/tests/sync.content.test.js.map +1 -1
  89. package/dist/tests/sync.invite.test.js +6 -6
  90. package/dist/tests/sync.load.test.js +22 -22
  91. package/dist/tests/sync.mesh.test.js +9 -9
  92. package/dist/tests/sync.storage.test.js +13 -7
  93. package/dist/tests/sync.storage.test.js.map +1 -1
  94. package/dist/tests/sync.storageAsync.test.js +3 -3
  95. package/dist/tests/sync.test.js +13 -33
  96. package/dist/tests/sync.test.js.map +1 -1
  97. package/dist/tests/sync.upload.test.js +2 -2
  98. package/package.json +3 -3
  99. package/src/PeerState.ts +86 -34
  100. package/src/SyncStateManager.ts +25 -60
  101. package/src/coValueContentMessage.ts +3 -1
  102. package/src/coValueCore/PeerKnownState.ts +74 -0
  103. package/src/coValueCore/coValueCore.ts +180 -49
  104. package/src/coValueCore/decryptTransactionChangesAndMeta.ts +0 -6
  105. package/src/coValueCore/verifiedState.ts +2 -37
  106. package/src/coValues/coList.ts +7 -7
  107. package/src/coValues/coMap.ts +9 -7
  108. package/src/coValues/coStream.ts +6 -5
  109. package/src/coValues/group.ts +99 -60
  110. package/src/exports.ts +2 -1
  111. package/src/localNode.ts +7 -5
  112. package/src/permissions.ts +204 -123
  113. package/src/sync.ts +37 -53
  114. package/src/tests/PeerKnownState.test.ts +426 -0
  115. package/src/tests/PeerState.test.ts +24 -24
  116. package/src/tests/StorageApiAsync.test.ts +12 -12
  117. package/src/tests/StorageApiSync.test.ts +11 -11
  118. package/src/tests/SyncStateManager.test.ts +23 -53
  119. package/src/tests/coValueCore.dependencies.test.ts +87 -0
  120. package/src/tests/coValueCore.test.ts +64 -22
  121. package/src/tests/group.addMember.test.ts +384 -345
  122. package/src/tests/group.inheritance.test.ts +33 -0
  123. package/src/tests/group.invite.test.ts +117 -0
  124. package/src/tests/group.removeMember.test.ts +95 -9
  125. package/src/tests/group.roleOf.test.ts +16 -4
  126. package/src/tests/permissions.test.ts +56 -295
  127. package/src/tests/sync.content.test.ts +2 -2
  128. package/src/tests/sync.invite.test.ts +6 -6
  129. package/src/tests/sync.load.test.ts +22 -22
  130. package/src/tests/sync.mesh.test.ts +9 -9
  131. package/src/tests/sync.storage.test.ts +13 -8
  132. package/src/tests/sync.storageAsync.test.ts +3 -3
  133. package/src/tests/sync.test.ts +21 -50
  134. package/src/tests/sync.upload.test.ts +2 -2
  135. package/dist/PeerKnownStates.d.ts +0 -19
  136. package/dist/PeerKnownStates.d.ts.map +0 -1
  137. package/dist/PeerKnownStates.js +0 -64
  138. package/dist/PeerKnownStates.js.map +0 -1
  139. package/dist/tests/PeerKnownStates.test.d.ts +0 -2
  140. package/dist/tests/PeerKnownStates.test.d.ts.map +0 -1
  141. package/dist/tests/PeerKnownStates.test.js +0 -77
  142. package/dist/tests/PeerKnownStates.test.js.map +0 -1
  143. package/src/PeerKnownStates.ts +0 -93
  144. package/src/tests/PeerKnownStates.test.ts +0 -99
package/src/PeerState.ts CHANGED
@@ -1,40 +1,55 @@
1
- import { PeerKnownStates, ReadonlyPeerKnownStates } from "./PeerKnownStates.js";
1
+ import { PeerKnownState } from "./coValueCore/PeerKnownState.js";
2
2
  import { RawCoID, SessionID } from "./ids.js";
3
3
  import { CoValueKnownState } from "./knownState.js";
4
4
  import { logger } from "./logger.js";
5
5
  import { Peer, SyncMessage } from "./sync.js";
6
6
 
7
7
  export class PeerState {
8
- constructor(
9
- private peer: Peer,
10
- knownStates: ReadonlyPeerKnownStates | undefined,
11
- ) {
12
- this._knownStates = knownStates?.clone() ?? new PeerKnownStates();
13
- this._optimisticKnownStates = knownStates?.clone() ?? new PeerKnownStates();
14
- }
15
-
16
8
  /**
17
9
  * Here we to collect all the known states that a given peer has told us about.
18
10
  *
19
11
  * This can be used to safely track the sync state of a coValue in a given peer.
20
12
  */
21
- readonly _knownStates: PeerKnownStates;
13
+ private readonly _knownStates: Map<RawCoID, PeerKnownState>;
14
+
15
+ constructor(
16
+ private peer: Peer,
17
+ knownStates: Map<RawCoID, PeerKnownState> | undefined,
18
+ ) {
19
+ this._knownStates = knownStates ?? new Map();
20
+ }
21
+
22
+ getKnownState(id: RawCoID) {
23
+ return this._knownStates.get(id)?.value();
24
+ }
25
+
26
+ getOptimisticKnownState(id: RawCoID) {
27
+ return this._knownStates.get(id)?.optimisticValue();
28
+ }
22
29
 
23
- get knownStates(): ReadonlyPeerKnownStates {
24
- return this._knownStates;
30
+ isCoValueSubscribedToPeer(id: RawCoID) {
31
+ return this._knownStates.has(id);
25
32
  }
26
33
 
27
34
  /**
28
- * This one collects the known states "optimistically".
29
- * We use it to keep track of the content we have sent to a given peer.
35
+ * Closes the current peer state and creates a new one from a given peer,
36
+ * keeping the same known states.
30
37
  *
31
- * The main difference with knownState is that this is updated when the content is sent to the peer without
32
- * waiting for any acknowledgement from the peer.
38
+ * This is used to create a new peer state when a peer reconnects.
33
39
  */
34
- readonly _optimisticKnownStates: PeerKnownStates;
40
+ newPeerStateFrom(peer: Peer) {
41
+ if (!this.closed) {
42
+ this.gracefulShutdown();
43
+ }
44
+
45
+ const knownStates = new Map<RawCoID, PeerKnownState>();
46
+ // On reconnect, we reset all the optimistic known states
47
+ // because we can't know if those syncs were successful or not
48
+ for (const knownState of this._knownStates.values()) {
49
+ knownStates.set(knownState.id, knownState.cloneWithoutOptimistic());
50
+ }
35
51
 
36
- get optimisticKnownStates(): ReadonlyPeerKnownStates {
37
- return this._optimisticKnownStates;
52
+ return new PeerState(peer, knownStates);
38
53
  }
39
54
 
40
55
  readonly toldKnownState: Set<RawCoID> = new Set();
@@ -49,35 +64,72 @@ export class PeerState {
49
64
  this.toldKnownState.add(id);
50
65
  }
51
66
 
67
+ private getOrCreateKnownState(id: RawCoID) {
68
+ let knownState = this._knownStates.get(id);
69
+
70
+ if (!knownState) {
71
+ knownState = new PeerKnownState(id, this.peer.id);
72
+ this._knownStates.set(id, knownState);
73
+ }
74
+
75
+ return knownState;
76
+ }
77
+
52
78
  updateHeader(id: RawCoID, header: boolean) {
53
- this._knownStates.updateHeader(id, header);
54
- this._optimisticKnownStates.updateHeader(id, header);
79
+ const knownState = this.getOrCreateKnownState(id);
80
+ knownState.updateHeader(header);
81
+ this.triggerUpdate(id, knownState);
55
82
  }
56
83
 
57
84
  combineWith(id: RawCoID, value: CoValueKnownState) {
58
- this._knownStates.combineWith(id, value);
59
- this._optimisticKnownStates.combineWith(id, value);
85
+ const knownState = this.getOrCreateKnownState(id);
86
+ knownState.combineWith(value);
87
+ this.triggerUpdate(id, knownState);
60
88
  }
61
89
 
62
90
  combineOptimisticWith(id: RawCoID, value: CoValueKnownState) {
63
- this._optimisticKnownStates.combineWith(id, value);
91
+ const knownState = this.getOrCreateKnownState(id);
92
+ knownState.combineOptimisticWith(value);
93
+ this.triggerUpdate(id, knownState);
64
94
  }
65
95
 
66
- updateSessionCounter(id: RawCoID, sessionId: SessionID, value: number) {
67
- this._knownStates.updateSessionCounter(id, sessionId, value);
68
- this._optimisticKnownStates.updateSessionCounter(id, sessionId, value);
96
+ setKnownState(id: RawCoID, payload: CoValueKnownState | "empty") {
97
+ const knownState = this.getOrCreateKnownState(id);
98
+ knownState.set(payload);
99
+ this.triggerUpdate(id, knownState);
69
100
  }
70
101
 
71
- setKnownState(id: RawCoID, knownState: CoValueKnownState | "empty") {
72
- this._knownStates.set(id, knownState);
73
- this._optimisticKnownStates.set(id, knownState);
102
+ /**
103
+ * Emit a change event for a given coValue.
104
+ *
105
+ * This is used to notify subscribers that the known state of a coValue has changed,
106
+ * but the known state of the peer has not.
107
+ */
108
+ emitCoValueChange(id: RawCoID) {
109
+ if (this.peer.role === "client" && !this.isCoValueSubscribedToPeer(id)) {
110
+ return;
111
+ }
112
+
113
+ const knownState = this.getOrCreateKnownState(id);
114
+ this.triggerUpdate(id, knownState);
115
+ }
116
+
117
+ listeners = new Set<(id: RawCoID, value: PeerKnownState) => void>();
118
+
119
+ private triggerUpdate(id: RawCoID, value: PeerKnownState) {
120
+ for (const listener of this.listeners) {
121
+ listener(id, value);
122
+ }
74
123
  }
75
124
 
76
- setOptimisticKnownState(
77
- id: RawCoID,
78
- knownState: CoValueKnownState | "empty",
125
+ subscribeToKnownStatesUpdates(
126
+ listener: (id: RawCoID, value: PeerKnownState) => void,
79
127
  ) {
80
- this._optimisticKnownStates.set(id, knownState);
128
+ this.listeners.add(listener);
129
+
130
+ return () => {
131
+ this.listeners.delete(listener);
132
+ };
81
133
  }
82
134
 
83
135
  get id() {
@@ -1,9 +1,6 @@
1
1
  import { RawCoID } from "./ids.js";
2
- import {
3
- CoValueKnownState,
4
- emptyKnownState,
5
- areLocalSessionsUploaded,
6
- } from "./knownState.js";
2
+ import { CoValueKnownState, areLocalSessionsUploaded } from "./knownState.js";
3
+ import { PeerState } from "./PeerState.js";
7
4
  import { PeerID, SyncManager } from "./sync.js";
8
5
 
9
6
  export type SyncState = {
@@ -55,40 +52,17 @@ export class SyncStateManager {
55
52
  };
56
53
  }
57
54
 
58
- getCurrentSyncState(peerId: PeerID, id: RawCoID) {
59
- // Build a lazy sync state object to process the isUploaded info
60
- // only when requested
61
- const syncState = {} as SyncState;
62
-
63
- const getIsUploaded = () =>
64
- this.getIsCoValueFullyUploadedIntoPeer(peerId, id);
65
-
66
- Object.defineProperties(syncState, {
67
- uploaded: {
68
- enumerable: true,
69
- get: getIsUploaded,
70
- },
71
- });
72
-
73
- return syncState;
74
- }
75
-
76
- triggerUpdate(peerId: PeerID, id: RawCoID) {
77
- const peer = this.syncManager.peers[peerId];
78
-
79
- if (!peer) {
80
- return;
81
- }
82
-
83
- const peerListeners = this.listenersByPeers.get(peer.id);
55
+ triggerUpdate(peerId: PeerID, id: RawCoID, knownState: CoValueKnownState) {
56
+ const peerListeners = this.listenersByPeers.get(peerId);
84
57
 
85
58
  // If we don't have any active listeners do nothing
86
59
  if (!peerListeners?.size && !this.listeners.size) {
87
60
  return;
88
61
  }
89
62
 
90
- const knownState = peer.knownStates.get(id) ?? emptyKnownState(id);
91
- const syncState = this.getCurrentSyncState(peerId, id);
63
+ const syncState = {
64
+ uploaded: this.getIsCoValueFullyUploadedIntoPeer(knownState, id),
65
+ };
92
66
 
93
67
  for (const listener of this.listeners) {
94
68
  listener(peerId, knownState, syncState);
@@ -101,40 +75,31 @@ export class SyncStateManager {
101
75
  }
102
76
  }
103
77
 
104
- private getKnownStateSessions(peerId: PeerID, id: RawCoID) {
105
- const peer = this.syncManager.peers[peerId];
106
-
107
- if (!peer) {
108
- return undefined;
109
- }
78
+ isSynced(peer: PeerState, id: RawCoID) {
79
+ const peerKnownState = peer.getKnownState(id);
110
80
 
111
- const peerSessions = peer.knownStates.get(id)?.sessions;
81
+ if (!peerKnownState) return false;
112
82
 
113
- if (!peerSessions) {
114
- return undefined;
115
- }
116
-
117
- const entry = this.syncManager.local.getCoValue(id);
118
-
119
- if (!entry.isAvailable()) {
120
- return undefined;
121
- }
122
-
123
- const coValueSessions = entry.knownState().sessions;
124
-
125
- return {
126
- peer: peerSessions,
127
- coValue: coValueSessions,
128
- };
83
+ return this.getIsCoValueFullyUploadedIntoPeer(peerKnownState, id);
129
84
  }
130
85
 
131
- private getIsCoValueFullyUploadedIntoPeer(peerId: PeerID, id: RawCoID) {
132
- const sessions = this.getKnownStateSessions(peerId, id);
86
+ private getIsCoValueFullyUploadedIntoPeer(
87
+ peerKnownState: CoValueKnownState,
88
+ id: RawCoID,
89
+ ) {
90
+ const entry = this.syncManager.local.getCoValue(id);
133
91
 
134
- if (!sessions) {
92
+ if (!entry.hasVerifiedContent()) {
135
93
  return false;
136
94
  }
137
95
 
138
- return areLocalSessionsUploaded(sessions.coValue, sessions.peer);
96
+ // Accessing verified knownState to skip the immutability
97
+ // applied on CoValueCore
98
+ const knownState = entry.verified.knownState();
99
+
100
+ return areLocalSessionsUploaded(
101
+ knownState.sessions,
102
+ peerKnownState.sessions,
103
+ );
139
104
  }
140
105
  }
@@ -61,8 +61,10 @@ export function exceedsRecommendedSize(
61
61
  );
62
62
  }
63
63
 
64
+ const cachedTextEncoder = new TextEncoder();
65
+
64
66
  export function validateTxSizeLimitInBytes(changes: JsonValue[]): void {
65
- const serializedSize = new TextEncoder().encode(
67
+ const serializedSize = cachedTextEncoder.encode(
66
68
  JSON.stringify(changes),
67
69
  ).length;
68
70
  if (serializedSize > TRANSACTION_CONFIG.MAX_TX_SIZE_BYTES) {
@@ -0,0 +1,74 @@
1
+ import type { RawCoID } from "../ids.js";
2
+ import {
3
+ cloneKnownState,
4
+ combineKnownStates,
5
+ CoValueKnownState,
6
+ emptyKnownState,
7
+ } from "../knownState.js";
8
+ import { PeerID } from "../sync.js";
9
+
10
+ export class PeerKnownState {
11
+ readonly id: RawCoID;
12
+ readonly peerId: PeerID;
13
+ private knownState: CoValueKnownState;
14
+ private optimisticKnownState?: CoValueKnownState;
15
+
16
+ constructor(id: RawCoID, peerId: PeerID) {
17
+ this.id = id;
18
+ this.peerId = peerId;
19
+ this.knownState = emptyKnownState(id);
20
+ }
21
+
22
+ cloneWithoutOptimistic() {
23
+ const clone = new PeerKnownState(this.id, this.peerId);
24
+ clone.set(this.knownState);
25
+ return clone;
26
+ }
27
+
28
+ updateHeader(header: boolean) {
29
+ this.knownState.header = header;
30
+
31
+ if (this.optimisticKnownState) {
32
+ this.optimisticKnownState.header = header;
33
+ }
34
+ }
35
+
36
+ combineWith(value: CoValueKnownState) {
37
+ combineKnownStates(this.knownState, value);
38
+
39
+ if (this.optimisticKnownState) {
40
+ combineKnownStates(this.optimisticKnownState, value);
41
+ }
42
+ }
43
+
44
+ combineOptimisticWith(value: CoValueKnownState) {
45
+ if (!this.optimisticKnownState) {
46
+ this.optimisticKnownState = cloneKnownState(this.knownState);
47
+ }
48
+
49
+ combineKnownStates(this.optimisticKnownState, value);
50
+ }
51
+
52
+ /**
53
+ * Aligns the CoValue known state with the defined payload
54
+ */
55
+ set(payload: CoValueKnownState | "empty") {
56
+ if (payload === "empty") {
57
+ this.knownState.header = false;
58
+ this.knownState.sessions = {};
59
+ } else {
60
+ this.knownState.header = payload.header;
61
+ this.knownState.sessions = { ...payload.sessions };
62
+ }
63
+
64
+ this.optimisticKnownState = undefined;
65
+ }
66
+
67
+ value() {
68
+ return this.knownState;
69
+ }
70
+
71
+ optimisticValue() {
72
+ return this.optimisticKnownState ?? this.knownState;
73
+ }
74
+ }