cojson 0.13.10 → 0.13.12

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 (102) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +15 -0
  3. package/dist/CoValuesStore.d.ts +3 -1
  4. package/dist/CoValuesStore.d.ts.map +1 -1
  5. package/dist/CoValuesStore.js +7 -6
  6. package/dist/CoValuesStore.js.map +1 -1
  7. package/dist/PeerKnownStates.d.ts +5 -25
  8. package/dist/PeerKnownStates.d.ts.map +1 -1
  9. package/dist/PeerKnownStates.js +7 -20
  10. package/dist/PeerKnownStates.js.map +1 -1
  11. package/dist/PeerState.d.ts +14 -9
  12. package/dist/PeerState.d.ts.map +1 -1
  13. package/dist/PeerState.js +51 -8
  14. package/dist/PeerState.js.map +1 -1
  15. package/dist/SyncStateManager.js +2 -2
  16. package/dist/SyncStateManager.js.map +1 -1
  17. package/dist/coValueCore.js +2 -2
  18. package/dist/coValueCore.js.map +1 -1
  19. package/dist/coValueState.d.ts +21 -46
  20. package/dist/coValueState.d.ts.map +1 -1
  21. package/dist/coValueState.js +174 -246
  22. package/dist/coValueState.js.map +1 -1
  23. package/dist/coValues/coList.d.ts +13 -2
  24. package/dist/coValues/coList.d.ts.map +1 -1
  25. package/dist/coValues/coList.js +60 -34
  26. package/dist/coValues/coList.js.map +1 -1
  27. package/dist/coValues/coPlainText.d.ts +45 -0
  28. package/dist/coValues/coPlainText.d.ts.map +1 -1
  29. package/dist/coValues/coPlainText.js +61 -11
  30. package/dist/coValues/coPlainText.js.map +1 -1
  31. package/dist/coValues/group.js +2 -2
  32. package/dist/coValues/group.js.map +1 -1
  33. package/dist/exports.d.ts +2 -4
  34. package/dist/exports.d.ts.map +1 -1
  35. package/dist/exports.js +1 -2
  36. package/dist/exports.js.map +1 -1
  37. package/dist/localNode.d.ts.map +1 -1
  38. package/dist/localNode.js +20 -16
  39. package/dist/localNode.js.map +1 -1
  40. package/dist/sync.d.ts.map +1 -1
  41. package/dist/sync.js +40 -92
  42. package/dist/sync.js.map +1 -1
  43. package/dist/tests/PeerKnownStates.test.js +9 -14
  44. package/dist/tests/PeerKnownStates.test.js.map +1 -1
  45. package/dist/tests/PeerState.test.js +22 -34
  46. package/dist/tests/PeerState.test.js.map +1 -1
  47. package/dist/tests/coList.test.js +63 -0
  48. package/dist/tests/coList.test.js.map +1 -1
  49. package/dist/tests/coPlainText.test.js +66 -11
  50. package/dist/tests/coPlainText.test.js.map +1 -1
  51. package/dist/tests/coValueState.test.js +57 -104
  52. package/dist/tests/coValueState.test.js.map +1 -1
  53. package/dist/tests/group.test.js +1 -2
  54. package/dist/tests/group.test.js.map +1 -1
  55. package/dist/tests/messagesTestUtils.d.ts +4 -1
  56. package/dist/tests/messagesTestUtils.d.ts.map +1 -1
  57. package/dist/tests/messagesTestUtils.js +10 -0
  58. package/dist/tests/messagesTestUtils.js.map +1 -1
  59. package/dist/tests/sync.mesh.test.js +65 -3
  60. package/dist/tests/sync.mesh.test.js.map +1 -1
  61. package/dist/tests/sync.peerReconciliation.test.js +8 -8
  62. package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
  63. package/dist/tests/sync.test.js +6 -4
  64. package/dist/tests/sync.test.js.map +1 -1
  65. package/package.json +1 -1
  66. package/src/CoValuesStore.ts +9 -6
  67. package/src/PeerKnownStates.ts +19 -56
  68. package/src/PeerState.ts +69 -13
  69. package/src/SyncStateManager.ts +2 -2
  70. package/src/coValueCore.ts +2 -2
  71. package/src/coValueState.ts +197 -317
  72. package/src/coValues/coList.ts +84 -44
  73. package/src/coValues/coPlainText.ts +75 -11
  74. package/src/coValues/group.ts +2 -2
  75. package/src/exports.ts +0 -6
  76. package/src/localNode.ts +30 -21
  77. package/src/sync.ts +46 -95
  78. package/src/tests/PeerKnownStates.test.ts +9 -14
  79. package/src/tests/PeerState.test.ts +27 -40
  80. package/src/tests/coList.test.ts +83 -0
  81. package/src/tests/coPlainText.test.ts +81 -11
  82. package/src/tests/coValueState.test.ts +55 -106
  83. package/src/tests/group.test.ts +2 -2
  84. package/src/tests/messagesTestUtils.ts +12 -1
  85. package/src/tests/sync.mesh.test.ts +81 -3
  86. package/src/tests/sync.peerReconciliation.test.ts +8 -8
  87. package/src/tests/sync.test.ts +8 -23
  88. package/dist/storage/FileSystem.d.ts +0 -37
  89. package/dist/storage/FileSystem.d.ts.map +0 -1
  90. package/dist/storage/FileSystem.js +0 -48
  91. package/dist/storage/FileSystem.js.map +0 -1
  92. package/dist/storage/chunksAndKnownStates.d.ts +0 -7
  93. package/dist/storage/chunksAndKnownStates.d.ts.map +0 -1
  94. package/dist/storage/chunksAndKnownStates.js +0 -98
  95. package/dist/storage/chunksAndKnownStates.js.map +0 -1
  96. package/dist/storage/index.d.ts +0 -52
  97. package/dist/storage/index.d.ts.map +0 -1
  98. package/dist/storage/index.js +0 -335
  99. package/dist/storage/index.js.map +0 -1
  100. package/src/storage/FileSystem.ts +0 -113
  101. package/src/storage/chunksAndKnownStates.ts +0 -137
  102. package/src/storage/index.ts +0 -531
