cojson 0.13.17 → 0.13.18

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 (166) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +9 -0
  3. package/dist/PeerState.d.ts +3 -0
  4. package/dist/PeerState.d.ts.map +1 -1
  5. package/dist/PeerState.js +9 -0
  6. package/dist/PeerState.js.map +1 -1
  7. package/dist/SyncStateManager.d.ts.map +1 -1
  8. package/dist/SyncStateManager.js +2 -3
  9. package/dist/SyncStateManager.js.map +1 -1
  10. package/dist/coValue.d.ts +4 -4
  11. package/dist/coValue.d.ts.map +1 -1
  12. package/dist/coValue.js +4 -4
  13. package/dist/coValue.js.map +1 -1
  14. package/dist/coValueCore/coValueCore.d.ts +143 -0
  15. package/dist/coValueCore/coValueCore.d.ts.map +1 -0
  16. package/dist/{coValueCore.js → coValueCore/coValueCore.js} +314 -246
  17. package/dist/coValueCore/coValueCore.js.map +1 -0
  18. package/dist/coValueCore/verifiedState.d.ts +65 -0
  19. package/dist/coValueCore/verifiedState.d.ts.map +1 -0
  20. package/dist/coValueCore/verifiedState.js +210 -0
  21. package/dist/coValueCore/verifiedState.js.map +1 -0
  22. package/dist/coValues/account.d.ts +8 -10
  23. package/dist/coValues/account.d.ts.map +1 -1
  24. package/dist/coValues/account.js +12 -13
  25. package/dist/coValues/account.js.map +1 -1
  26. package/dist/coValues/coList.d.ts +3 -3
  27. package/dist/coValues/coList.d.ts.map +1 -1
  28. package/dist/coValues/coList.js +6 -3
  29. package/dist/coValues/coList.js.map +1 -1
  30. package/dist/coValues/coMap.d.ts +3 -3
  31. package/dist/coValues/coMap.d.ts.map +1 -1
  32. package/dist/coValues/coMap.js +3 -3
  33. package/dist/coValues/coMap.js.map +1 -1
  34. package/dist/coValues/coPlainText.d.ts +2 -2
  35. package/dist/coValues/coPlainText.d.ts.map +1 -1
  36. package/dist/coValues/coPlainText.js +4 -4
  37. package/dist/coValues/coPlainText.js.map +1 -1
  38. package/dist/coValues/coStream.d.ts +3 -3
  39. package/dist/coValues/coStream.d.ts.map +1 -1
  40. package/dist/coValues/coStream.js +3 -3
  41. package/dist/coValues/coStream.js.map +1 -1
  42. package/dist/coValues/group.d.ts +7 -2
  43. package/dist/coValues/group.d.ts.map +1 -1
  44. package/dist/coValues/group.js +29 -26
  45. package/dist/coValues/group.js.map +1 -1
  46. package/dist/coreToCoValue.d.ts +2 -2
  47. package/dist/coreToCoValue.d.ts.map +1 -1
  48. package/dist/coreToCoValue.js +10 -14
  49. package/dist/coreToCoValue.js.map +1 -1
  50. package/dist/exports.d.ts +6 -5
  51. package/dist/exports.d.ts.map +1 -1
  52. package/dist/exports.js +3 -4
  53. package/dist/exports.js.map +1 -1
  54. package/dist/localNode.d.ts +30 -24
  55. package/dist/localNode.d.ts.map +1 -1
  56. package/dist/localNode.js +139 -170
  57. package/dist/localNode.js.map +1 -1
  58. package/dist/permissions.d.ts +2 -1
  59. package/dist/permissions.d.ts.map +1 -1
  60. package/dist/permissions.js +15 -11
  61. package/dist/permissions.js.map +1 -1
  62. package/dist/priority.d.ts +1 -1
  63. package/dist/priority.d.ts.map +1 -1
  64. package/dist/sync.d.ts +2 -2
  65. package/dist/sync.d.ts.map +1 -1
  66. package/dist/sync.js +86 -55
  67. package/dist/sync.js.map +1 -1
  68. package/dist/tests/coList.test.js +19 -16
  69. package/dist/tests/coList.test.js.map +1 -1
  70. package/dist/tests/coMap.test.js +12 -13
  71. package/dist/tests/coMap.test.js.map +1 -1
  72. package/dist/tests/coPlainText.test.js +9 -10
  73. package/dist/tests/coPlainText.test.js.map +1 -1
  74. package/dist/tests/coStream.test.js +22 -17
  75. package/dist/tests/coStream.test.js.map +1 -1
  76. package/dist/tests/coValueCore.test.js +22 -28
  77. package/dist/tests/coValueCore.test.js.map +1 -1
  78. package/dist/tests/coValueCoreLoadingState.test.d.ts +2 -0
  79. package/dist/tests/coValueCoreLoadingState.test.d.ts.map +1 -0
  80. package/dist/tests/{coValueState.test.js → coValueCoreLoadingState.test.js} +62 -46
  81. package/dist/tests/coValueCoreLoadingState.test.js.map +1 -0
  82. package/dist/tests/group.test.js +42 -43
  83. package/dist/tests/group.test.js.map +1 -1
  84. package/dist/tests/messagesTestUtils.d.ts +2 -2
  85. package/dist/tests/messagesTestUtils.d.ts.map +1 -1
  86. package/dist/tests/messagesTestUtils.js +1 -1
  87. package/dist/tests/messagesTestUtils.js.map +1 -1
  88. package/dist/tests/permissions.test.js +224 -292
  89. package/dist/tests/permissions.test.js.map +1 -1
  90. package/dist/tests/priority.test.js +13 -14
  91. package/dist/tests/priority.test.js.map +1 -1
  92. package/dist/tests/sync.auth.test.d.ts +2 -0
  93. package/dist/tests/sync.auth.test.d.ts.map +1 -0
  94. package/dist/tests/sync.auth.test.js +141 -0
  95. package/dist/tests/sync.auth.test.js.map +1 -0
  96. package/dist/tests/sync.load.test.js +4 -4
  97. package/dist/tests/sync.load.test.js.map +1 -1
  98. package/dist/tests/sync.mesh.test.js +25 -12
  99. package/dist/tests/sync.mesh.test.js.map +1 -1
  100. package/dist/tests/sync.peerReconciliation.test.js +19 -19
  101. package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
  102. package/dist/tests/sync.storage.test.js +20 -13
  103. package/dist/tests/sync.storage.test.js.map +1 -1
  104. package/dist/tests/sync.test.js +32 -39
  105. package/dist/tests/sync.test.js.map +1 -1
  106. package/dist/tests/sync.upload.test.js +126 -37
  107. package/dist/tests/sync.upload.test.js.map +1 -1
  108. package/dist/tests/testUtils.d.ts +24 -15
  109. package/dist/tests/testUtils.d.ts.map +1 -1
  110. package/dist/tests/testUtils.js +88 -61
  111. package/dist/tests/testUtils.js.map +1 -1
  112. package/dist/typeUtils/expectGroup.js +1 -1
  113. package/dist/typeUtils/expectGroup.js.map +1 -1
  114. package/package.json +1 -1
  115. package/src/PeerState.ts +11 -0
  116. package/src/SyncStateManager.ts +2 -3
  117. package/src/coValue.ts +11 -8
  118. package/src/{coValueCore.ts → coValueCore/coValueCore.ts} +469 -413
  119. package/src/coValueCore/verifiedState.ts +376 -0
  120. package/src/coValues/account.ts +20 -25
  121. package/src/coValues/coList.ts +12 -6
  122. package/src/coValues/coMap.ts +9 -6
  123. package/src/coValues/coPlainText.ts +9 -6
  124. package/src/coValues/coStream.ts +9 -6
  125. package/src/coValues/group.ts +50 -28
  126. package/src/coreToCoValue.ts +14 -15
  127. package/src/exports.ts +9 -7
  128. package/src/localNode.ts +227 -273
  129. package/src/permissions.ts +18 -12
  130. package/src/priority.ts +1 -1
  131. package/src/sync.ts +96 -63
  132. package/src/tests/coList.test.ts +21 -15
  133. package/src/tests/coMap.test.ts +12 -13
  134. package/src/tests/coPlainText.test.ts +12 -9
  135. package/src/tests/coStream.test.ts +25 -16
  136. package/src/tests/coValueCore.test.ts +30 -27
  137. package/src/tests/{coValueState.test.ts → coValueCoreLoadingState.test.ts} +67 -57
  138. package/src/tests/group.test.ts +44 -68
  139. package/src/tests/messagesTestUtils.ts +3 -8
  140. package/src/tests/permissions.test.ts +283 -449
  141. package/src/tests/priority.test.ts +17 -13
  142. package/src/tests/sync.auth.test.ts +188 -0
  143. package/src/tests/sync.load.test.ts +4 -4
  144. package/src/tests/sync.mesh.test.ts +25 -12
  145. package/src/tests/sync.peerReconciliation.test.ts +25 -25
  146. package/src/tests/sync.storage.test.ts +20 -13
  147. package/src/tests/sync.test.ts +43 -43
  148. package/src/tests/sync.upload.test.ts +157 -37
  149. package/src/tests/testUtils.ts +120 -74
  150. package/src/typeUtils/expectGroup.ts +1 -1
  151. package/dist/CoValuesStore.d.ts +0 -14
  152. package/dist/CoValuesStore.d.ts.map +0 -1
  153. package/dist/CoValuesStore.js +0 -32
  154. package/dist/CoValuesStore.js.map +0 -1
  155. package/dist/coValueCore.d.ts +0 -142
  156. package/dist/coValueCore.d.ts.map +0 -1
  157. package/dist/coValueCore.js.map +0 -1
  158. package/dist/coValueState.d.ts +0 -34
  159. package/dist/coValueState.d.ts.map +0 -1
  160. package/dist/coValueState.js +0 -190
  161. package/dist/coValueState.js.map +0 -1
  162. package/dist/tests/coValueState.test.d.ts +0 -2
  163. package/dist/tests/coValueState.test.d.ts.map +0 -1
  164. package/dist/tests/coValueState.test.js.map +0 -1
  165. package/src/CoValuesStore.ts +0 -41
  166. package/src/coValueState.ts +0 -245
