cojson 0.8.5 → 0.8.11

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 (49) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/native/PeerKnownStates.js +63 -0
  3. package/dist/native/PeerKnownStates.js.map +1 -0
  4. package/dist/native/PeerState.js +2 -1
  5. package/dist/native/PeerState.js.map +1 -1
  6. package/dist/native/coValueState.js +74 -0
  7. package/dist/native/coValueState.js.map +1 -0
  8. package/dist/native/exports.js +39 -0
  9. package/dist/native/exports.js.map +1 -0
  10. package/dist/native/index.native.js +2 -39
  11. package/dist/native/index.native.js.map +1 -1
  12. package/dist/native/localNode.js +28 -50
  13. package/dist/native/localNode.js.map +1 -1
  14. package/dist/native/storage/index.js +1 -1
  15. package/dist/native/storage/index.js.map +1 -1
  16. package/dist/native/sync.js +85 -105
  17. package/dist/native/sync.js.map +1 -1
  18. package/dist/web/PeerKnownStates.js +63 -0
  19. package/dist/web/PeerKnownStates.js.map +1 -0
  20. package/dist/web/PeerState.js +2 -1
  21. package/dist/web/PeerState.js.map +1 -1
  22. package/dist/web/coValueState.js +74 -0
  23. package/dist/web/coValueState.js.map +1 -0
  24. package/dist/web/exports.js +39 -0
  25. package/dist/web/exports.js.map +1 -0
  26. package/dist/web/index.web.js +2 -39
  27. package/dist/web/index.web.js.map +1 -1
  28. package/dist/web/localNode.js +28 -50
  29. package/dist/web/localNode.js.map +1 -1
  30. package/dist/web/storage/index.js +1 -1
  31. package/dist/web/storage/index.js.map +1 -1
  32. package/dist/web/sync.js +85 -105
  33. package/dist/web/sync.js.map +1 -1
  34. package/package.json +1 -1
  35. package/src/PeerKnownStates.ts +108 -0
  36. package/src/PeerState.ts +4 -2
  37. package/src/coValueState.ts +107 -0
  38. package/src/exports.ts +149 -0
  39. package/src/index.native.ts +2 -152
  40. package/src/index.web.ts +2 -152
  41. package/src/localNode.ts +42 -89
  42. package/src/storage/index.ts +1 -1
  43. package/src/sync.ts +90 -143
  44. package/src/tests/PeerKnownStates.test.ts +100 -0
  45. package/src/tests/PeerState.test.ts +0 -11
  46. package/src/tests/sync.test.ts +2 -2
  47. package/.turbo/turbo-build.log +0 -16
  48. package/.turbo/turbo-lint.log +0 -4
  49. package/.turbo/turbo-test.log +0 -1001