@@ -2,6 +2,7 @@ import { CoID, RawCoValue } from "../coValue.js";
2
2
  import { CoValueCore } from "../coValueCore.js";
3
3
  import { AgentID, SessionID, TransactionID } from "../ids.js";
4
4
  import { JsonObject, JsonValue } from "../jsonValue.js";
5
+ import { CoValueKnownState } from "../sync.js";
5
6
  import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
6
7
  import { isCoValue } from "../typeUtils/isCoValue.js";
7
8
  import { RawAccountID } from "./account.js";
@@ -81,6 +82,8 @@ export class RawCoListView<
81
82
  madeAt: number;
82
83
  opID: OpID;
83
84
  }[];
85
+ /** @internal */
86
+ knownTransactions: CoValueKnownState["sessions"];
84
87
 
85
88
  /** @internal */
86
89
  constructor(core: CoValueCore) {
@@ -95,12 +98,24 @@ export class RawCoListView<
95
98
  this.deletionsByInsertion = {};
96
99
  this.afterStart = [];
97
100
  this.beforeEnd = [];
101
+ this.knownTransactions = {};
102
+
103
+ this.processNewTransactions();
104
+ }
105
+
106
+ processNewTransactions() {
107
+ const newTransactions = this.core.getValidTransactions({
108
+ ignorePrivateTransactions: false,
109
+ knownTransactions: this.knownTransactions,
110
+ });
98
111
 
99
- for (const {
100
- txID,
101
- changes,
102
- madeAt,
103
- } of this.core.getValidSortedTransactions()) {
112
+ if (newTransactions.length === 0) {
113
+ return;
114
+ }
115
+
116
+ this._cachedEntries = undefined;
117
+
118
+ for (const { txID, changes, madeAt } of newTransactions) {
104
119
  for (const [changeIdx, changeUntyped] of changes.entries()) {
105
120
  const change = changeUntyped as ListOpPayload<Item>;
106
121
 
@@ -192,6 +207,11 @@ export class RawCoListView<
192
207
  );
193
208
  }
194
209
  }
210
+
211
+ this.knownTransactions[txID.sessionID] = Math.max(
212
+ this.knownTransactions[txID.sessionID] ?? 0,
213
+ txID.txIndex,
214
+ );
195
215
  }
196
216
  }
197
217
 
@@ -279,30 +299,52 @@ export class RawCoListView<
279
299
  opID: OpID;
280
300
  }[],