@@ -1,5 +1,6 @@
1
1
  import { CoID } from "./coValue.js";
2
- import { CoValueCore, Transaction } from "./coValueCore.js";
2
+ import { CoValueCore } from "./coValueCore/coValueCore.js";
3
+ import { Transaction } from "./coValueCore/verifiedState.js";
3
4
  import { RawAccount, RawAccountID, RawProfile } from "./coValues/account.js";
4
5
  import { MapOpPayload } from "./coValues/coMap.js";
5
6
  import {
@@ -64,19 +65,23 @@ export function determineValidTransactions(
64
65
  coValue: CoValueCore,
65
66
  knownTransactions?: CoValueKnownState["sessions"],
66
67
  ): { txID: TransactionID; tx: Transaction }[] {
67
- if (coValue.header.ruleset.type === "group") {
68
- const initialAdmin = coValue.header.ruleset.initialAdmin;
68
+ if (!coValue.isAvailable()) {
69
+ throw new Error("determineValidTransactions CoValue is not available");
70
+ }
71
+
72
+ if (coValue.verified.header.ruleset.type === "group") {
73
+ const initialAdmin = coValue.verified.header.ruleset.initialAdmin;
69
74
  if (!initialAdmin) {
70
75
  throw new Error("Group must have initialAdmin");
71
76
  }
72
77
 
73
78
  return determineValidTransactionsForGroup(coValue, initialAdmin)
74
79
  .validTransactions;
75
- } else if (coValue.header.ruleset.type === "ownedByGroup") {
80
+ } else if (coValue.verified.header.ruleset.type === "ownedByGroup") {
76
81
  const groupContent = expectGroup(
77
82
  coValue.node
78
83
  .expectCoValueLoaded(
79
- coValue.header.ruleset.group,
84
+ coValue.verified.header.ruleset.group,
80
85
  "Determining valid transaction in owned object but its group wasn't loaded",
81
86
  )
82
87
  .getCurrentContent(),
@@ -88,7 +93,7 @@ export function determineValidTransactions(
88
93
 
89
94
  const validTransactions: ValidTransactionsResult[] = [];
90
95
 
91
- for (const [sessionID, sessionLog] of coValue.sessionLogs.entries()) {
96
+ for (const [sessionID, sessionLog] of coValue.verified.sessions.entries()) {
92
97
  const transactor = accountOrAgentIDfromSessionID(sessionID);
93
98
  const knownTransactionsForSession = knownTransactions?.[sessionID] ?? -1;
94
99
 
@@ -123,10 +128,10 @@ export function determineValidTransactions(
123
128
  }
124
129
 
125
130
  return validTransactions;
126
- } else if (coValue.header.ruleset.type === "unsafeAllowAll") {
131
+ } else if (coValue.verified.header.ruleset.type === "unsafeAllowAll") {
127
132
  const validTransactions: ValidTransactionsResult[] = [];
128
133
 
129
- for (const [sessionID, sessionLog] of coValue.sessionLogs.entries()) {
134
+ for (const [sessionID, sessionLog] of coValue.verified.sessions.entries()) {
130
135
  const knownTransactionsForSession = knownTransactions?.[sessionID] ?? -1;
131
136
 
132
137
  sessionLog.transactions.forEach((tx, txIndex) => {
@@ -141,7 +146,7 @@ export function determineValidTransactions(
141
146
  } else {
142
147
  throw new Error(
143
148
  "Unknown ruleset type " +
144
- (coValue.header.ruleset as { type: string }).type,
149
+ (coValue.verified.header.ruleset as { type: string }).type,
145
150
  );
146
151
  }
147
152
  }
@@ -167,7 +172,7 @@ function resolveMemberStateFromParentReference(
167
172
  "Expected parent group to be loaded",
168
173
  );
169
174
 
170
- if (parentGroup.header.ruleset.type !== "group") {
175
+ if (parentGroup.verified.header.ruleset.type !== "group") {
171
176
  return;
172
177
  }
173
178
 
@@ -176,7 +181,7 @@ function resolveMemberStateFromParentReference(
176
181
  return;
177
182
  }
178
183
 
179
- const initialAdmin = parentGroup.header.ruleset.initialAdmin;
184
+ const initialAdmin = parentGroup.verified.header.ruleset.initialAdmin;
180
185
 
181
186
  if (!initialAdmin) {
182
187
  throw new Error("Group must have initialAdmin");
@@ -214,7 +219,8 @@ function determineValidTransactionsForGroup(
214
219
  tx: Transaction;
215
220
  }[] = [];
216
221
 
217
- for (const [sessionID, sessionLog] of coValue.sessionLogs.entries()) {
222
+ for (const [sessionID, sessionLog] of coValue.verified?.sessions.entries() ??
223
+ []) {
218
224
  sessionLog.transactions.forEach((tx, txIndex) => {
219
225
  allTransactionsSorted.push({ sessionID, txIndex, tx });
220
226
  });
package/src/priority.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { type CoValueHeader } from "./coValueCore.js";
1
+ import { type CoValueHeader } from "./coValueCore/verifiedState.js";
2
2
 
3
3
  /**
4
4
  * The priority of a `CoValue` determines how much priority is given
package/src/sync.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import { Histogram, ValueType, metrics } from "@opentelemetry/api";
2
2
  import { PeerState } from "./PeerState.js";
3
3
  import { SyncStateManager } from "./SyncStateManager.js";
4
- import { CoValueHeader, Transaction } from "./coValueCore.js";
5
- import { CoValueCore } from "./coValueCore.js";
6
- import { CoValueState } from "./coValueState.js";
4
+ import {
5
+ AvailableCoValueCore,
6
+ CoValueCore,
7
+ } from "./coValueCore/coValueCore.js";
8
+ import { CoValueHeader, Transaction } from "./coValueCore/verifiedState.js";
7
9
  import { Signature } from "./crypto/crypto.js";
8
10
  import { RawCoID, SessionID } from "./ids.js";
9
11
  import { LocalNode } from "./localNode.js";
@@ -158,7 +160,7 @@ export class SyncManager {
158
160
  }
159
161
 
160
162
  handleSyncMessage(msg: SyncMessage, peer: PeerState) {
161
- if (this.local.coValuesStore.get(msg.id).isErroredInPeer(peer.id)) {
163
+ if (this.local.getCoValue(msg.id).isErroredInPeer(peer.id)) {
162
164
  logger.warn(
163
165
  `Skipping message ${msg.action} on errored coValue ${msg.id} from peer ${peer.id}`,
164
166
  );
@@ -197,13 +199,17 @@ export class SyncManager {
197
199
  }
198
200
 
199
201
  sendNewContentIncludingDependencies(id: RawCoID, peer: PeerState) {
200
- const coValue = this.local.expectCoValueLoaded(id);
202
+ const coValue = this.local.getCoValue(id);
203
+
204
+ if (!coValue.isAvailable()) {
205
+ return;
206
+ }
201
207
 
202
208
  coValue
203
209
  .getDependedOnCoValues()
204
210
  .map((id) => this.sendNewContentIncludingDependencies(id, peer));
205
211
 
206
- const newContentPieces = coValue.newContentSince(
212
+ const newContentPieces = coValue.verified.newContentSince(
207
213
  peer.optimisticKnownStates.get(id),
208
214
  );
209
215
 
@@ -212,16 +218,15 @@ export class SyncManager {
212
218
  this.trySendToPeer(peer, piece);
213
219
  }
214
220
 
215
- peer.toldKnownState.add(id);
216
221
  peer.combineOptimisticWith(id, coValue.knownState());
217
222
  } else if (!peer.toldKnownState.has(id)) {
218
223
  this.trySendToPeer(peer, {
219
224
  action: "known",
220
225
  ...coValue.knownState(),
221
226
  });
222
-
223
- peer.toldKnownState.add(id);
224
227
  }
228
+
229
+ peer.trackToldKnownState(id);
225
230
  }
226
231
 
227
232
  startPeerReconciliation(peer: PeerState) {
@@ -237,40 +242,38 @@ export class SyncManager {
237
242
  gathered.add(coValue.id);
238
243
 
239
244
  for (const id of coValue.getDependedOnCoValues()) {
240
- const entry = this.local.coValuesStore.get(id);
245
+ const coValue = this.local.getCoValue(id);
241
246
 
242
- if (entry.isAvailable()) {
243
- buildOrderedCoValueList(entry.core);
247
+ if (coValue.isAvailable()) {
248
+ buildOrderedCoValueList(coValue);
244
249
  }
245
250
  }
246
251
 
247
252
  coValuesOrderedByDependency.push(coValue);
248
253
  };
249
254
 
250
- for (const entry of this.local.coValuesStore.getValues()) {
251
- if (!entry.isAvailable()) {
255
+ for (const coValue of this.local.allCoValues()) {
256
+ if (!coValue.isAvailable()) {
252
257
  // If the coValue is unavailable and we never tried this peer
253
258
  // we try to load it from the peer
254
- if (!peer.toldKnownState.has(entry.id)) {
255
- peer.toldKnownState.add(entry.id);
259
+ if (!peer.loadRequestSent.has(coValue.id)) {
260
+ peer.trackLoadRequestSent(coValue.id);
256
261
  this.trySendToPeer(peer, {
257
262
  action: "load",
258
263
  header: false,
259
- id: entry.id,
264
+ id: coValue.id,
260
265
  sessions: {},
261
266
  });
262
267
  }
263
268
  } else {
264
- const coValue = entry.core;
265
-
266
269
  // Build the list of coValues ordered by dependency
267
270
  // so we can send the load message in the correct order
268
271
  buildOrderedCoValueList(coValue);
269
272
  }
270
273
 
271
274
  // Fill the missing known states with empty known states
272
- if (!peer.optimisticKnownStates.has(entry.id)) {
273
- peer.setOptimisticKnownState(entry.id, "empty");
275
+ if (!peer.optimisticKnownStates.has(coValue.id)) {
276
+ peer.setOptimisticKnownState(coValue.id, "empty");
274
277
  }
275
278
  }
276
279
 
@@ -281,7 +284,7 @@ export class SyncManager {
281
284
  * - Start the sync process in case we or the other peer
282
285
  * lacks some transactions
283
286
  */
284
- peer.toldKnownState.add(coValue.id);
287
+ peer.trackLoadRequestSent(coValue.id);
285
288
  this.trySendToPeer(peer, {
286
289
  action: "load",
287
290
  ...coValue.knownState(),
@@ -368,11 +371,11 @@ export class SyncManager {
368
371
  *
369
372
  */
370
373
  peer.setKnownState(msg.id, knownStateIn(msg));
371
- const entry = this.local.coValuesStore.get(msg.id);
374
+ const coValue = this.local.getCoValue(msg.id);
372
375
 
373
376
  if (
374
- entry.highLevelState === "unknown" ||
375
- entry.highLevelState === "unavailable"
377
+ coValue.loadingState === "unknown" ||
378
+ coValue.loadingState === "unavailable"
376
379
  ) {
377
380
  const eligiblePeers = this.getServerAndStoragePeers(peer.id);
378
381
 
@@ -380,8 +383,7 @@ export class SyncManager {
380
383
  // We don't have any eligible peers to load the coValue from
381
384
  // so we send a known state back to the sender to let it know
382
385
  // that the coValue is unavailable
383
- peer.toldKnownState.add(msg.id);
384
-
386
+ peer.trackToldKnownState(msg.id);
385
387
  this.trySendToPeer(peer, {
386
388
  action: "known",
387
389
  id: msg.id,
@@ -398,17 +400,16 @@ export class SyncManager {
398
400
  }
399
401
  }
400
402
 
401
- if (entry.highLevelState === "loading") {
403
+ if (coValue.loadingState === "loading") {
402
404
  // We need to return from handleLoad immediately and wait for the CoValue to be loaded
403
405
  // in a new task, otherwise we might block further incoming content messages that would
404
406
  // resolve the CoValue as available. This can happen when we receive fresh
405
407
  // content from a client, but we are a server with our own upstream server(s)
406
- entry
407
- .getCoValue()
408
+ coValue
409
+ .waitForAvailableOrUnavailable()
408
410
  .then(async (value) => {
409
- if (value === "unavailable") {
410
- peer.toldKnownState.add(msg.id);
411
-
411
+ if (!value.isAvailable()) {
412
+ peer.trackToldKnownState(msg.id);
412
413
  this.trySendToPeer(peer, {
413
414
  action: "known",
414
415
  id: msg.id,
@@ -426,9 +427,10 @@ export class SyncManager {
426
427
  err: e,
427
428
  });
428
429
  });
429
- } else if (entry.isAvailable()) {
430
+ } else if (coValue.isAvailable()) {
430
431
  this.sendNewContentIncludingDependencies(msg.id, peer);
431
432
  } else {
433
+ peer.trackToldKnownState(msg.id);
432
434
  this.trySendToPeer(peer, {
433
435
  action: "known",
434
436
  id: msg.id,
@@ -439,7 +441,7 @@ export class SyncManager {
439
441
  }
440
442
 
441
443
  handleKnownState(msg: KnownStateMessage, peer: PeerState) {
442
- const entry = this.local.coValuesStore.get(msg.id);
444
+ const coValue = this.local.getCoValue(msg.id);
443
445
 
444
446
  peer.combineWith(msg.id, knownStateIn(msg));
445
447
 
@@ -448,10 +450,10 @@ export class SyncManager {
448
450
  const availableOnPeer = peer.optimisticKnownStates.get(msg.id)?.header;
449
451
 
450
452
  if (!availableOnPeer) {
451
- entry.markNotFoundInPeer(peer.id);
453
+ coValue.markNotFoundInPeer(peer.id);
452
454
  }
453
455
 
454
- if (entry.isAvailable()) {
456
+ if (coValue.isAvailable()) {
455
457
  this.sendNewContentIncludingDependencies(msg.id, peer);
456
458
  }
457
459
  }
@@ -470,11 +472,9 @@ export class SyncManager {
470
472
  }
471
473
 
472
474
  handleNewContent(msg: NewContentMessage, peer: PeerState) {
473
- const entry = this.local.coValuesStore.get(msg.id);
474
-
475
- let coValue: CoValueCore;
475
+ const coValue = this.local.getCoValue(msg.id);
476
476
 
477
- if (!entry.isAvailable()) {
477
+ if (!coValue.isAvailable()) {
478
478
  if (!msg.header) {
479
479
  this.trySendToPeer(peer, {
480
480
  action: "known",
@@ -487,12 +487,11 @@ export class SyncManager {
487
487
  }
488
488
 
489
489
  peer.updateHeader(msg.id, true);
490
+ coValue.markAvailable(msg.header, peer.id);
491
+ }
490
492
 
491
- coValue = new CoValueCore(msg.header, this.local);
492
-
493
- entry.markAvailable(coValue, peer.id);
494
- } else {
495
- coValue = entry.core;
493
+ if (!coValue.isAvailable()) {
494
+ throw new Error("Unreachable: CoValue should be available in every case");
496
495
  }
497
496
 
498
497
  let invalidStateAssumed = false;
@@ -502,7 +501,7 @@ export class SyncManager {
502
501
  SessionNewContent,
503
502
  ][]) {
504
503
  const ourKnownTxIdx =
505
- coValue.sessionLogs.get(sessionID)?.transactions.length;
504
+ coValue.verified.sessions.get(sessionID)?.transactions.length;
506
505
  const theirFirstNewTxIdx = newContentForSession.after;
507
506
 
508
507
  if ((ourKnownTxIdx || 0) < theirFirstNewTxIdx) {
@@ -526,16 +525,17 @@ export class SyncManager {
526
525
  newTransactions,
527
526
  undefined,
528
527
  newContentForSession.lastSignature,
528
+ "immediate", // TODO: can we change this to deferred?
529
529
  );
530
530
 
531
531
  if (result.isErr()) {
532
- logger.error("Failed to add transactions", {
532
+ console.error("Failed to add transactions", {
533
533
  peerId: peer.id,
534
534
  peerRole: peer.role,
535
535
  id: msg.id,
536
536
  err: result.error,
537
537
  });
538
- entry.markErrored(peer.id, result.error);
538
+ coValue.markErrored(peer.id, result.error);
539
539
  continue;
540
540
  }
541
541
 
@@ -555,7 +555,7 @@ export class SyncManager {
555
555
  isCorrection: true,
556
556
  ...coValue.knownState(),
557
557
  });
558
- peer.toldKnownState.add(msg.id);
558
+ peer.trackToldKnownState(msg.id);
559
559
  } else {
560
560
  /**
561
561
  * We are sending a known state message to the peer to acknowledge the
@@ -568,15 +568,50 @@ export class SyncManager {
568
568
  action: "known",
569
569
  ...coValue.knownState(),
570
570
  });
571
- peer.toldKnownState.add(msg.id);
571
+ peer.trackToldKnownState(msg.id);
572
572
  }
573
573
 
574
- /**
575
- * We do send a correction/ack message before syncing to give an immediate
576
- * response to the peers that are waiting for confirmation that a coValue is
577
- * fully synced
578
- */
579
- this.requestCoValueSync(coValue);
574
+ const sourcePeer = peer;
575
+ const syncedPeers = [];
576
+
577
+ for (const peer of this.peersInPriorityOrder()) {
578
+ /**
579
+ * We sync the content against the source peer if it is a client or server peers
580
+ * to upload any content that is available on the current node and not on the source peer.
581
+ *
582
+ * We don't need to do this with storage peers because we don't get updates from those peers,
583
+ * only load and store content.
584
+ */
585
+ if (peer.id === sourcePeer.id && sourcePeer.role === "storage") continue;
586
+ if (peer.closed) continue;
587
+ if (coValue.isErroredInPeer(peer.id)) continue;
588
+
589
+ // We directly forward the new content to peers that have an active subscription
590
+ if (peer.optimisticKnownStates.has(coValue.id)) {
591
+ this.sendNewContentIncludingDependencies(coValue.id, peer);
592
+ syncedPeers.push(peer);
593
+ } else if (
594
+ peer.isServerOrStoragePeer() &&
595
+ !peer.loadRequestSent.has(coValue.id)
596
+ ) {
597
+ const state = coValue.getStateForPeer(peer.id)?.type;
598
+
599
+ // Check if there is a inflight load operation and we
600
+ // are waiting for other peers to send the load request
601
+ if (state === "unknown" || state === undefined) {
602
+ this.trySendToPeer(peer, {
603
+ action: "load",
604
+ ...coValue.knownState(),
605
+ });
606
+ peer.trackLoadRequestSent(coValue.id);
607
+ syncedPeers.push(peer);
608
+ }
609
+ }
610
+ }
611
+
612
+ for (const peer of syncedPeers) {
613
+ this.syncState.triggerUpdate(peer.id, coValue.id);
614
+ }
580
615
  }
581
616
 
582
617
  handleCorrection(msg: KnownStateMessage, peer: PeerState) {
@@ -609,11 +644,9 @@ export class SyncManager {
609
644
  }
610
645
 
611
646
  async syncCoValue(coValue: CoValueCore) {
612
- const entry = this.local.coValuesStore.get(coValue.id);
613
-
614
647
  for (const peer of this.peersInPriorityOrder()) {
615
648
  if (peer.closed) continue;
616
- if (entry.isErroredInPeer(peer.id)) continue;
649
+ if (coValue.isErroredInPeer(peer.id)) continue;
617
650
 
618
651
  // Only subscribed CoValues are synced to clients
619
652
  if (
@@ -669,11 +702,11 @@ export class SyncManager {
669
702
  }
670
703
 
671
704
  async waitForAllCoValuesSync(timeout = 60_000) {
672
- const coValues = this.local.coValuesStore.getValues();
705
+ const coValues = this.local.allCoValues();
673
706
  const validCoValues = Array.from(coValues).filter(
674
707
  (coValue) =>
675
- coValue.highLevelState === "available" ||
676
- coValue.highLevelState === "loading",
708
+ coValue.loadingState === "available" ||
709
+ coValue.loadingState === "loading",
677
710
  );
678
711
 
679
712
  return Promise.all(
@@ -2,9 +2,11 @@ import { beforeEach, expect, test } from "vitest";
2
2
  import { expectList } from "../coValue.js";
3
3
  import { WasmCrypto } from "../crypto/WasmCrypto.js";
4
4
  import { LocalNode } from "../localNode.js";
5
+ import { expectGroup } from "../typeUtils/expectGroup.js";
5
6
  import {
6
7
  loadCoValueOrFail,
7
- randomAnonymousAccountAndSessionID,
8
+ nodeWithRandomAgentAndSessionID,
9
+ randomAgentAndSessionID,
8
10
  setupTestNode,
9
11
  waitFor,
10
12
  } from "./testUtils.js";
@@ -16,7 +18,7 @@ beforeEach(async () => {
16
18
  });
17
19
 
18
20
  test("Empty CoList works", () => {
19
- const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
21
+ const node = nodeWithRandomAgentAndSessionID();
20
22
 
21
23
  const coValue = node.createCoValue({
22
24
  type: "colist",
@@ -32,7 +34,7 @@ test("Empty CoList works", () => {
32
34
  });
33
35
 
34
36
  test("Can append, prepend, delete and replace items in CoList", () => {
35
- const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
37
+ const node = nodeWithRandomAgentAndSessionID();
36
38
 
37
39
  const coValue = node.createCoValue({
38
40
  type: "colist",
@@ -63,7 +65,7 @@ test("Can append, prepend, delete and replace items in CoList", () => {
63
65
  });
64
66
 
65
67
  test("Push is equivalent to append after last item", () => {
66
- const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
68
+ const node = nodeWithRandomAgentAndSessionID();
67
69
 
68
70
  const coValue = node.createCoValue({
69
71
  type: "colist",
@@ -85,7 +87,7 @@ test("Push is equivalent to append after last item", () => {
85
87
  });
86
88
 
87
89
  test("appendItems add an array of items at the end of the list", () => {
88
- const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
90
+ const node = nodeWithRandomAgentAndSessionID();
89
91
 
90
92
  const coValue = node.createCoValue({
91
93
  type: "colist",
@@ -105,7 +107,7 @@ test("appendItems add an array of items at the end of the list", () => {
105
107
  });
106
108
 
107
109
  test("appendItems at index", () => {
108
- const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
110
+ const node = nodeWithRandomAgentAndSessionID();
109
111
 
110
112
  const coValue = node.createCoValue({
111
113
  type: "colist",
@@ -135,7 +137,7 @@ test("appendItems at index", () => {
135
137
  });
136
138
 
137
139
  test("appendItems at index", () => {
138
- const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
140
+ const node = nodeWithRandomAgentAndSessionID();
139
141
 
140
142
  const coValue = node.createCoValue({
141
143
  type: "colist",
@@ -157,7 +159,7 @@ test("appendItems at index", () => {
157
159
  });
158
160
 
159
161
  test("appendItems with negative index", () => {
160
- const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
162
+ const node = nodeWithRandomAgentAndSessionID();
161
163
 
162
164
  const coValue = node.createCoValue({
163
165
  type: "colist",
@@ -175,7 +177,7 @@ test("appendItems with negative index", () => {
175
177
  });
176
178
 
177
179
  test("Can push into empty list", () => {
178
- const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
180
+ const node = nodeWithRandomAgentAndSessionID();
179
181
 
180
182
  const coValue = node.createCoValue({
181
183
  type: "colist",
@@ -193,7 +195,7 @@ test("Can push into empty list", () => {
193
195
  });
194
196
 
195
197
  test("init the list correctly", () => {
196
- const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
198
+ const node = nodeWithRandomAgentAndSessionID();
197
199
 
198
200
  const group = node.createGroup();
199
201
 
@@ -213,7 +215,7 @@ test("init the list correctly", () => {
213
215
  });
214
216
 
215
217
  test("Items prepended to start appear with latest first", () => {
216
- const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
218
+ const node = nodeWithRandomAgentAndSessionID();
217
219
 
218
220
  const coValue = node.createCoValue({
219
221
  type: "colist",
@@ -232,7 +234,7 @@ test("Items prepended to start appear with latest first", () => {
232
234
  });
233
235
 
234
236
  test("mixing prepend and append", () => {
235
- const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
237
+ const node = nodeWithRandomAgentAndSessionID();
236
238
 
237
239
  const coValue = node.createCoValue({
238
240
  type: "colist",
@@ -251,7 +253,7 @@ test("mixing prepend and append", () => {
251
253
  });
252
254
 
253
255
  test("Items appended to start", () => {
254
- const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
256
+ const node = nodeWithRandomAgentAndSessionID();
255
257
 
256
258
  const coValue = node.createCoValue({
257
259
  type: "colist",
@@ -277,7 +279,9 @@ test("syncing appends with an older timestamp", async () => {
277
279
  });
278
280
  const otherClient = setupTestNode({});
279
281
 
280
- const otherClientConnection = otherClient.connectToSyncServer();
282
+ const otherClientConnection = otherClient.connectToSyncServer({
283
+ ourName: "otherClient",
284
+ });
281
285
 
282
286
  const coValue = client.node.createCoValue({
283
287
  type: "colist",
@@ -309,7 +313,9 @@ test("syncing appends with an older timestamp", async () => {
309
313
 
310
314
  list.append(6, undefined, "trusting");
311
315
 
312
- otherClient.connectToSyncServer();
316
+ otherClient.connectToSyncServer({
317
+ ourName: "otherClient",
318
+ });
313
319
 
314
320
  await waitFor(() => {
315
321
  expect(list.toJSON()).toEqual([1, 2, 4, 6, 3, 5]);