cojson 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 (218) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +12 -0
  3. package/dist/IncomingMessagesQueue.d.ts +27 -0
  4. package/dist/IncomingMessagesQueue.d.ts.map +1 -0
  5. package/dist/IncomingMessagesQueue.js +114 -0
  6. package/dist/IncomingMessagesQueue.js.map +1 -0
  7. package/dist/PeerState.d.ts +2 -10
  8. package/dist/PeerState.d.ts.map +1 -1
  9. package/dist/PeerState.js +9 -90
  10. package/dist/PeerState.js.map +1 -1
  11. package/dist/PriorityBasedMessageQueue.d.ts +2 -1
  12. package/dist/PriorityBasedMessageQueue.d.ts.map +1 -1
  13. package/dist/PriorityBasedMessageQueue.js +9 -6
  14. package/dist/PriorityBasedMessageQueue.js.map +1 -1
  15. package/dist/SyncStateManager.d.ts +1 -0
  16. package/dist/SyncStateManager.d.ts.map +1 -1
  17. package/dist/SyncStateManager.js +1 -1
  18. package/dist/SyncStateManager.js.map +1 -1
  19. package/dist/coValue.d.ts +1 -1
  20. package/dist/coValueCore/coValueCore.d.ts +9 -17
  21. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  22. package/dist/coValueCore/coValueCore.js +75 -50
  23. package/dist/coValueCore/coValueCore.js.map +1 -1
  24. package/dist/coValueCore/verifiedState.d.ts +10 -3
  25. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  26. package/dist/coValueCore/verifiedState.js +73 -14
  27. package/dist/coValueCore/verifiedState.js.map +1 -1
  28. package/dist/coValues/coMap.d.ts +3 -3
  29. package/dist/coValues/coStream.d.ts +2 -2
  30. package/dist/coValues/group.d.ts +1 -1
  31. package/dist/coValues/group.d.ts.map +1 -1
  32. package/dist/coValues/group.js +2 -4
  33. package/dist/coValues/group.js.map +1 -1
  34. package/dist/config.d.ts +19 -0
  35. package/dist/config.d.ts.map +1 -0
  36. package/dist/config.js +23 -0
  37. package/dist/config.js.map +1 -0
  38. package/dist/crypto/WasmCrypto.d.ts.map +1 -1
  39. package/dist/crypto/WasmCrypto.js +2 -1
  40. package/dist/crypto/WasmCrypto.js.map +1 -1
  41. package/dist/exports.d.ts +18 -7
  42. package/dist/exports.d.ts.map +1 -1
  43. package/dist/exports.js +11 -8
  44. package/dist/exports.js.map +1 -1
  45. package/dist/localNode.d.ts +8 -2
  46. package/dist/localNode.d.ts.map +1 -1
  47. package/dist/localNode.js +19 -12
  48. package/dist/localNode.js.map +1 -1
  49. package/dist/storage/StoreQueue.d.ts +15 -0
  50. package/dist/storage/StoreQueue.d.ts.map +1 -0
  51. package/dist/storage/StoreQueue.js +35 -0
  52. package/dist/storage/StoreQueue.js.map +1 -0
  53. package/dist/storage/index.d.ts +6 -0
  54. package/dist/storage/index.d.ts.map +1 -0
  55. package/dist/storage/index.js +6 -0
  56. package/dist/storage/index.js.map +1 -0
  57. package/dist/storage/knownState.d.ts +18 -0
  58. package/dist/storage/knownState.d.ts.map +1 -0
  59. package/dist/storage/knownState.js +63 -0
  60. package/dist/storage/knownState.js.map +1 -0
  61. package/dist/storage/sqlite/client.d.ts +37 -0
  62. package/dist/storage/sqlite/client.d.ts.map +1 -0
  63. package/dist/storage/sqlite/client.js +89 -0
  64. package/dist/storage/sqlite/client.js.map +1 -0
  65. package/dist/storage/sqlite/index.d.ts +5 -0
  66. package/dist/storage/sqlite/index.d.ts.map +1 -0
  67. package/dist/storage/sqlite/index.js +13 -0
  68. package/dist/storage/sqlite/index.js.map +1 -0
  69. package/dist/storage/sqlite/sqliteMigrations.d.ts +3 -0
  70. package/dist/storage/sqlite/sqliteMigrations.d.ts.map +1 -0
  71. package/dist/storage/sqlite/sqliteMigrations.js +44 -0
  72. package/dist/storage/sqlite/sqliteMigrations.js.map +1 -0
  73. package/dist/storage/sqlite/types.d.ts +8 -0
  74. package/dist/storage/sqlite/types.d.ts.map +1 -0
  75. package/dist/storage/sqlite/types.js +2 -0
  76. package/dist/storage/sqlite/types.js.map +1 -0
  77. package/dist/storage/sqliteAsync/client.d.ts +37 -0
  78. package/dist/storage/sqliteAsync/client.d.ts.map +1 -0
  79. package/dist/storage/sqliteAsync/client.js +88 -0
  80. package/dist/storage/sqliteAsync/client.js.map +1 -0
  81. package/dist/storage/sqliteAsync/index.d.ts +6 -0
  82. package/dist/storage/sqliteAsync/index.d.ts.map +1 -0
  83. package/dist/storage/sqliteAsync/index.js +15 -0
  84. package/dist/storage/sqliteAsync/index.js.map +1 -0
  85. package/dist/storage/sqliteAsync/types.d.ts +9 -0
  86. package/dist/storage/sqliteAsync/types.d.ts.map +1 -0
  87. package/dist/storage/sqliteAsync/types.js +2 -0
  88. package/dist/storage/sqliteAsync/types.js.map +1 -0
  89. package/dist/storage/storageAsync.d.ts +22 -0
  90. package/dist/storage/storageAsync.d.ts.map +1 -0
  91. package/dist/storage/storageAsync.js +214 -0
  92. package/dist/storage/storageAsync.js.map +1 -0
  93. package/dist/storage/storageSync.d.ts +21 -0
  94. package/dist/storage/storageSync.d.ts.map +1 -0
  95. package/dist/storage/storageSync.js +206 -0
  96. package/dist/storage/storageSync.js.map +1 -0
  97. package/dist/storage/syncUtils.d.ts +13 -0
  98. package/dist/storage/syncUtils.d.ts.map +1 -0
  99. package/dist/storage/syncUtils.js +25 -0
  100. package/dist/storage/syncUtils.js.map +1 -0
  101. package/dist/storage/types.d.ts +82 -0
  102. package/dist/storage/types.d.ts.map +1 -0
  103. package/dist/storage/types.js +2 -0
  104. package/dist/storage/types.js.map +1 -0
  105. package/dist/streamUtils.d.ts +13 -9
  106. package/dist/streamUtils.d.ts.map +1 -1
  107. package/dist/streamUtils.js +46 -13
  108. package/dist/streamUtils.js.map +1 -1
  109. package/dist/sync.d.ts +22 -14
  110. package/dist/sync.d.ts.map +1 -1
  111. package/dist/sync.js +143 -125
  112. package/dist/sync.js.map +1 -1
  113. package/dist/tests/IncomingMessagesQueue.test.d.ts +2 -0
  114. package/dist/tests/IncomingMessagesQueue.test.d.ts.map +1 -0
  115. package/dist/tests/IncomingMessagesQueue.test.js +437 -0
  116. package/dist/tests/IncomingMessagesQueue.test.js.map +1 -0
  117. package/dist/tests/PeerState.test.js +6 -94
  118. package/dist/tests/PeerState.test.js.map +1 -1
  119. package/dist/tests/PriorityBasedMessageQueue.test.js +14 -14
  120. package/dist/tests/PriorityBasedMessageQueue.test.js.map +1 -1
  121. package/dist/tests/StoreQueue.test.d.ts +2 -0
  122. package/dist/tests/StoreQueue.test.d.ts.map +1 -0
  123. package/dist/tests/StoreQueue.test.js +208 -0
  124. package/dist/tests/StoreQueue.test.js.map +1 -0
  125. package/dist/tests/SyncStateManager.test.js +3 -1
  126. package/dist/tests/SyncStateManager.test.js.map +1 -1
  127. package/dist/tests/account.test.js +9 -9
  128. package/dist/tests/account.test.js.map +1 -1
  129. package/dist/tests/coStream.test.js +1 -1
  130. package/dist/tests/coStream.test.js.map +1 -1
  131. package/dist/tests/coValueCore.test.js +208 -1
  132. package/dist/tests/coValueCore.test.js.map +1 -1
  133. package/dist/tests/coValueCoreLoadingState.test.js +2 -2
  134. package/dist/tests/coValueCoreLoadingState.test.js.map +1 -1
  135. package/dist/tests/group.addMember.test.js.map +1 -1
  136. package/dist/tests/group.removeMember.test.js +1 -1
  137. package/dist/tests/group.removeMember.test.js.map +1 -1
  138. package/dist/tests/messagesTestUtils.js +1 -1
  139. package/dist/tests/messagesTestUtils.js.map +1 -1
  140. package/dist/tests/sync.auth.test.js +23 -15
  141. package/dist/tests/sync.auth.test.js.map +1 -1
  142. package/dist/tests/sync.invite.test.js +10 -16
  143. package/dist/tests/sync.invite.test.js.map +1 -1
  144. package/dist/tests/sync.load.test.js +52 -50
  145. package/dist/tests/sync.load.test.js.map +1 -1
  146. package/dist/tests/sync.mesh.test.js +173 -56
  147. package/dist/tests/sync.mesh.test.js.map +1 -1
  148. package/dist/tests/sync.peerReconciliation.test.js +42 -32
  149. package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
  150. package/dist/tests/sync.storage.test.js +162 -62
  151. package/dist/tests/sync.storage.test.js.map +1 -1
  152. package/dist/tests/sync.storageAsync.test.d.ts +2 -0
  153. package/dist/tests/sync.storageAsync.test.d.ts.map +1 -0
  154. package/dist/tests/sync.storageAsync.test.js +361 -0
  155. package/dist/tests/sync.storageAsync.test.js.map +1 -0
  156. package/dist/tests/sync.test.js +16 -21
  157. package/dist/tests/sync.test.js.map +1 -1
  158. package/dist/tests/sync.upload.test.js +28 -25
  159. package/dist/tests/sync.upload.test.js.map +1 -1
  160. package/dist/tests/testStorage.d.ts +12 -0
  161. package/dist/tests/testStorage.d.ts.map +1 -0
  162. package/dist/tests/testStorage.js +151 -0
  163. package/dist/tests/testStorage.js.map +1 -0
  164. package/dist/tests/testUtils.d.ts +20 -15
  165. package/dist/tests/testUtils.d.ts.map +1 -1
  166. package/dist/tests/testUtils.js +79 -45
  167. package/dist/tests/testUtils.js.map +1 -1
  168. package/package.json +2 -2
  169. package/src/IncomingMessagesQueue.ts +142 -0
  170. package/src/PeerState.ts +11 -110
  171. package/src/PriorityBasedMessageQueue.ts +13 -5
  172. package/src/SyncStateManager.ts +1 -1
  173. package/src/coValueCore/coValueCore.ts +100 -66
  174. package/src/coValueCore/verifiedState.ts +91 -21
  175. package/src/coValues/group.ts +2 -4
  176. package/src/config.ts +26 -0
  177. package/src/crypto/WasmCrypto.ts +3 -1
  178. package/src/exports.ts +20 -27
  179. package/src/localNode.ts +27 -12
  180. package/src/storage/StoreQueue.ts +56 -0
  181. package/src/storage/index.ts +5 -0
  182. package/src/storage/knownState.ts +88 -0
  183. package/src/storage/sqlite/client.ts +180 -0
  184. package/src/storage/sqlite/index.ts +19 -0
  185. package/src/storage/sqlite/sqliteMigrations.ts +44 -0
  186. package/src/storage/sqlite/types.ts +7 -0
  187. package/src/storage/sqliteAsync/client.ts +179 -0
  188. package/src/storage/sqliteAsync/index.ts +25 -0
  189. package/src/storage/sqliteAsync/types.ts +8 -0
  190. package/src/storage/storageAsync.ts +367 -0
  191. package/src/storage/storageSync.ts +343 -0
  192. package/src/storage/syncUtils.ts +50 -0
  193. package/src/storage/types.ts +162 -0
  194. package/src/streamUtils.ts +61 -19
  195. package/src/sync.ts +191 -160
  196. package/src/tests/IncomingMessagesQueue.test.ts +626 -0
  197. package/src/tests/PeerState.test.ts +6 -118
  198. package/src/tests/PriorityBasedMessageQueue.test.ts +18 -14
  199. package/src/tests/StoreQueue.test.ts +283 -0
  200. package/src/tests/SyncStateManager.test.ts +4 -1
  201. package/src/tests/account.test.ts +11 -12
  202. package/src/tests/coStream.test.ts +1 -3
  203. package/src/tests/coValueCore.test.ts +270 -1
  204. package/src/tests/coValueCoreLoadingState.test.ts +2 -2
  205. package/src/tests/group.addMember.test.ts +1 -0
  206. package/src/tests/group.removeMember.test.ts +2 -8
  207. package/src/tests/messagesTestUtils.ts +2 -2
  208. package/src/tests/sync.auth.test.ts +24 -14
  209. package/src/tests/sync.invite.test.ts +11 -17
  210. package/src/tests/sync.load.test.ts +53 -49
  211. package/src/tests/sync.mesh.test.ts +198 -56
  212. package/src/tests/sync.peerReconciliation.test.ts +44 -34
  213. package/src/tests/sync.storage.test.ts +231 -64
  214. package/src/tests/sync.storageAsync.test.ts +486 -0
  215. package/src/tests/sync.test.ts +17 -23
  216. package/src/tests/sync.upload.test.ts +29 -24
  217. package/src/tests/testStorage.ts +216 -0
  218. package/src/tests/testUtils.ts +89 -54