281
301
  ) {
282
- const entry =
283
- this.insertions[opID.sessionID]?.[opID.txIndex]?.[opID.changeIdx];
302
+ const todo = [opID]; // a stack with the next item to do at the end
303
+ const predecessorsVisited = new Set<OpID>();
284
304
 
285
- if (!entry) {
286
- throw new Error("Missing op " + opID);
287
- }
288
- for (const predecessor of entry.predecessors) {
289
- this.fillArrayFromOpID(predecessor, arr);
290
- }
291
- const deleted =
292
- (this.deletionsByInsertion[opID.sessionID]?.[opID.txIndex]?.[
293
- opID.changeIdx
294
- ]?.length || 0) > 0;
295
- if (!deleted) {
296
- arr.push({
297
- value: entry.value,
298
- madeAt: entry.madeAt,
299
- opID,
300
- });
301
- }
302
- // traverse successors in reverse for correct insertion behavior
303
- for (let i = entry.successors.length - 1; i >= 0; i--) {
304
- const successor = entry.successors[i]!;
305
- this.fillArrayFromOpID(successor, arr);
305
+ while (todo.length > 0) {
306
+ const currentOpID = todo[todo.length - 1]!;
307
+
308
+ const entry =
309
+ this.insertions[currentOpID.sessionID]?.[currentOpID.txIndex]?.[
310
+ currentOpID.changeIdx
311
+ ];
312
+
313
+ if (!entry) {
314
+ throw new Error("Missing op " + currentOpID);
315
+ }
316
+
317
+ const shouldTraversePredecessors =
318
+ entry.predecessors.length > 0 && !predecessorsVisited.has(currentOpID);
319
+
320
+ // We navigate the predecessors before processing the current opID in the list
321
+ if (shouldTraversePredecessors) {
322
+ for (let i = entry.predecessors.length - 1; i >= 0; i--) {
323
+ todo.push(entry.predecessors[i]!);
324
+ }
325
+ predecessorsVisited.add(currentOpID);
326
+ } else {
327
+ // Remove the current opID from the todo stack to consider it processed.
328
+ todo.pop();
329
+
330
+ const deleted =
331
+ (this.deletionsByInsertion[currentOpID.sessionID]?.[
332
+ currentOpID.txIndex
333
+ ]?.[currentOpID.changeIdx]?.length || 0) > 0;
334
+
335
+ if (!deleted) {
336
+ arr.push({
337
+ value: entry.value,
338
+ madeAt: entry.madeAt,
339
+ opID: currentOpID,
340
+ });
341
+ }
342
+
343
+ // traverse successors in reverse for correct insertion behavior
344
+ for (const successor of entry.successors) {
345
+ todo.push(successor);
346
+ }
347
+ }
306
348
  }
307
349
  }
308
350
 
@@ -410,6 +452,15 @@ export class RawCoList<
410
452
  this.appendItems([item], after, privacy);
411
453
  }
412
454
 
455
+ /**
456
+ * Appends `items` to the list at index `after`. If `after` is negative, it is treated as `0`.
457
+ *
458
+ * If `privacy` is `"private"` **(default)**, `items` are encrypted in the transaction, only readable by other members of the group this `CoList` belongs to. Not even sync servers can see the content in plaintext.
459
+ *
460
+ * If `privacy` is `"trusting"`, `items` are stored in plaintext in the transaction, visible to everyone who gets a hold of it, including sync servers.
461
+ *
462
+ * @category 2. Editing
463
+ */
413
464
  appendItems(
414
465
  items: Item[],
415
466
  after?: number,
@@ -421,7 +472,7 @@ export class RawCoList<
421
472
  ? entries.length > 0
422
473
  ? entries.length - 1
423
474
  : 0
424
- : after;
475
+ : Math.max(0, after);
425
476
  let opIDBefore: OpID | "start";
426
477
  if (entries.length > 0) {
427
478
  const entryBefore = entries[after];
@@ -450,7 +501,7 @@ export class RawCoList<
450
501
 
451
502
  this.core.makeTransaction(changes, privacy);
452
503
 
453
- this.rebuildFromCore();
504
+ this.processNewTransactions();
454
505
  }
455
506
 
456
507
  /**
@@ -497,7 +548,7 @@ export class RawCoList<
497
548
  privacy,
498
549
  );
499
550
 
500
- this.rebuildFromCore();
551
+ this.processNewTransactions();
501
552
  }
502
553
 
503
554
  /** Deletes the item at index `at`.
@@ -524,7 +575,7 @@ export class RawCoList<
524
575
  privacy,
525
576
  );
526
577
 
527
- this.rebuildFromCore();
578
+ this.processNewTransactions();
528
579
  }
529
580
 
530
581
  replace(
@@ -552,17 +603,6 @@ export class RawCoList<
552
603
  ],
553
604
  privacy,
554
605
  );
555
- this.rebuildFromCore();
556
- }
557
-
558
- /** @internal */
559
- rebuildFromCore() {
560
- const listAfter = new RawCoList(this.core) as this;
561
-
562
- this.afterStart = listAfter.afterStart;
563
- this.beforeEnd = listAfter.beforeEnd;
564
- this.insertions = listAfter.insertions;
565
- this.deletionsByInsertion = listAfter.deletionsByInsertion;
566
- this._cachedEntries = undefined;
606
+ this.processNewTransactions();
567
607
  }
568
608
  }