package/src/localNode.ts CHANGED
@@ -28,10 +28,11 @@ import {
28
28
  import { RawCoValue } from "./coValue.js";
29
29
  import { expectGroup } from "./typeUtils/expectGroup.js";
30
30
  import { err, ok, okAsync, Result, ResultAsync } from "neverthrow";
31
+ import { CoValueState } from "./coValueState.js";
31
32
 
32
33
  /** A `LocalNode` represents a local view of a set of loaded `CoValue`s, from the perspective of a particular account (or primitive cryptographic agent).
33
34
 
34
- A `LocalNode` can have peers that it syncs to, for example some form of local persistence, or a sync server, such as `sync.jazz.tools` (Jazz Global Mesh).
35
+ A `LocalNode` can have peers that it syncs to, for example some form of local persistence, or a sync server, such as `cloud.jazz.tools` (Jazz Cloud).
35
36
 
36
37
  @example
37
38
  You typically get hold of a `LocalNode` using `jazz-react`'s `useJazz()`:
@@ -130,10 +131,9 @@ export class LocalNode {
130
131
  );
131
132
 
132
133
  nodeWithAccount.account = controlledAccount;
133
- nodeWithAccount.coValues[controlledAccount.id] = {
134
- state: "loaded",
135
- coValue: controlledAccount.core,
136
- };
134
+ nodeWithAccount.coValues[controlledAccount.id] = CoValueState.Available(
135
+ controlledAccount.core,
136
+ );
137
137
  controlledAccount.core._cachedContent = undefined;
138
138
 
139
139
  if (!controlledAccount.get("profile")) {
@@ -145,9 +145,9 @@ export class LocalNode {
145
145
  for (const coValueEntry of Object.values(
146
146
  nodeWithAccount.coValues,
147
147
  )) {
148
- if (coValueEntry.state === "loaded") {
148
+ if (coValueEntry.state.type === "available") {
149
149
  void nodeWithAccount.syncManager.syncCoValue(
150
- coValueEntry.coValue,
150
+ coValueEntry.state.coValue,
151
151
  );
152
152
  }
153
153
  }
@@ -214,10 +214,9 @@ export class LocalNode {
214
214
  node.syncManager.local = node;
215
215
 
216
216
  controlledAccount.core.node = node;
217
- node.coValues[accountID] = {
218
- state: "loaded",
219
- coValue: controlledAccount.core,
220
- };
217
+ node.coValues[accountID] = CoValueState.Available(
218
+ controlledAccount.core,
219
+ );
221
220
  controlledAccount.core._cachedContent = undefined;
222
221
 
223
222
  const profileID = account.get("profile");
@@ -257,7 +256,7 @@ export class LocalNode {
257
256
  }
258
257
 
259
258
  const coValue = new CoValueCore(header, this);
260
- this.coValues[coValue.id] = { state: "loaded", coValue: coValue };
259
+ this.coValues[coValue.id] = CoValueState.Available(coValue);
261
260
 
262
261
  void this.syncManager.syncCoValue(coValue);
263
262
 
@@ -270,7 +269,6 @@ export class LocalNode {
270
269
  options: {
271
270
  dontLoadFrom?: PeerID;
272
271
  dontWaitFor?: PeerID;
273
- onProgress?: (progress: number) => void;
274
272
  } = {},
275
273
  ): Promise<CoValueCore | "unavailable"> {
276
274
  if (this.crashed) {
@@ -287,7 +285,7 @@ export class LocalNode {
287
285
  .map((peer) => peer.id),
288
286
  );
289
287
  if (options.dontWaitFor) peersToWaitFor.delete(options.dontWaitFor);
290
- entry = newLoadingState(peersToWaitFor, options.onProgress);
288
+ entry = CoValueState.Unknown(peersToWaitFor);
291
289
 
292
290
  this.coValues[id] = entry;
293
291
 
@@ -302,10 +300,19 @@ export class LocalNode {
302
300
  );
303
301
  });
304
302
  }
305
- if (entry.state === "loaded") {
306
- return Promise.resolve(entry.coValue);
303
+ if (entry.state.type === "available") {
304
+ return Promise.resolve(entry.state.coValue);
307
305
  }
308
- return entry.done;
306
+
307
+ await entry.state.ready;
308
+
309
+ const updatedEntry = this.coValues[id];
310
+
311
+ if (updatedEntry?.state.type === "available") {
312
+ return Promise.resolve(updatedEntry.state.coValue);
313
+ }
314
+
315
+ return "unavailable";
309
316
  }
310
317
 
311
318
  /**
@@ -315,11 +322,8 @@ export class LocalNode {
315
322
  *
316
323
  * @category 3. Low-level
317
324
  */
318
- async load<T extends RawCoValue>(
319
- id: CoID<T>,
320
- onProgress?: (progress: number) => void,
321
- ): Promise<T | "unavailable"> {
322
- const core = await this.loadCoValueCore(id, { onProgress });
325
+ async load<T extends RawCoValue>(id: CoID<T>): Promise<T | "unavailable"> {
326
+ const core = await this.loadCoValueCore(id);
323
327
 
324
328
  if (core === "unavailable") {
325
329
  return "unavailable";
@@ -333,8 +337,8 @@ export class LocalNode {
333
337
  if (!entry) {
334
338
  return undefined;
335
339
  }
336
- if (entry.state === "loaded") {
337
- return entry.coValue.getCurrentContent() as T;
340
+ if (entry.state.type === "available") {
341
+ return entry.state.coValue.getCurrentContent() as T;
338
342
  }
339
343
  return undefined;
340
344
  }
@@ -465,14 +469,14 @@ export class LocalNode {
465
469
  `${expectation ? expectation + ": " : ""}Unknown CoValue ${id}`,
466
470
  );
467
471
  }
468
- if (entry.state === "loading") {
472
+ if (entry.state.type === "unknown") {
469
473
  throw new Error(
470
474
  `${
471
475
  expectation ? expectation + ": " : ""
472
476
  }CoValue ${id} not yet loaded`,
473
477
  );
474
478
  }
475
- return entry.coValue;
479
+ return entry.state.coValue;
476
480
  }
477
481
 
478
482
  /** @internal */
@@ -547,7 +551,7 @@ export class LocalNode {
547
551
  }
548
552
 
549
553
  let coValue: CoValueCore;
550
-
554
+
551
555
  try {
552
556
  coValue = this.expectCoValueLoaded(id, expectation);
553
557
  } catch (e) {
@@ -558,7 +562,7 @@ export class LocalNode {
558
562
  error: e,
559
563
  } satisfies LoadCoValueCoreError);
560
564
  }
561
-
565
+
562
566
  if (
563
567
  coValue.header.type !== "comap" ||
564
568
  coValue.header.ruleset.type !== "group" ||
@@ -673,13 +677,16 @@ export class LocalNode {
673
677
  const [coValueID, entry] =
674
678
  coValuesToCopy[coValuesToCopy.length - 1]!;
675
679
 
676
- if (entry.state === "loading") {
680
+ if (entry.state.type === "unknown") {
677
681
  coValuesToCopy.pop();
678
682
  continue;
679
683
  } else {
680
- const allDepsCopied = entry.coValue
684
+ const allDepsCopied = entry.state.coValue
681
685
  .getDependedOnCoValues()
682
- .every((dep) => newNode.coValues[dep]?.state === "loaded");
686
+ .every(
687
+ (dep) =>
688
+ newNode.coValues[dep]?.state.type === "available",
689
+ );
683
690
 
684
691
  if (!allDepsCopied) {
685
692
  // move to end of queue
@@ -688,15 +695,13 @@ export class LocalNode {
688
695
  }
689
696
 
690
697
  const newCoValue = new CoValueCore(
691
- entry.coValue.header,
698
+ entry.state.coValue.header,
692
699
  newNode,
693
- new Map(entry.coValue.sessionLogs),
700
+ new Map(entry.state.coValue.sessionLogs),
694
701
  );
695
702
 
696
- newNode.coValues[coValueID as RawCoID] = {
697
- state: "loaded",
698
- coValue: newCoValue,
699
- };
703
+ newNode.coValues[coValueID as RawCoID] =
704
+ CoValueState.Available(newCoValue);
700
705
 
701
706
  coValuesToCopy.pop();
702
707
  }
@@ -722,30 +727,6 @@ export class LocalNode {
722
727
  }
723
728
  }
724
729
 
725
- /** @internal */
726
- type CoValueState =
727
- | {
728
- state: "loading";
729
- done: Promise<CoValueCore | "unavailable">;
730
- resolve: (coValue: CoValueCore | "unavailable") => void;
731
- onProgress?: (progress: number) => void;
732
- firstPeerState: {
733
- [peerID: string]:
734
- | {
735
- type: "waiting";
736
- done: Promise<void>;
737
- resolve: () => void;
738
- }
739
- | { type: "available" }
740
- | { type: "unavailable" };
741
- };
742
- }
743
- | {
744
- state: "loaded";
745
- coValue: CoValueCore;
746
- onProgress?: (progress: number) => void;
747
- };
748
-
749
730
  export type LoadCoValueCoreError = {
750
731
  type: "ErrorLoadingCoValueCore";
751
732
  error: unknown;
@@ -770,31 +751,3 @@ export type ResolveAccountAgentError =
770
751
  | LoadCoValueCoreError
771
752
  | AccountUnavailableFromAllPeersError
772
753
  | UnexpectedlyNotAccountError;
773
-
774
- /** @internal */
775
- export function newLoadingState(
776
- currentPeerIds: Set<PeerID>,
777
- onProgress?: (progress: number) => void,
778
- ): CoValueState {
779
- let resolve: (coValue: CoValueCore | "unavailable") => void;
780
-
781
- const promise = new Promise<CoValueCore | "unavailable">((r) => {
782
- resolve = r;
783
- });
784
-
785
- return {
786
- state: "loading",
787
- done: promise,
788
- resolve: resolve!,
789
- onProgress,
790
- firstPeerState: Object.fromEntries(
791
- [...currentPeerIds].map((id) => {
792
- let resolve: () => void;
793
- const done = new Promise<void>((r) => {
794
- resolve = r;
795
- });
796
- return [id, { type: "waiting", done, resolve: resolve! }];
797
- }),
798
- ),
799
- };
800
- }
@@ -79,7 +79,7 @@ export class LSMStorage<WH, RH, FS extends FileSystem<WH, RH>> {
79
79
 
80
80
  if (msg.action === "content") {
81
81
  await this.handleNewContent(msg);
82
- } else {
82
+ } else if (msg.action === 'load' || msg.action === 'known') {
83
83
  await this.sendNewContent(msg.id, msg, undefined);
84
84
  }
85
85
  } catch (e) {
package/src/sync.ts CHANGED
@@ -1,7 +1,8 @@
1
1
  import { Signature } from "./crypto/crypto.js";
2
2
  import { CoValueHeader, Transaction } from "./coValueCore.js";
3
3
  import { CoValueCore } from "./coValueCore.js";
4
- import { LocalNode, newLoadingState } from "./localNode.js";
4
+ import { LocalNode } from "./localNode.js";
5
+ import { CoValueState } from "./coValueState.js";
5
6
  import { RawCoID, SessionID } from "./ids.js";
6
7
  import { PeerState } from "./PeerState.js";
7
8
  import { CoValuePriority } from "./priority.js";
@@ -131,8 +132,9 @@ export class SyncManager {
131
132
  (peer) => peer.id !== forPeer && peer.role === "server",
132
133
  );
133
134
 
135
+ const coValueEntry = this.local.coValues[id];
136
+
134
137
  for (const peer of eligiblePeers) {
135
- // console.log("loading", id, "from", peer.id);
136
138
  await peer.pushOutgoingMessage({
137
139
  action: "load",
138
140
  id: id,
@@ -140,44 +142,9 @@ export class SyncManager {
140
142
  sessions: {},
141
143
  });
142
144
 
143
- const coValueEntry = this.local.coValues[id];
144
- if (coValueEntry?.state !== "loading") {
145
- continue;
145
+ if (coValueEntry?.state.type === "unknown") {
146
+ await coValueEntry.state.waitForPeer(peer.id);
146
147
  }
147
- const firstStateEntry = coValueEntry.firstPeerState[peer.id];
148
- if (firstStateEntry?.type !== "waiting") {
149
- throw new Error("Expected firstPeerState to be waiting " + id);
150
- }
151
- await new Promise<void>((resolve) => {
152
- // const timeout = setTimeout(() => {
153
- // if (this.local.coValues[id]?.state === "loading") {
154
- // console.warn(
155
- // "Timeout waiting for peer to load",
156
- // id,
157
- // "from",
158
- // peer.id,
159
- // "and it hasn't loaded from other peers yet"
160
- // );
161
- // }
162
- // resolve();
163
- // }, 1000);
164
- firstStateEntry.done
165
- .then(() => {
166
- // clearTimeout(timeout);
167
- resolve();
168
- })
169
- .catch((e) => {
170
- // clearTimeout(timeout);
171
- console.error(
172
- "Error waiting for peer to load",
173
- id,
174
- "from",
175
- peer.id,
176
- e,
177
- );
178
- resolve();
179
- });
180
- });
181
148
  }
182
149
  }
183
150
 
@@ -213,7 +180,7 @@ export class SyncManager {
213
180
  throw new Error("Expected coValue entry on subscribe");
214
181
  }
215
182
 
216
- if (entry.state === "loading") {
183
+ if (entry.state.type === "unknown") {
217
184
  this.trySendToPeer(peer, {
218
185
  action: "load",
219
186
  id,
@@ -225,7 +192,7 @@ export class SyncManager {
225
192
  return;
226
193
  }
227
194
 
228
- const coValue = entry.coValue;
195
+ const coValue = entry.state.coValue;
229
196
 
230
197
  for (const id of coValue.getDependedOnCoValues()) {
231
198
  await this.subscribeToIncludingDependencies(id, peer);
@@ -274,10 +241,7 @@ export class SyncManager {
274
241
  }
275
242
  }
276
243
 
277
- async sendNewContentIncludingDependencies(
278
- id: RawCoID,
279
- peer: PeerState,
280
- ) {
244
+ async sendNewContentIncludingDependencies(id: RawCoID, peer: PeerState) {
281
245
  const coValue = this.local.expectCoValueLoaded(id);
282
246
 
283
247
  await Promise.all(
@@ -289,12 +253,12 @@ export class SyncManager {
289
253
  );
290
254
 
291
255
  const newContentPieces = coValue.newContentSince(
292
- peer.optimisticKnownStates[id],
256
+ peer.optimisticKnownStates.get(id),
293
257
  );
294
258
 
295
259
  if (newContentPieces) {
296
260
  const optimisticKnownStateBefore =
297
- peer.optimisticKnownStates[id] || emptyKnownState(id);
261
+ peer.optimisticKnownStates.get(id) || emptyKnownState(id);
298
262
 
299
263
  const sendPieces = async () => {
300
264
  let lastYield = performance.now();
@@ -321,14 +285,19 @@ export class SyncManager {
321
285
 
322
286
  sendPieces().catch((e) => {
323
287
  console.error("Error sending new content piece, retrying", e);
324
- peer.optimisticKnownStates[id] = optimisticKnownStateBefore;
288
+ peer.optimisticKnownStates.dispatch({
289
+ type: "SET",
290
+ id,
291
+ value: optimisticKnownStateBefore ?? emptyKnownState(id),
292
+ });
325
293
  return this.sendNewContentIncludingDependencies(id, peer);
326
294
  });
327
295
 
328
- peer.optimisticKnownStates[id] = combinedKnownStates(
329
- optimisticKnownStateBefore,
330
- coValue.knownState(),
331
- );
296
+ peer.optimisticKnownStates.dispatch({
297
+ type: "COMBINE_WITH",
298
+ id,
299
+ value: coValue.knownState(),
300
+ });
332
301
  }
333
302
  }
334
303
 
@@ -344,11 +313,10 @@ export class SyncManager {
344
313
  // console.log("subscribing to after peer added", id, peer.id)
345
314
  await this.subscribeToIncludingDependencies(id, peerState);
346
315
 
347
- peerState.optimisticKnownStates[id] = {
348
- id: id,
349
- header: false,
350
- sessions: {},
351
- };
316
+ peerState.optimisticKnownStates.dispatch({
317
+ type: "SET_AS_EMPTY",
318
+ id,
319
+ });
352
320
  }
353
321
  };
354
322
  void initialSync();
@@ -412,7 +380,11 @@ export class SyncManager {
412
380
  }
413
381
 
414
382
  async handleLoad(msg: LoadMessage, peer: PeerState) {
415
- peer.optimisticKnownStates[msg.id] = knownStateIn(msg);
383
+ peer.optimisticKnownStates.dispatch({
384
+ type: "SET",
385
+ id: msg.id,
386
+ value: knownStateIn(msg),
387
+ });
416
388
  let entry = this.local.coValues[msg.id];
417
389
 
418
390
  if (!entry) {
@@ -425,7 +397,7 @@ export class SyncManager {
425
397
  );
426
398
  if (eligiblePeers.length === 0) {
427
399
  if (msg.header || Object.keys(msg.sessions).length > 0) {
428
- this.local.coValues[msg.id] = newLoadingState(
400
+ this.local.coValues[msg.id] = CoValueState.Unknown(
429
401
  new Set([peer.id]),
430
402
  );
431
403
  this.trySendToPeer(peer, {
@@ -452,17 +424,21 @@ export class SyncManager {
452
424
  entry = this.local.coValues[msg.id]!;
453
425
  }
454
426
 
455
- if (entry.state === "loading") {
427
+ if (entry.state.type === "unknown") {
456
428
  // console.debug(
457
429
  // "Waiting for loaded",
458
430
  // msg.id,
459
431
  // "after message from",
460
432
  // peer.id,
461
433
  // );
462
- const loaded = await entry.done;
463
- // console.log("Loaded", msg.id, loaded);
434
+ const loaded = await entry.state.ready;
435
+
464
436
  if (loaded === "unavailable") {
465
- peer.optimisticKnownStates[msg.id] = knownStateIn(msg);
437
+ peer.optimisticKnownStates.dispatch({
438
+ type: "SET",
439
+ id: msg.id,
440
+ value: knownStateIn(msg),
441
+ });
466
442
  peer.toldKnownState.add(msg.id);
467
443
 
468
444
  this.trySendToPeer(peer, {
@@ -485,10 +461,11 @@ export class SyncManager {
485
461
  async handleKnownState(msg: KnownStateMessage, peer: PeerState) {
486
462
  let entry = this.local.coValues[msg.id];
487
463
 
488
- peer.optimisticKnownStates[msg.id] = combinedKnownStates(
489
- peer.optimisticKnownStates[msg.id] || emptyKnownState(msg.id),
490
- knownStateIn(msg),
491
- );
464
+ peer.optimisticKnownStates.dispatch({
465
+ type: "COMBINE_WITH",
466
+ id: msg.id,
467
+ value: knownStateIn(msg),
468
+ });
492
469
 
493
470
  if (!entry) {
494
471
  if (msg.asDependencyOf) {
@@ -514,31 +491,17 @@ export class SyncManager {
514
491
  }
515
492
  }
516
493
 
517
- if (entry.state === "loading") {
518
- const availableOnPeer = peer.optimisticKnownStates[msg.id]?.header;
519
- const firstPeerStateEntry = entry.firstPeerState[peer.id];
520
- if (firstPeerStateEntry?.type === "waiting") {
521
- firstPeerStateEntry.resolve();
522
- }
523
- entry.firstPeerState[peer.id] = availableOnPeer
524
- ? { type: "available" }
525
- : { type: "unavailable" };
526
- // console.log(
527
- // "Marking",
528
- // msg.id,
529
- // "as",
530
- // entry.firstPeerState[peer.id]?.type,
531
- // "from",
532
- // peer.id
533
- // );
534
- if (
535
- Object.values(entry.firstPeerState).every(
536
- (s) => s.type === "unavailable",
537
- )
538
- ) {
539
- entry.resolve("unavailable");
494
+ if (entry.state.type === "unknown") {
495
+ const availableOnPeer = peer.optimisticKnownStates.get(msg.id)?.header;
496
+
497
+ if (!availableOnPeer) {
498
+ entry.dispatch({
499
+ type: "not-found",
500
+ peerId: peer.id,
501
+ });
540
502
  }
541
- return [];
503
+
504
+ return;
542
505
  }
543
506
 
544
507
  await this.tellUntoldKnownStateIncludingDependencies(msg.id, peer);
@@ -546,7 +509,7 @@ export class SyncManager {
546
509
  }
547
510
 
548
511
  async handleNewContent(msg: NewContentMessage, peer: PeerState) {
549
- let entry = this.local.coValues[msg.id];
512
+ const entry = this.local.coValues[msg.id];
550
513
 
551
514
  if (!entry) {
552
515
  console.error(
@@ -555,46 +518,31 @@ export class SyncManager {
555
518
  return;
556
519
  }
557
520
 
558
- let resolveAfterDone: ((coValue: CoValueCore) => void) | undefined;
559
-
560
- const peerOptimisticKnownState = peer.optimisticKnownStates[msg.id];
561
-
562
- if (!peerOptimisticKnownState) {
563
- console.error(
564
- "Expected optimisticKnownState to be set for coValue we receive new content for",
565
- );
566
- return;
567
- }
521
+ let coValue: CoValueCore;
568
522
 
569
- if (entry.state === "loading") {
523
+ if (entry.state.type === "unknown") {
570
524
  if (!msg.header) {
571
525
  console.error("Expected header to be sent in first message");
572
526
  return;
573
527
  }
574
528
 
575
- const firstPeerStateEntry = entry.firstPeerState[peer.id];
576
- if (firstPeerStateEntry?.type === "waiting") {
577
- firstPeerStateEntry.resolve();
578
- entry.firstPeerState[peer.id] = { type: "available" };
579
- }
580
-
581
- peerOptimisticKnownState.header = true;
582
-
583
- const coValue = new CoValueCore(msg.header, this.local);
529
+ peer.optimisticKnownStates.dispatch({
530
+ type: "UPDATE_HEADER",
531
+ id: msg.id,
532
+ header: true,
533
+ });
584
534
 
585
- resolveAfterDone = entry.resolve;
535
+ coValue = new CoValueCore(msg.header, this.local);
586
536
 
587
- entry = {
588
- state: "loaded",
589
- coValue: coValue,
590
- onProgress: entry.onProgress,
591
- };
592
-
593
- this.local.coValues[msg.id] = entry;
537
+ entry.dispatch({
538
+ type: "found",
539
+ coValue,
540
+ peerId: peer.id,
541
+ });
542
+ } else {
543
+ coValue = entry.state.coValue;
594
544
  }
595
545
 
596
- const coValue = entry.coValue;
597
-
598
546
  let invalidStateAssumed = false;
599
547
 
600
548
  for (const [sessionID, newContentForSession] of Object.entries(
@@ -648,15 +596,13 @@ export class SyncManager {
648
596
  );
649
597
  }
650
598
 
651
- const theirTotalnTxs = Object.values(
652
- peer.optimisticKnownStates[msg.id]?.sessions || {},
653
- ).reduce((sum, nTxs) => sum + nTxs, 0);
654
- const ourTotalnTxs = [...coValue.sessionLogs.values()].reduce(
655
- (sum, session) => sum + session.transactions.length,
656
- 0,
657
- );
658
-
659
- entry.onProgress?.(ourTotalnTxs / theirTotalnTxs);
599
+ // const theirTotalnTxs = Object.values(
600
+ // peer.optimisticKnownStates[msg.id]?.sessions || {},
601
+ // ).reduce((sum, nTxs) => sum + nTxs, 0);
602
+ // const ourTotalnTxs = [...coValue.sessionLogs.values()].reduce(
603
+ // (sum, session) => sum + session.transactions.length,
604
+ // 0,
605
+ // );
660
606
 
661
607
  if (result.isErr()) {
662
608
  console.error(
@@ -673,15 +619,14 @@ export class SyncManager {
673
619
  continue;
674
620
  }
675
621
 
676
- peerOptimisticKnownState.sessions[sessionID] = Math.max(
677
- peerOptimisticKnownState.sessions[sessionID] || 0,
678
- newContentForSession.after +
622
+ peer.optimisticKnownStates.dispatch({
623
+ type: "UPDATE_SESSION_COUNTER",
624
+ id: msg.id,
625
+ sessionId: sessionID,
626
+ value:
627
+ newContentForSession.after +
679
628
  newContentForSession.newTransactions.length,
680
- );
681
- }
682
-
683
- if (resolveAfterDone) {
684
- resolveAfterDone(coValue);
629
+ });
685
630
  }
686
631
 
687
632
  await this.syncCoValue(coValue);
@@ -698,7 +643,11 @@ export class SyncManager {
698
643
  }
699
644
 
700
645
  async handleCorrection(msg: KnownStateMessage, peer: PeerState) {
701
- peer.optimisticKnownStates[msg.id] = msg;
646
+ peer.optimisticKnownStates.dispatch({
647
+ type: "SET",
648
+ id: msg.id,
649
+ value: knownStateIn(msg),
650
+ });
702
651
 
703
652
  return this.sendNewContentIncludingDependencies(msg.id, peer);
704
653
  }
@@ -740,9 +689,7 @@ export class SyncManager {
740
689
  // });
741
690
  // blockingSince = performance.now();
742
691
  // }
743
- const optimisticKnownState = peer.optimisticKnownStates[coValue.id];
744
-
745
- if (optimisticKnownState) {
692
+ if (peer.optimisticKnownStates.has(coValue.id)) {
746
693
  await this.tellUntoldKnownStateIncludingDependencies(
747
694
  coValue.id,
748
695
  peer,