package/src/sync.ts CHANGED
@@ -1,10 +1,8 @@
1
1
  import { Histogram, ValueType, metrics } from "@opentelemetry/api";
2
+ import { IncomingMessagesQueue } from "./IncomingMessagesQueue.js";
2
3
  import { PeerState } from "./PeerState.js";
3
4
  import { SyncStateManager } from "./SyncStateManager.js";
4
- import {
5
- AvailableCoValueCore,
6
- CoValueCore,
7
- } from "./coValueCore/coValueCore.js";
5
+ import { CoValueCore } from "./coValueCore/coValueCore.js";
8
6
  import { getDependedOnCoValuesFromRawData } from "./coValueCore/utils.js";
9
7
  import { CoValueHeader, Transaction } from "./coValueCore/verifiedState.js";
10
8
  import { Signature } from "./crypto/crypto.js";
@@ -53,6 +51,9 @@ export type NewContentMessage = {
53
51
  new: {
54
52
  [sessionID: SessionID]: SessionNewContent;
55
53
  };
54
+ expectContentUntil?: {
55
+ [sessionID: SessionID]: number;
56
+ };
56
57
  };
57
58
 
58
59
  export type SessionNewContent = {
@@ -69,23 +70,24 @@ export type PeerID = string;
69
70
 
70
71
  export type DisconnectedError = "Disconnected";
71
72
 
72
- export type PingTimeoutError = "PingTimeout";
73
+ export interface IncomingPeerChannel {
74
+ close: () => void;
75
+ onMessage: (callback: (msg: SyncMessage | DisconnectedError) => void) => void;
76
+ onClose: (callback: () => void) => void;
77
+ }
73
78
 
74
- export type IncomingSyncStream = AsyncIterable<
75
- SyncMessage | DisconnectedError | PingTimeoutError
76
- >;
77
- export type OutgoingSyncQueue = {
78
- push: (msg: SyncMessage) => Promise<unknown>;
79
+ export interface OutgoingPeerChannel {
80
+ push: (msg: SyncMessage | DisconnectedError) => void;
79
81
  close: () => void;
80
- };
82
+ onClose: (callback: () => void) => void;
83
+ }
81
84
 
82
85
  export interface Peer {
83
86
  id: PeerID;
84
- incoming: IncomingSyncStream;
85
- outgoing: OutgoingSyncQueue;
86
- role: "server" | "client" | "storage";
87
+ incoming: IncomingPeerChannel;
88
+ outgoing: OutgoingPeerChannel;
89
+ role: "server" | "client";
87
90
  priority?: number;
88
- crashOnClose: boolean;
89
91
  deletePeerStateOnClose?: boolean;
90
92
  }
91
93
 
@@ -153,18 +155,10 @@ export class SyncManager {
153
155
  return Object.values(this.peers);
154
156
  }
155
157
 
156
- getServerAndStoragePeers(excludePeerId?: PeerID): PeerState[] {
157
- return this.peersInPriorityOrder().filter(
158
+ getServerPeers(excludePeerId?: PeerID): PeerState[] {
159
+ return this.getPeers().filter(
158
160
  (peer) =>
159
- peer.isServerOrStoragePeer() &&
160
- peer.id !== excludePeerId &&
161
- !peer.closed,
162
- );
163
- }
164
-
165
- hasStoragePeers(): boolean {
166
- return this.getPeers().some(
167
- (peer) => peer.role === "storage" && !peer.closed,
161
+ peer.role === "server" && peer.id !== excludePeerId && !peer.closed,
168
162
  );
169
163
  }
170
164
 
@@ -199,7 +193,7 @@ export class SyncManager {
199
193
  case "content":
200
194
  return this.handleNewContent(msg, peer);
201
195
  case "done":
202
- return this.handleUnsubscribe(msg);
196
+ return;
203
197
  default:
204
198
  throw new Error(
205
199
  `Unknown message type ${(msg as { action: "string" }).action}`,
@@ -241,7 +235,7 @@ export class SyncManager {
241
235
  } else if (!peer.toldKnownState.has(id)) {
242
236
  this.trySendToPeer(peer, {
243
237
  action: "known",
244
- ...coValue.knownState(),
238
+ ...coValue.knownStateWithStreaming(),
245
239
  });
246
240
  }
247
241
 
@@ -311,6 +305,19 @@ export class SyncManager {
311
305
  }
312
306
  }
313
307
 
308
+ messagesQueue = new IncomingMessagesQueue();
309
+ pushMessage(incoming: SyncMessage, peer: PeerState) {
310
+ this.messagesQueue.push(incoming, peer);
311
+
312
+ if (this.messagesQueue.processing) {
313
+ return;
314
+ }
315
+
316
+ this.messagesQueue.processQueue((msg, peer) => {
317
+ this.handleSyncMessage(msg, peer);
318
+ });
319
+ }
320
+
314
321
  addPeer(peer: Peer) {
315
322
  const prevPeer = this.peers[peer.id];
316
323
 
@@ -329,45 +336,27 @@ export class SyncManager {
329
336
  },
330
337
  );
331
338
 
332
- if (peerState.isServerOrStoragePeer()) {
339
+ if (peerState.role === "server") {
333
340
  void this.startPeerReconciliation(peerState);
334
341
  }
335
342
 
336
- peerState
337
- .processIncomingMessages((msg) => {
338
- this.handleSyncMessage(msg, peerState);
339
- })
340
- .then(() => {
341
- if (peer.crashOnClose) {
342
- logger.error("Unexepcted close from peer", {
343
- peerId: peer.id,
344
- peerRole: peer.role,
345
- });
346
- this.local.crashed = new Error("Unexpected close from peer");
347
- throw new Error("Unexpected close from peer");
348
- }
349
- })
350
- .catch((e) => {
351
- logger.error("Error processing messages from peer", {
352
- err: e,
353
- peerId: peer.id,
354
- peerRole: peer.role,
355
- });
356
-
357
- if (peer.crashOnClose) {
358
- this.local.crashed = e;
359
- throw new Error(e);
360
- }
361
- })
362
- .finally(() => {
343
+ peerState.incoming.onMessage((msg) => {
344
+ if (msg === "Disconnected") {
363
345
  peerState.gracefulShutdown();
364
- unsubscribeFromKnownStatesUpdates();
365
- this.peersCounter.add(-1, { role: peer.role });
346
+ return;
347
+ }
366
348
 
367
- if (peer.deletePeerStateOnClose && this.peers[peer.id] === peerState) {
368
- delete this.peers[peer.id];
369
- }
370
- });
349
+ this.pushMessage(msg, peerState);
350
+ });
351
+
352
+ peerState.addCloseListener(() => {
353
+ unsubscribeFromKnownStatesUpdates();
354
+ this.peersCounter.add(-1, { role: peer.role });
355
+
356
+ if (peer.deletePeerStateOnClose && this.peers[peer.id] === peerState) {
357
+ delete this.peers[peer.id];
358
+ }
359
+ });
371
360
  }
372
361
 
373
362
  trySendToPeer(peer: PeerState, msg: SyncMessage) {
@@ -397,12 +386,16 @@ export class SyncManager {
397
386
  return;
398
387
  }
399
388
 
400
- const eligiblePeers = this.getServerAndStoragePeers(peer.id);
389
+ const peers = this.getServerPeers(peer.id);
390
+
391
+ coValue.load(peers);
392
+
393
+ const handleLoadResult = () => {
394
+ if (coValue.isAvailable()) {
395
+ this.sendNewContentIncludingDependencies(msg.id, peer);
396
+ return;
397
+ }
401
398
 
402
- if (eligiblePeers.length === 0) {
403
- // We don't have any eligible peers to load the coValue from
404
- // so we send a known state back to the sender to let it know
405
- // that the coValue is unavailable
406
399
  peer.trackToldKnownState(msg.id);
407
400
  this.trySendToPeer(peer, {
408
401
  action: "known",
@@ -410,41 +403,15 @@ export class SyncManager {
410
403
  header: false,
411
404
  sessions: {},
412
405
  });
406
+ };
413
407
 
414
- return;
408
+ if (peers.length > 0 || this.local.storage) {
409
+ coValue.waitForAvailableOrUnavailable().then(handleLoadResult);
410
+ } else {
411
+ handleLoadResult();
415
412
  }
416
-
417
- coValue.loadFromPeers(eligiblePeers).catch((e) => {
418
- logger.error("Error loading coValue in handleLoad", { err: e });
419
- });
420
-
421
- // We need to return from handleLoad immediately and wait for the CoValue to be loaded
422
- // in a new task, otherwise we might block further incoming content messages that would
423
- // resolve the CoValue as available. This can happen when we receive fresh
424
- // content from a client, but we are a server with our own upstream server(s)
425
- coValue
426
- .waitForAvailableOrUnavailable()
427
- .then((value) => {
428
- if (!value.isAvailable()) {
429
- peer.trackToldKnownState(msg.id);
430
- this.trySendToPeer(peer, {
431
- action: "known",
432
- id: msg.id,
433
- header: false,
434
- sessions: {},
435
- });
436
-
437
- return;
438
- }
439
-
440
- this.sendNewContentIncludingDependencies(msg.id, peer);
441
- })
442
- .catch((e) => {
443
- logger.error("Error loading coValue in handleLoad loading state", {
444
- err: e,
445
- });
446
- });
447
413
  }
414
+
448
415
  handleKnownState(msg: KnownStateMessage, peer: PeerState) {
449
416
  const coValue = this.local.getCoValue(msg.id);
450
417
 
@@ -476,18 +443,28 @@ export class SyncManager {
476
443
  }
477
444
  }
478
445
 
479
- handleNewContent(msg: NewContentMessage, peer: PeerState) {
446
+ handleNewContent(msg: NewContentMessage, from: PeerState | "storage") {
480
447
  const coValue = this.local.getCoValue(msg.id);
448
+ const peer = from === "storage" ? undefined : from;
481
449
 
482
- if (!coValue.verified) {
450
+ if (!coValue.hasVerifiedContent()) {
483
451
  if (!msg.header) {
484
- this.trySendToPeer(peer, {
485
- action: "known",
486
- isCorrection: true,
487
- id: msg.id,
488
- header: false,
489
- sessions: {},
490
- });
452
+ if (peer) {
453
+ this.trySendToPeer(peer, {
454
+ action: "known",
455
+ isCorrection: true,
456
+ id: msg.id,
457
+ header: false,
458
+ sessions: {},
459
+ });
460
+ } else {
461
+ logger.error(
462
+ "Received new content with no header on a missing CoValue",
463
+ {
464
+ id: msg.id,
465
+ },
466
+ );
467
+ }
491
468
  return;
492
469
  }
493
470
 
@@ -504,28 +481,40 @@ export class SyncManager {
504
481
  )) {
505
482
  const dependencyCoValue = this.local.getCoValue(dependency);
506
483
 
507
- if (!dependencyCoValue.verified) {
484
+ if (!dependencyCoValue.hasVerifiedContent()) {
508
485
  coValue.markMissingDependency(dependency);
509
486
 
510
- if (!dependencyCoValue.verified) {
511
- const peers = this.getServerAndStoragePeers();
512
-
513
- // if the peer that sent the content is a client, we add it to the list of peers
514
- // to also ask them for the dependency
515
- if (peer.role === "client") {
516
- peers.push(peer);
517
- }
487
+ const peers = this.getServerPeers();
518
488
 
519
- dependencyCoValue.loadFromPeers(peers);
489
+ // if the peer that sent the content is a client, we add it to the list of peers
490
+ // to also ask them for the dependency
491
+ if (peer?.role === "client") {
492
+ peers.push(peer);
520
493
  }
494
+
495
+ dependencyCoValue.load(peers);
496
+ } else if (!dependencyCoValue.isAvailable()) {
497
+ coValue.markMissingDependency(dependency);
521
498
  }
522
499
  }
523
500
 
524
- peer.updateHeader(msg.id, true);
525
- coValue.provideHeader(msg.header, peer.id);
501
+ peer?.updateHeader(msg.id, true);
502
+ coValue.provideHeader(
503
+ msg.header,
504
+ peer?.id ?? "storage",
505
+ msg.expectContentUntil,
506
+ );
507
+
508
+ if (msg.expectContentUntil) {
509
+ peer?.combineWith(msg.id, {
510
+ id: msg.id,
511
+ header: true,
512
+ sessions: msg.expectContentUntil,
513
+ });
514
+ }
526
515
  }
527
516
 
528
- if (!coValue.verified) {
517
+ if (!coValue.hasVerifiedContent()) {
529
518
  throw new Error(
530
519
  "Unreachable: CoValue should always have a verified state at this point",
531
520
  );
@@ -567,15 +556,15 @@ export class SyncManager {
567
556
  // This covers the case where we are getting a new session on an already loaded coValue
568
557
  // where we need to load the account to get their public key
569
558
  if (!coValue.missingDependencies.has(accountId)) {
570
- const peers = this.getServerAndStoragePeers();
559
+ const peers = this.getServerPeers();
571
560
 
572
- if (peer.role === "client") {
561
+ if (peer?.role === "client") {
573
562
  // if the peer that sent the content is a client, we add it to the list of peers
574
563
  // to also ask them for the dependency
575
564
  peers.push(peer);
576
565
  }
577
566
 
578
- account.loadFromPeers(peers);
567
+ account.load(peers);
579
568
  }
580
569
 
581
570
  // We need to wait for the account to be available before we can verify the transaction
@@ -598,7 +587,7 @@ export class SyncManager {
598
587
  },
599
588
  priority: msg.priority,
600
589
  },
601
- peer,
590
+ from,
602
591
  );
603
592
  });
604
593
  continue;
@@ -610,23 +599,30 @@ export class SyncManager {
610
599
  newTransactions,
611
600
  undefined,
612
601
  newContentForSession.lastSignature,
613
- "immediate", // TODO: can we change this to deferred?
602
+ "immediate",
614
603
  );
615
604
 
616
605
  if (result.isErr()) {
617
- console.error("Failed to add transactions", {
618
- peerId: peer.id,
619
- peerRole: peer.role,
620
- id: msg.id,
621
- err: result.error,
622
- });
623
- coValue.markErrored(peer.id, result.error);
606
+ if (peer) {
607
+ logger.error("Failed to add transactions", {
608
+ peerId: peer.id,
609
+ peerRole: peer.role,
610
+ id: msg.id,
611
+ err: result.error,
612
+ });
613
+ coValue.markErrored(peer.id, result.error);
614
+ } else {
615
+ logger.error("Failed to add transactions from storage", {
616
+ id: msg.id,
617
+ err: result.error,
618
+ });
619
+ }
624
620
  continue;
625
621
  }
626
622
 
627
- this.recordTransactionsSize(newTransactions, peer.role);
623
+ this.recordTransactionsSize(newTransactions, peer?.role ?? "storage");
628
624
 
629
- peer.updateSessionCounter(
625
+ peer?.updateSessionCounter(
630
626
  msg.id,
631
627
  sessionID,
632
628
  newContentForSession.after +
@@ -635,13 +631,22 @@ export class SyncManager {
635
631
  }
636
632
 
637
633
  if (invalidStateAssumed) {
638
- this.trySendToPeer(peer, {
639
- action: "known",
640
- isCorrection: true,
641
- ...coValue.knownState(),
642
- });
643
- peer.trackToldKnownState(msg.id);
644
- } else {
634
+ if (peer) {
635
+ this.trySendToPeer(peer, {
636
+ action: "known",
637
+ isCorrection: true,
638
+ ...coValue.knownState(),
639
+ });
640
+ peer.trackToldKnownState(msg.id);
641
+ } else {
642
+ logger.error(
643
+ "Invalid state assumed when handling new content from storage",
644
+ {
645
+ id: msg.id,
646
+ },
647
+ );
648
+ }
649
+ } else if (peer) {
645
650
  /**
646
651
  * We are sending a known state message to the peer to acknowledge the
647
652
  * receipt of the new content.
@@ -656,18 +661,17 @@ export class SyncManager {
656
661
  peer.trackToldKnownState(msg.id);
657
662
  }
658
663
 
659
- const sourcePeer = peer;
660
664
  const syncedPeers = [];
661
665
 
666
+ if (from !== "storage") {
667
+ this.storeCoValue(coValue, [msg]);
668
+ }
669
+
662
670
  for (const peer of this.peersInPriorityOrder()) {
663
671
  /**
664
672
  * We sync the content against the source peer if it is a client or server peers
665
673
  * to upload any content that is available on the current node and not on the source peer.
666
- *
667
- * We don't need to do this with storage peers because we don't get updates from those peers,
668
- * only load and store content.
669
674
  */
670
- if (peer.id === sourcePeer.id && sourcePeer.role === "storage") continue;
671
675
  if (peer.closed) continue;
672
676
  if (coValue.isErroredInPeer(peer.id)) continue;
673
677
 
@@ -676,7 +680,7 @@ export class SyncManager {
676
680
  this.sendNewContentIncludingDependencies(coValue.id, peer);
677
681
  syncedPeers.push(peer);
678
682
  } else if (
679
- peer.isServerOrStoragePeer() &&
683
+ peer.role === "server" &&
680
684
  !peer.loadRequestSent.has(coValue.id)
681
685
  ) {
682
686
  const state = coValue.getStateForPeer(peer.id)?.type;
@@ -688,7 +692,7 @@ export class SyncManager {
688
692
  // before sending the new content
689
693
  this.trySendToPeer(peer, {
690
694
  action: "load",
691
- ...coValue.knownState(),
695
+ ...coValue.knownStateWithStreaming(),
692
696
  });
693
697
  peer.trackLoadRequestSent(coValue.id);
694
698
  syncedPeers.push(peer);
@@ -707,8 +711,6 @@ export class SyncManager {
707
711
  return this.sendNewContentIncludingDependencies(msg.id, peer);
708
712
  }
709
713
 
710
- handleUnsubscribe(_msg: DoneMessage) {}
711
-
712
714
  requestedSyncs = new Set<RawCoID>();
713
715
  requestCoValueSync(coValue: CoValueCore) {
714
716
  if (this.requestedSyncs.has(coValue.id)) {
@@ -724,9 +726,42 @@ export class SyncManager {
724
726
  this.requestedSyncs.add(coValue.id);
725
727
  }
726
728
 
729
+ storeCoValue(coValue: CoValueCore, data: NewContentMessage[] | undefined) {
730
+ const storage = this.local.storage;
731
+
732
+ if (!storage || !data) return;
733
+
734
+ // Try to store the content as-is for performance
735
+ // In case that some transactions are missing, a correction will be requested, but it's an edge case
736
+ storage.store(data, (correction) => {
737
+ if (!coValue.hasVerifiedContent()) return;
738
+
739
+ const newContentPieces = coValue.verified.newContentSince(correction);
740
+
741
+ if (!newContentPieces) return;
742
+
743
+ storage.store(newContentPieces, (response) => {
744
+ logger.error(
745
+ "Correction requested by storage after sending a correction content",
746
+ {
747
+ response,
748
+ knownState: coValue.knownState(),
749
+ },
750
+ );
751
+ });
752
+ });
753
+ }
754
+
727
755
  syncCoValue(coValue: CoValueCore) {
728
756
  this.requestedSyncs.delete(coValue.id);
729
757
 
758
+ if (this.local.storage && coValue.hasVerifiedContent()) {
759
+ const knownState = this.local.storage.getKnownState(coValue.id);
760
+ const newContentPieces = coValue.verified.newContentSince(knownState);
761
+
762
+ this.storeCoValue(coValue, newContentPieces);
763
+ }
764
+
730
765
  for (const peer of this.peersInPriorityOrder()) {
731
766
  if (peer.closed) continue;
732
767
  if (coValue.isErroredInPeer(peer.id)) continue;
@@ -791,21 +826,17 @@ export class SyncManager {
791
826
  });
792
827
  }
793
828
 
794
- waitForStorageSync(id: RawCoID, timeout = 30_000) {
795
- const peers = this.getPeers();
796
-
797
- return Promise.all(
798
- peers
799
- .filter((peer) => peer.role === "storage")
800
- .map((peer) => this.waitForSyncWithPeer(peer.id, id, timeout)),
801
- );
829
+ waitForStorageSync(id: RawCoID) {
830
+ return this.local.storage?.waitForSync(id, this.local.getCoValue(id));
802
831
  }
803
832
 
804
833
  waitForSync(id: RawCoID, timeout = 30_000) {
805
834
  const peers = this.getPeers();
806
835
 
807
836
  return Promise.all(
808
- peers.map((peer) => this.waitForSyncWithPeer(peer.id, id, timeout)),
837
+ peers
838
+ .map((peer) => this.waitForSyncWithPeer(peer.id, id, timeout))
839
+ .concat(this.waitForStorageSync(id)),
809
840
  );
810
841
  }
811
842