@@ -2,6 +2,12 @@ import { CoValueCore } from "../coValueCore.js";
2
2
  import { JsonObject } from "../jsonValue.js";
3
3
  import { DeletionOpPayload, OpID, RawCoList } from "./coList.js";
4
4
 
5
+ declare const navigator:
6
+ | {
7
+ language: string;
8
+ }
9
+ | undefined;
10
+
5
11
  export type StringifiedOpID = string & { __stringifiedOpID: true };
6
12
 
7
13
  export function stringifyOpID(opID: OpID): StringifiedOpID {
@@ -15,6 +21,33 @@ type PlaintextIdxMapping = {
15
21
  idxBeforeOpID: { [opID: StringifiedOpID]: number };
16
22
  };
17
23
 
24
+ /**
25
+ * A collaborative plain text implementation that supports grapheme-accurate editing.
26
+ *
27
+ * Locale support:
28
+ * - Locale can be specified in the meta field when creating the text: `{ meta: { locale: "ja-JP" } }`
29
+ * - If no locale is specified, falls back to browser's locale (`navigator.language`)
30
+ * - If browser locale is not available, defaults to 'en'
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * // With specific locale
35
+ * const textJa = node.createCoValue({
36
+ * type: "coplaintext",
37
+ * ruleset: { type: "unsafeAllowAll" },
38
+ * meta: { locale: "ja-JP" },
39
+ * ...Crypto.createdNowUnique(),
40
+ * });
41
+ *
42
+ * // Using browser locale
43
+ * const text = node.createCoValue({
44
+ * type: "coplaintext",
45
+ * ruleset: { type: "unsafeAllowAll" },
46
+ * meta: null,
47
+ * ...Crypto.createdNowUnique(),
48
+ * });
49
+ * ```
50
+ */
18
51
  export class RawCoPlainText<
19
52
  Meta extends JsonObject | null = JsonObject | null,
20
53
  > extends RawCoList<string, Meta> {
@@ -36,7 +69,17 @@ export class RawCoPlainText<
36
69
  "Intl.Segmenter is not supported. Use a polyfill to get coPlainText support in Jazz. (eg. https://formatjs.github.io/docs/polyfills/intl-segmenter/)",
37
70
  );
38
71
  }
39
- this._segmenter = new Intl.Segmenter("en", {
72
+
73
+ // Use locale from meta if provided, fallback to browser locale, or 'en' as last resort
74
+ const effectiveLocale =
75
+ (core.header.meta &&
76
+ typeof core.header.meta === "object" &&
77
+ "locale" in core.header.meta
78
+ ? (core.header.meta.locale as string)
79
+ : undefined) ||
80
+ (typeof navigator !== "undefined" ? navigator.language : "en");
81
+
82
+ this._segmenter = new Intl.Segmenter(effectiveLocale, {
40
83
  granularity: "grapheme",
41
84
  });
42
85
  }
@@ -78,7 +121,16 @@ export class RawCoPlainText<
78
121
  .join("");
79
122
  }
80
123
 
81
- insertAfter(
124
+ /**
125
+ * Inserts `text` before the character at index `idx`.
126
+ * If idx is 0, inserts at the start of the text.
127
+ *
128
+ * @param idx - The index of the character to insert before
129
+ * @param text - The text to insert
130
+ * @param privacy - Whether the operation should be private or trusting
131
+ * @category 2. Editing
132
+ */
133
+ insertBefore(
82
134
  idx: number,
83
135
  text: string,
84
136
  privacy: "private" | "trusting" = "private",
@@ -86,21 +138,33 @@ export class RawCoPlainText<
86
138
  const graphemes = [...this._segmenter.segment(text)].map((g) => g.segment);
87
139
 
88
140
  if (idx === 0) {
89
- // For insertions at start, just prepend each character, in reverse
141
+ // For insertions at start, prepend each character in reverse
90
142
  for (const grapheme of graphemes.reverse()) {
91
143
  this.prepend(grapheme, 0, privacy);
92
144
  }
93
145
  } else {
94
- // For other insertions, use append after the specified index
95
- // We append in forward order to maintain the text order
96
- let after = idx - 1;
97
- for (const grapheme of graphemes) {
98
- this.append(grapheme, after, privacy);
99
- after++; // Move the insertion point forward for each grapheme
100
- }
146
+ // For other insertions, append after the previous character
147
+ this.appendItems(graphemes, idx - 1, privacy);
101
148
  }
102
149
  }
103
150
 
151
+ /**
152
+ * Inserts `text` after the character at index `idx`.
153
+ *
154
+ * @param idx - The index of the character to insert after
155
+ * @param text - The text to insert
156
+ * @param privacy - Whether the operation should be private or trusting
157
+ * @category 2. Editing
158
+ */
159
+ insertAfter(
160
+ idx: number,
161
+ text: string,
162
+ privacy: "private" | "trusting" = "private",
163
+ ) {
164
+ const graphemes = [...this._segmenter.segment(text)].map((g) => g.segment);
165
+ this.appendItems(graphemes, idx, privacy);
166
+ }
167
+
104
168
  deleteRange(
105
169
  { from, to }: { from: number; to: number },
106
170
  privacy: "private" | "trusting" = "private",
@@ -123,6 +187,6 @@ export class RawCoPlainText<
123
187
  }
124
188
  this.core.makeTransaction(ops, privacy);
125
189
 
126
- this.rebuildFromCore();
190
+ this.processNewTransactions();
127
191
  }
128
192
  }
@@ -186,8 +186,8 @@ export class RawGroup<
186
186
  const child = store.get(id);
187
187
 
188
188
  if (
189
- child.state.type === "unknown" ||
190
- child.state.type === "unavailable"
189
+ child.highLevelState === "unknown" ||
190
+ child.highLevelState === "unavailable"
191
191
  ) {
192
192
  child.loadFromPeers(peers).catch(() => {
193
193
  logger.error(`Failed to load child group ${id}`);
package/src/exports.ts CHANGED
@@ -79,8 +79,6 @@ type Value = JsonValue | AnyRawCoValue;
79
79
  import { CO_VALUE_LOADING_CONFIG } from "./coValueState.js";
80
80
  import { logger } from "./logger.js";
81
81
  import { getPriorityFromHeader } from "./priority.js";
82
- import { FileSystem } from "./storage/FileSystem.js";
83
- import { BlockFilename, LSMStorage, WalFilename } from "./storage/index.js";
84
82
 
85
83
  /** @hidden */
86
84
  export const cojsonInternals = {
@@ -143,7 +141,6 @@ export {
143
141
  CryptoProvider,
144
142
  SyncMessage,
145
143
  isRawCoID,
146
- LSMStorage,
147
144
  emptyKnownState,
148
145
  RawCoPlainText,
149
146
  stringifyOpID,
@@ -154,9 +151,6 @@ export {
154
151
 
155
152
  export type {
156
153
  Value,
157
- FileSystem,
158
- BlockFilename,
159
- WalFilename,
160
154
  IncomingSyncStream,
161
155
  OutgoingSyncQueue,
162
156
  DisconnectedError,
package/src/localNode.ts CHANGED
@@ -126,7 +126,7 @@ export class LocalNode {
126
126
  );
127
127
 
128
128
  nodeWithAccount.account = controlledAccount;
129
- nodeWithAccount.coValuesStore.setAsAvailable(
129
+ nodeWithAccount.coValuesStore.internalMarkMagicallyAvailable(
130
130
  controlledAccount.id,
131
131
  controlledAccount.core,
132
132
  );
@@ -139,10 +139,8 @@ export class LocalNode {
139
139
  // we shouldn't need this, but it fixes account data not syncing for new accounts
140
140
  function syncAllCoValuesAfterCreateAccount() {
141
141
  for (const coValueEntry of nodeWithAccount.coValuesStore.getValues()) {
142
- if (coValueEntry.state.type === "available") {
143
- void nodeWithAccount.syncManager.syncCoValue(
144
- coValueEntry.state.coValue,
145
- );
142
+ if (coValueEntry.isAvailable()) {
143
+ void nodeWithAccount.syncManager.syncCoValue(coValueEntry.core);
146
144
  }
147
145
  }
148
146
  }
@@ -208,7 +206,10 @@ export class LocalNode {
208
206
  node.syncManager.local = node;
209
207
 
210
208
  controlledAccount.core.node = node;
211
- node.coValuesStore.setAsAvailable(accountID, controlledAccount.core);
209
+ node.coValuesStore.internalMarkMagicallyAvailable(
210
+ accountID,
211
+ controlledAccount.core,
212
+ );
212
213
  controlledAccount.core._cachedContent = undefined;
213
214
 
214
215
  const profileID = account.get("profile");
@@ -245,7 +246,7 @@ export class LocalNode {
245
246
  }
246
247
 
247
248
  const coValue = new CoValueCore(header, this);
248
- this.coValuesStore.setAsAvailable(coValue.id, coValue);
249
+ this.coValuesStore.internalMarkMagicallyAvailable(coValue.id, coValue);
249
250
 
250
251
  void this.syncManager.syncCoValue(coValue);
251
252
 
@@ -265,10 +266,17 @@ export class LocalNode {
265
266
 
266
267
  const entry = this.coValuesStore.get(id);
267
268
 
268
- if (entry.state.type === "unknown" || entry.state.type === "unavailable") {
269
+ if (
270
+ entry.highLevelState === "unknown" ||
271
+ entry.highLevelState === "unavailable"
272
+ ) {
269
273
  const peers =
270
274
  this.syncManager.getServerAndStoragePeers(skipLoadingFromPeer);
271
275
 
276
+ if (peers.length === 0) {
277
+ return "unavailable";
278
+ }
279
+
272
280
  await entry.loadFromPeers(peers).catch((e) => {
273
281
  logger.error("Error loading from peers", {
274
282
  id,
@@ -309,8 +317,8 @@ export class LocalNode {
309
317
  getLoaded<T extends RawCoValue>(id: CoID<T>): T | undefined {
310
318
  const entry = this.coValuesStore.get(id);
311
319
 
312
- if (entry.state.type === "available") {
313
- return entry.state.coValue.getCurrentContent() as T;
320
+ if (entry.isAvailable()) {
321
+ return entry.core.getCurrentContent() as T;
314
322
  }
315
323
 
316
324
  return undefined;
@@ -439,12 +447,12 @@ export class LocalNode {
439
447
  expectCoValueLoaded(id: RawCoID, expectation?: string): CoValueCore {
440
448
  const entry = this.coValuesStore.get(id);
441
449
 
442
- if (entry.state.type !== "available") {
450
+ if (!entry.isAvailable()) {
443
451
  throw new Error(
444
- `${expectation ? expectation + ": " : ""}CoValue ${id} not yet loaded. Current state: ${entry.state.type}`,
452
+ `${expectation ? expectation + ": " : ""}CoValue ${id} not yet loaded. Current state: ${JSON.stringify(entry)}`,
445
453
  );
446
454
  }
447
- return entry.state.coValue;
455
+ return entry.core;
448
456
  }
449
457
 
450
458
  /** @internal */
@@ -638,15 +646,13 @@ export class LocalNode {
638
646
  while (coValuesToCopy.length > 0) {
639
647
  const [coValueID, entry] = coValuesToCopy[coValuesToCopy.length - 1]!;
640
648
 
641
- if (entry.state.type !== "available") {
649
+ if (!entry.isAvailable()) {
642
650
  coValuesToCopy.pop();
643
651
  continue;
644
652
  } else {
645
- const allDepsCopied = entry.state.coValue
653
+ const allDepsCopied = entry.core
646
654
  .getDependedOnCoValues()
647
- .every(
648
- (dep) => newNode.coValuesStore.get(dep).state.type === "available",
649
- );
655
+ .every((dep) => newNode.coValuesStore.get(dep).isAvailable());
650
656
 
651
657
  if (!allDepsCopied) {
652
658
  // move to end of queue
@@ -655,12 +661,15 @@ export class LocalNode {
655
661
  }
656
662
 
657
663
  const newCoValue = new CoValueCore(
658
- entry.state.coValue.header,
664
+ entry.core.header,
659
665
  newNode,
660
- new Map(entry.state.coValue.sessionLogs),
666
+ new Map(entry.core.sessionLogs),
661
667
  );
662
668
 
663
- newNode.coValuesStore.setAsAvailable(coValueID, newCoValue);
669
+ newNode.coValuesStore.internalMarkMagicallyAvailable(
670
+ coValueID,
671
+ newCoValue,
672
+ );
664
673
 
665
674
  coValuesToCopy.pop();
666
675
  }