cojson 0.18.26 → 0.18.27

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 (126) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +7 -0
  3. package/dist/PeerKnownStates.d.ts +4 -3
  4. package/dist/PeerKnownStates.d.ts.map +1 -1
  5. package/dist/PeerKnownStates.js +27 -18
  6. package/dist/PeerKnownStates.js.map +1 -1
  7. package/dist/PeerState.d.ts +3 -2
  8. package/dist/PeerState.d.ts.map +1 -1
  9. package/dist/PeerState.js.map +1 -1
  10. package/dist/SyncStateManager.d.ts +2 -2
  11. package/dist/SyncStateManager.d.ts.map +1 -1
  12. package/dist/SyncStateManager.js +2 -10
  13. package/dist/SyncStateManager.js.map +1 -1
  14. package/dist/coValueContentMessage.d.ts +1 -1
  15. package/dist/coValueContentMessage.d.ts.map +1 -1
  16. package/dist/coValueContentMessage.js +1 -1
  17. package/dist/coValueContentMessage.js.map +1 -1
  18. package/dist/coValueCore/SessionMap.d.ts +6 -3
  19. package/dist/coValueCore/SessionMap.d.ts.map +1 -1
  20. package/dist/coValueCore/SessionMap.js +41 -8
  21. package/dist/coValueCore/SessionMap.js.map +1 -1
  22. package/dist/coValueCore/branching.d.ts +3 -3
  23. package/dist/coValueCore/branching.d.ts.map +1 -1
  24. package/dist/coValueCore/branching.js +2 -2
  25. package/dist/coValueCore/branching.js.map +1 -1
  26. package/dist/coValueCore/coValueCore.d.ts +3 -2
  27. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  28. package/dist/coValueCore/coValueCore.js +2 -3
  29. package/dist/coValueCore/coValueCore.js.map +1 -1
  30. package/dist/coValueCore/verifiedState.d.ts +12 -6
  31. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  32. package/dist/coValueCore/verifiedState.js +28 -56
  33. package/dist/coValueCore/verifiedState.js.map +1 -1
  34. package/dist/coValues/coMap.d.ts.map +1 -1
  35. package/dist/coValues/coMap.js.map +1 -1
  36. package/dist/coValues/coStream.d.ts.map +1 -1
  37. package/dist/coValues/coStream.js.map +1 -1
  38. package/dist/exports.d.ts +3 -2
  39. package/dist/exports.d.ts.map +1 -1
  40. package/dist/exports.js +2 -1
  41. package/dist/exports.js.map +1 -1
  42. package/dist/knownState.d.ts +58 -2
  43. package/dist/knownState.d.ts.map +1 -1
  44. package/dist/knownState.js +79 -5
  45. package/dist/knownState.js.map +1 -1
  46. package/dist/permissions.d.ts.map +1 -1
  47. package/dist/permissions.js.map +1 -1
  48. package/dist/storage/knownState.d.ts +1 -1
  49. package/dist/storage/knownState.d.ts.map +1 -1
  50. package/dist/storage/knownState.js +2 -3
  51. package/dist/storage/knownState.js.map +1 -1
  52. package/dist/storage/storageAsync.d.ts +2 -1
  53. package/dist/storage/storageAsync.d.ts.map +1 -1
  54. package/dist/storage/storageAsync.js +5 -6
  55. package/dist/storage/storageAsync.js.map +1 -1
  56. package/dist/storage/storageSync.d.ts +2 -1
  57. package/dist/storage/storageSync.d.ts.map +1 -1
  58. package/dist/storage/storageSync.js +5 -5
  59. package/dist/storage/storageSync.js.map +1 -1
  60. package/dist/storage/types.d.ts +2 -1
  61. package/dist/storage/types.d.ts.map +1 -1
  62. package/dist/sync.d.ts +2 -12
  63. package/dist/sync.d.ts.map +1 -1
  64. package/dist/sync.js +7 -36
  65. package/dist/sync.js.map +1 -1
  66. package/dist/tests/PeerKnownStates.test.js +1 -1
  67. package/dist/tests/PeerKnownStates.test.js.map +1 -1
  68. package/dist/tests/PeerState.test.js +19 -0
  69. package/dist/tests/PeerState.test.js.map +1 -1
  70. package/dist/tests/PureJSCrypto.test.js.map +1 -1
  71. package/dist/tests/StorageApiAsync.test.js +1 -1
  72. package/dist/tests/StorageApiAsync.test.js.map +1 -1
  73. package/dist/tests/StorageApiSync.test.js +1 -2
  74. package/dist/tests/StorageApiSync.test.js.map +1 -1
  75. package/dist/tests/StoreQueue.test.js.map +1 -1
  76. package/dist/tests/SyncStateManager.test.js +1 -1
  77. package/dist/tests/SyncStateManager.test.js.map +1 -1
  78. package/dist/tests/coValueContentMessage.test.js +1 -1
  79. package/dist/tests/coValueContentMessage.test.js.map +1 -1
  80. package/dist/tests/coValueCore.test.js +28 -0
  81. package/dist/tests/coValueCore.test.js.map +1 -1
  82. package/dist/tests/knownState.test.d.ts +2 -0
  83. package/dist/tests/knownState.test.d.ts.map +1 -0
  84. package/dist/tests/knownState.test.js +510 -0
  85. package/dist/tests/knownState.test.js.map +1 -0
  86. package/dist/tests/messagesTestUtils.d.ts.map +1 -1
  87. package/dist/tests/messagesTestUtils.js.map +1 -1
  88. package/dist/tests/priority.test.js.map +1 -1
  89. package/dist/tests/sync.storage.test.js.map +1 -1
  90. package/dist/tests/sync.upload.test.js.map +1 -1
  91. package/dist/tests/testUtils.d.ts.map +1 -1
  92. package/dist/tests/testUtils.js.map +1 -1
  93. package/package.json +3 -3
  94. package/src/PeerKnownStates.ts +36 -22
  95. package/src/PeerState.ts +2 -1
  96. package/src/SyncStateManager.ts +4 -17
  97. package/src/coValueContentMessage.ts +2 -1
  98. package/src/coValueCore/SessionMap.ts +66 -11
  99. package/src/coValueCore/branching.ts +9 -10
  100. package/src/coValueCore/coValueCore.ts +9 -10
  101. package/src/coValueCore/verifiedState.ts +32 -64
  102. package/src/coValues/coMap.ts +1 -5
  103. package/src/coValues/coStream.ts +1 -5
  104. package/src/exports.ts +2 -2
  105. package/src/knownState.ts +118 -12
  106. package/src/permissions.ts +0 -3
  107. package/src/storage/knownState.ts +9 -3
  108. package/src/storage/storageAsync.ts +15 -7
  109. package/src/storage/storageSync.ts +16 -8
  110. package/src/storage/types.ts +2 -1
  111. package/src/sync.ts +12 -58
  112. package/src/tests/PeerKnownStates.test.ts +1 -1
  113. package/src/tests/PeerState.test.ts +29 -3
  114. package/src/tests/PureJSCrypto.test.ts +0 -1
  115. package/src/tests/StorageApiAsync.test.ts +3 -6
  116. package/src/tests/StorageApiSync.test.ts +2 -6
  117. package/src/tests/StoreQueue.test.ts +3 -2
  118. package/src/tests/SyncStateManager.test.ts +1 -1
  119. package/src/tests/coValueContentMessage.test.ts +2 -2
  120. package/src/tests/coValueCore.test.ts +38 -0
  121. package/src/tests/knownState.test.ts +665 -0
  122. package/src/tests/messagesTestUtils.ts +2 -1
  123. package/src/tests/priority.test.ts +0 -2
  124. package/src/tests/sync.storage.test.ts +0 -1
  125. package/src/tests/sync.upload.test.ts +0 -1
  126. package/src/tests/testUtils.ts +1 -2
@@ -17,11 +17,11 @@ import { RawCoID, SessionID, TransactionID } from "../ids.js";
17
17
  import { Stringified } from "../jsonStringify.js";
18
18
  import { JsonObject, JsonValue } from "../jsonValue.js";
19
19
  import { PermissionsDef as RulesetDef } from "../permissions.js";
20
- import { CoValueKnownState, NewContentMessage } from "../sync.js";
20
+ import { NewContentMessage } from "../sync.js";
21
21
  import { TryAddTransactionsError } from "./coValueCore.js";
22
22
  import { SessionLog, SessionMap } from "./SessionMap.js";
23
23
  import { ControlledAccountOrAgent } from "../coValues/account.js";
24
- import { logger } from "../logger.js";
24
+ import { cloneKnownState, CoValueKnownState } from "../knownState.js";
25
25
 
26
26
  export type CoValueHeader = {
27
27
  type: AnyRawCoValue["type"];
@@ -56,8 +56,8 @@ export class VerifiedState {
56
56
  readonly header: CoValueHeader;
57
57
  readonly sessions: SessionMap;
58
58
  private _cachedKnownState?: CoValueKnownState;
59
+ private _cachedKnownStateWithStreaming?: CoValueKnownState;
59
60
  private _cachedNewContentSinceEmpty: NewContentMessage[] | undefined;
60
- private streamingKnownState?: CoValueKnownState["sessions"];
61
61
  public lastAccessed: number | undefined;
62
62
  public branchSourceId?: RawCoID;
63
63
  public branchName?: string;
@@ -67,15 +67,11 @@ export class VerifiedState {
67
67
  crypto: CryptoProvider,
68
68
  header: CoValueHeader,
69
69
  sessions?: SessionMap,
70
- streamingKnownState?: CoValueKnownState["sessions"],
71
70
  ) {
72
71
  this.id = id;
73
72
  this.crypto = crypto;
74
73
  this.header = header;
75
74
  this.sessions = sessions ?? new SessionMap(id, crypto);
76
- this.streamingKnownState = streamingKnownState
77
- ? { ...streamingKnownState }
78
- : undefined;
79
75
  this.branchSourceId = header.meta?.source as RawCoID | undefined;
80
76
  this.branchName = header.meta?.branch as string | undefined;
81
77
  }
@@ -86,7 +82,6 @@ export class VerifiedState {
86
82
  this.crypto,
87
83
  this.header,
88
84
  this.sessions.clone(),
89
- this.streamingKnownState ? { ...this.streamingKnownState } : undefined,
90
85
  );
91
86
  }
92
87
 
@@ -108,6 +103,7 @@ export class VerifiedState {
108
103
  if (result.isOk()) {
109
104
  this._cachedNewContentSinceEmpty = undefined;
110
105
  this._cachedKnownState = undefined;
106
+ this._cachedKnownStateWithStreaming = undefined;
111
107
  }
112
108
 
113
109
  return result;
@@ -130,6 +126,7 @@ export class VerifiedState {
130
126
 
131
127
  this._cachedNewContentSinceEmpty = undefined;
132
128
  this._cachedKnownState = undefined;
129
+ this._cachedKnownStateWithStreaming = undefined;
133
130
 
134
131
  return result;
135
132
  }
@@ -155,6 +152,7 @@ export class VerifiedState {
155
152
 
156
153
  this._cachedNewContentSinceEmpty = undefined;
157
154
  this._cachedKnownState = undefined;
155
+ this._cachedKnownStateWithStreaming = undefined;
158
156
 
159
157
  return result;
160
158
  }
@@ -289,72 +287,42 @@ export class VerifiedState {
289
287
  }
290
288
 
291
289
  /**
292
- * Returns the known state considering the known state of the streaming source
290
+ * Returns the known state of the CoValue
293
291
  *
294
- * Used to correctly manage the content & subscriptions during the content streaming process
292
+ * The return value identity is going to be stable as long as the CoValue is not modified.
293
+ *
294
+ * On change the knownState is invalidated and a new object is returned.
295
295
  */
296
- knownStateWithStreaming(): CoValueKnownState {
297
- const knownState = this.knownState();
298
-
299
- if (this.streamingKnownState) {
300
- const newSessions: CoValueKnownState["sessions"] = {};
301
- const entries = Object.entries(this.streamingKnownState);
302
-
303
- for (const [sessionID, txs] of entries) {
304
- newSessions[sessionID as SessionID] = txs;
305
- if ((knownState.sessions[sessionID as SessionID] ?? 0) < txs) {
306
- newSessions[sessionID as SessionID] = txs;
307
- } else {
308
- newSessions[sessionID as SessionID] = txs;
309
- delete this.streamingKnownState[sessionID as SessionID];
310
- }
311
- }
312
-
313
- if (Object.keys(this.streamingKnownState).length === 0) {
314
- this.streamingKnownState = undefined;
315
- return knownState;
316
- } else {
317
- return {
318
- id: knownState.id,
319
- header: knownState.header,
320
- sessions: newSessions,
321
- };
322
- }
323
- }
324
-
325
- return knownState;
326
- }
327
-
328
- isStreaming(): boolean {
329
- // Call knownStateWithStreaming to delete the streamingKnownState when it matches the current knownState
330
- this.knownStateWithStreaming();
331
-
332
- return this.streamingKnownState !== undefined;
333
- }
334
-
335
- knownState(): CoValueKnownState {
296
+ knownState() {
336
297
  if (this._cachedKnownState) {
337
298
  return this._cachedKnownState;
338
- } else {
339
- const knownState = this.knownStateUncached();
340
- this._cachedKnownState = knownState;
341
- return knownState;
342
299
  }
300
+ this._cachedKnownState = cloneKnownState(this.sessions.knownState);
301
+ return this._cachedKnownState;
343
302
  }
344
303
 
345
- /** @internal */
346
- knownStateUncached(): CoValueKnownState {
347
- const sessions: CoValueKnownState["sessions"] = {};
304
+ /**
305
+ * Returns the known state considering the known state of the streaming source
306
+ *
307
+ * Used to correctly manage the content & subscriptions during the content streaming process
308
+ */
309
+ knownStateWithStreaming() {
310
+ if (!this.sessions.knownStateWithStreaming) {
311
+ return this.knownState();
312
+ }
348
313
 
349
- for (const [sessionID, sessionLog] of this.sessions.entries()) {
350
- sessions[sessionID] = sessionLog.transactions.length;
314
+ if (this._cachedKnownStateWithStreaming) {
315
+ return this._cachedKnownStateWithStreaming;
351
316
  }
317
+ this._cachedKnownStateWithStreaming = cloneKnownState(
318
+ this.sessions.knownStateWithStreaming,
319
+ );
352
320
 
353
- return {
354
- id: this.id,
355
- header: true,
356
- sessions,
357
- };
321
+ return this._cachedKnownStateWithStreaming;
322
+ }
323
+
324
+ isStreaming(): boolean {
325
+ return Boolean(this.sessions.knownStateWithStreaming);
358
326
  }
359
327
 
360
328
  decryptTransaction(
@@ -1,11 +1,7 @@
1
1
  import { CoID, RawCoValue } from "../coValue.js";
2
- import {
3
- AvailableCoValueCore,
4
- CoValueCore,
5
- } from "../coValueCore/coValueCore.js";
2
+ import { AvailableCoValueCore } from "../coValueCore/coValueCore.js";
6
3
  import { AgentID, TransactionID } from "../ids.js";
7
4
  import { JsonObject, JsonValue } from "../jsonValue.js";
8
- import { CoValueKnownState } from "../sync.js";
9
5
  import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
10
6
  import { isCoValue } from "../typeUtils/isCoValue.js";
11
7
  import { RawAccountID } from "./account.js";
@@ -1,13 +1,9 @@
1
1
  import { base64URLtoBytes, bytesToBase64url } from "../base64url.js";
2
2
  import { CoID, RawCoValue } from "../coValue.js";
3
- import {
4
- AvailableCoValueCore,
5
- CoValueCore,
6
- } from "../coValueCore/coValueCore.js";
3
+ import { AvailableCoValueCore } from "../coValueCore/coValueCore.js";
7
4
  import { AgentID, SessionID, TransactionID } from "../ids.js";
8
5
  import { JsonObject, JsonValue } from "../jsonValue.js";
9
6
  import { logger } from "../logger.js";
10
- import { CoValueKnownState } from "../sync.js";
11
7
  import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
12
8
  import { isAccountID } from "../typeUtils/isAccountID.js";
13
9
  import { isCoValue } from "../typeUtils/isCoValue.js";
package/src/exports.ts CHANGED
@@ -65,9 +65,9 @@ import type { Peer, SyncMessage } from "./sync.js";
65
65
  import {
66
66
  DisconnectedError,
67
67
  SyncManager,
68
- emptyKnownState,
69
68
  hwrServerPeerSelector,
70
69
  } from "./sync.js";
70
+ import { emptyKnownState } from "./knownState.js";
71
71
 
72
72
  import {
73
73
  getContentMessageSize,
@@ -189,7 +189,7 @@ export * from "./storage/index.js";
189
189
  // biome-ignore format: off
190
190
  // eslint-disable-next-line @typescript-eslint/no-namespace
191
191
  export namespace CojsonInternalTypes {
192
- export type CoValueKnownState = import("./sync.js").CoValueKnownState;
192
+ export type CoValueKnownState = import("./knownState.js").CoValueKnownState;
193
193
  export type CoJsonValue<T> = import("./jsonValue.js").CoJsonValue<T>;
194
194
  export type DoneMessage = import("./sync.js").DoneMessage;
195
195
  export type Encrypted<T extends JsonValue, N extends JsonValue> = import("./crypto/crypto.js").Encrypted<T, N>;
package/src/knownState.ts CHANGED
@@ -1,17 +1,123 @@
1
- import type { SessionID } from "./exports.js";
2
- import type { CoValueKnownState } from "./sync.js";
1
+ import type { RawCoID, SessionID } from "./exports.js";
3
2
 
3
+ export type KnownStateSessions = { [sessionID: SessionID]: number };
4
+
5
+ export type CoValueKnownState = {
6
+ id: RawCoID;
7
+ header: boolean;
8
+ sessions: KnownStateSessions;
9
+ };
10
+
11
+ /**
12
+ * Returns an empty known state for a CoValue, with no header and empty sessions.
13
+ */
14
+ export function emptyKnownState(id: RawCoID): CoValueKnownState {
15
+ return {
16
+ id,
17
+ header: false,
18
+ sessions: {},
19
+ };
20
+ }
21
+
22
+ /**
23
+ * Picks the knownState properties from the input object
24
+ */
25
+ export function knownStateFrom(input: CoValueKnownState) {
26
+ return {
27
+ id: input.id,
28
+ header: input.header,
29
+ sessions: input.sessions,
30
+ };
31
+ }
32
+
33
+ /**
34
+ * Mutate the target known state by combining the sessions from the source.
35
+ *
36
+ * The function assigns the sessions to the target only when the value in the source is greater.
37
+ */
38
+ export function combineKnownStates(
39
+ target: CoValueKnownState,
40
+ source: CoValueKnownState,
41
+ ): CoValueKnownState {
42
+ combineKnownStateSessions(target.sessions, source.sessions);
43
+
44
+ if (source.header && !target.header) {
45
+ target.header = true;
46
+ }
47
+
48
+ return target;
49
+ }
50
+
51
+ /**
52
+ * Mutate the target sessions counter by combining the entries from the source.
53
+ *
54
+ * The function assigns the sessions to the target only when the value in the source is greater.
55
+ */
4
56
  export function combineKnownStateSessions(
5
- a: CoValueKnownState["sessions"],
6
- b: CoValueKnownState["sessions"],
7
- ): CoValueKnownState["sessions"] {
8
- const sessionStates: CoValueKnownState["sessions"] = {};
9
-
10
- for (const sessionID of Object.keys(a).concat(
11
- Object.keys(b),
12
- ) as SessionID[]) {
13
- sessionStates[sessionID] = Math.max(a[sessionID] || 0, b[sessionID] || 0);
57
+ target: KnownStateSessions,
58
+ source: KnownStateSessions,
59
+ ) {
60
+ for (const [sessionID, count] of Object.entries(source) as [
61
+ SessionID,
62
+ number,
63
+ ][]) {
64
+ const currentCount = target[sessionID] || 0;
65
+
66
+ if (count > currentCount) {
67
+ target[sessionID] = count;
68
+ }
69
+ }
70
+
71
+ return target;
72
+ }
73
+
74
+ /**
75
+ * Set the session counter for a sessionId in the known state.
76
+ */
77
+ export function setSessionCounter(
78
+ knownState: KnownStateSessions,
79
+ sessionId: SessionID,
80
+ value: number,
81
+ ) {
82
+ knownState[sessionId] = value;
83
+ }
84
+
85
+ /**
86
+ * Update the session counter for a sessionId in the known state.
87
+ *
88
+ * The function assigns the value to the target only when the value in the knownState is less than the provided value.
89
+ */
90
+ export function updateSessionCounter(
91
+ knownState: KnownStateSessions,
92
+ sessionId: SessionID,
93
+ value: number,
94
+ ) {
95
+ knownState[sessionId] = Math.max(knownState[sessionId] || 0, value);
96
+ }
97
+
98
+ /**
99
+ * Efficient cloning of a known state.
100
+ */
101
+ export function cloneKnownState(knownState: CoValueKnownState) {
102
+ return {
103
+ id: knownState.id,
104
+ header: knownState.header,
105
+ sessions: { ...knownState.sessions },
106
+ };
107
+ }
108
+
109
+ /**
110
+ * Checks if all the local sessions have the same counters as in remote.
111
+ */
112
+ export function areLocalSessionsUploaded(
113
+ local: Record<string, number>,
114
+ remote: Record<string, number>,
115
+ ) {
116
+ for (const sessionId of Object.keys(local)) {
117
+ if (local[sessionId] !== remote[sessionId]) {
118
+ return false;
119
+ }
14
120
  }
15
121
 
16
- return sessionStates;
122
+ return true;
17
123
  }
@@ -15,15 +15,12 @@ import {
15
15
  AgentID,
16
16
  ParentGroupReference,
17
17
  RawCoID,
18
- SessionID,
19
18
  TransactionID,
20
19
  getParentGroupId,
21
20
  } from "./ids.js";
22
21
  import { parseJSON } from "./jsonStringify.js";
23
22
  import { JsonValue } from "./jsonValue.js";
24
23
  import { logger } from "./logger.js";
25
- import { CoValueKnownState } from "./sync.js";
26
- import { accountOrAgentIDfromSessionID } from "./typeUtils/accountOrAgentIDfromSessionID.js";
27
24
  import { expectGroup } from "./typeUtils/expectGroup.js";
28
25
 
29
26
  export type PermissionsDef =
@@ -1,7 +1,10 @@
1
- import { getIsUploaded } from "../SyncStateManager.js";
2
1
  import { type CoValueCore } from "../exports.js";
3
2
  import { RawCoID } from "../ids.js";
4
- import { CoValueKnownState, emptyKnownState } from "../sync.js";
3
+ import {
4
+ CoValueKnownState,
5
+ emptyKnownState,
6
+ areLocalSessionsUploaded,
7
+ } from "../knownState.js";
5
8
 
6
9
  /**
7
10
  * Track how much data we have stored inside our storage
@@ -84,5 +87,8 @@ function isInSync(
84
87
  return false;
85
88
  }
86
89
 
87
- return getIsUploaded(knownState.sessions, knownStateFromStorage.sessions);
90
+ return areLocalSessionsUploaded(
91
+ knownState.sessions,
92
+ knownStateFromStorage.sessions,
93
+ );
88
94
  }
@@ -10,11 +10,12 @@ import {
10
10
  logger,
11
11
  } from "../exports.js";
12
12
  import { StoreQueue } from "../queue/StoreQueue.js";
13
+ import { NewContentMessage } from "../sync.js";
13
14
  import {
14
15
  CoValueKnownState,
15
- NewContentMessage,
16
16
  emptyKnownState,
17
- } from "../sync.js";
17
+ setSessionCounter,
18
+ } from "../knownState.js";
18
19
  import { StorageKnownState } from "./knownState.js";
19
20
  import {
20
21
  collectNewTxs,
@@ -93,7 +94,11 @@ export class StorageApiAsync implements StorageAPI {
93
94
  knownState.header = true;
94
95
 
95
96
  for (const sessionRow of allCoValueSessions) {
96
- knownState.sessions[sessionRow.sessionID] = sessionRow.lastIdx;
97
+ setSessionCounter(
98
+ knownState.sessions,
99
+ sessionRow.sessionID,
100
+ sessionRow.lastIdx,
101
+ );
97
102
  }
98
103
 
99
104
  this.loadedCoValues.add(coValueRow.id);
@@ -101,7 +106,7 @@ export class StorageApiAsync implements StorageAPI {
101
106
  let contentMessage = createContentMessage(coValueRow.id, coValueRow.header);
102
107
 
103
108
  if (contentStreaming) {
104
- contentMessage.expectContentUntil = knownState["sessions"];
109
+ contentMessage.expectContentUntil = knownState.sessions;
105
110
  }
106
111
 
107
112
  for (const sessionRow of allCoValueSessions) {
@@ -282,14 +287,17 @@ export class StorageApiAsync implements StorageAPI {
282
287
  );
283
288
 
284
289
  if (sessionRow) {
285
- knownState.sessions[sessionRow.sessionID] = sessionRow.lastIdx;
290
+ setSessionCounter(
291
+ knownState.sessions,
292
+ sessionRow.sessionID,
293
+ sessionRow.lastIdx,
294
+ );
286
295
  }
287
296
 
288
297
  const lastIdx = sessionRow?.lastIdx || 0;
289
298
  const after = msg.new[sessionID]?.after || 0;
290
299
 
291
300
  if (lastIdx < after) {
292
- knownState.sessions[sessionID] = lastIdx;
293
301
  invalidAssumptions = true;
294
302
  } else {
295
303
  const newLastIdx = await this.putNewTxs(
@@ -298,7 +306,7 @@ export class StorageApiAsync implements StorageAPI {
298
306
  sessionRow,
299
307
  storedCoValueRowID,
300
308
  );
301
- knownState.sessions[sessionID] = newLastIdx;
309
+ setSessionCounter(knownState.sessions, sessionID, newLastIdx);
302
310
  }
303
311
  });
304
312
  }
@@ -2,7 +2,6 @@ import { UpDownCounter, metrics } from "@opentelemetry/api";
2
2
  import {
3
3
  createContentMessage,
4
4
  exceedsRecommendedSize,
5
- getTransactionSize,
6
5
  } from "../coValueContentMessage.js";
7
6
  import {
8
7
  CoValueCore,
@@ -11,12 +10,13 @@ import {
11
10
  type StorageAPI,
12
11
  logger,
13
12
  } from "../exports.js";
13
+ import { NewContentMessage } from "../sync.js";
14
+ import { StorageKnownState } from "./knownState.js";
14
15
  import {
15
16
  CoValueKnownState,
16
- NewContentMessage,
17
17
  emptyKnownState,
18
- } from "../sync.js";
19
- import { StorageKnownState } from "./knownState.js";
18
+ setSessionCounter,
19
+ } from "../knownState.js";
20
20
  import {
21
21
  collectNewTxs,
22
22
  getDependedOnCoValues,
@@ -96,7 +96,11 @@ export class StorageApiSync implements StorageAPI {
96
96
  knownState.header = true;
97
97
 
98
98
  for (const sessionRow of allCoValueSessions) {
99
- knownState.sessions[sessionRow.sessionID] = sessionRow.lastIdx;
99
+ setSessionCounter(
100
+ knownState.sessions,
101
+ sessionRow.sessionID,
102
+ sessionRow.lastIdx,
103
+ );
100
104
  }
101
105
 
102
106
  this.loadedCoValues.add(coValueRow.id);
@@ -105,7 +109,7 @@ export class StorageApiSync implements StorageAPI {
105
109
 
106
110
  if (contentStreaming) {
107
111
  this.streamingCounter.add(1);
108
- contentMessage.expectContentUntil = knownState["sessions"];
112
+ contentMessage.expectContentUntil = knownState.sessions;
109
113
  }
110
114
 
111
115
  for (const sessionRow of allCoValueSessions) {
@@ -260,7 +264,11 @@ export class StorageApiSync implements StorageAPI {
260
264
  );
261
265
 
262
266
  if (sessionRow) {
263
- knownState.sessions[sessionRow.sessionID] = sessionRow.lastIdx;
267
+ setSessionCounter(
268
+ knownState.sessions,
269
+ sessionRow.sessionID,
270
+ sessionRow.lastIdx,
271
+ );
264
272
  }
265
273
 
266
274
  if ((sessionRow?.lastIdx || 0) < (msg.new[sessionID]?.after || 0)) {
@@ -272,7 +280,7 @@ export class StorageApiSync implements StorageAPI {
272
280
  sessionRow,
273
281
  storedCoValueRowID,
274
282
  );
275
- knownState.sessions[sessionID] = newLastIdx;
283
+ setSessionCounter(knownState.sessions, sessionID, newLastIdx);
276
284
  }
277
285
  });
278
286
  }
@@ -4,7 +4,8 @@ import type {
4
4
  } from "../coValueCore/verifiedState.js";
5
5
  import { Signature } from "../crypto/crypto.js";
6
6
  import type { CoValueCore, RawCoID, SessionID } from "../exports.js";
7
- import { CoValueKnownState, NewContentMessage } from "../sync.js";
7
+ import { NewContentMessage } from "../sync.js";
8
+ import { CoValueKnownState } from "../knownState.js";
8
9
 
9
10
  export type CorrectionCallback = (
10
11
  correction: CoValueKnownState,
package/src/sync.ts CHANGED
@@ -16,20 +16,11 @@ import { logger } from "./logger.js";
16
16
  import { CoValuePriority } from "./priority.js";
17
17
  import { IncomingMessagesQueue } from "./queue/IncomingMessagesQueue.js";
18
18
  import { LocalTransactionsSyncQueue } from "./queue/LocalTransactionsSyncQueue.js";
19
-
20
- export type CoValueKnownState = {
21
- id: RawCoID;
22
- header: boolean;
23
- sessions: { [sessionID: SessionID]: number };
24
- };
25
-
26
- export function emptyKnownState(id: RawCoID): CoValueKnownState {
27
- return {
28
- id,
29
- header: false,
30
- sessions: {},
31
- };
32
- }
19
+ import {
20
+ CoValueKnownState,
21
+ knownStateFrom,
22
+ KnownStateSessions,
23
+ } from "./knownState.js";
33
24
 
34
25
  export type SyncMessage =
35
26
  | LoadMessage
@@ -55,9 +46,7 @@ export type NewContentMessage = {
55
46
  new: {
56
47
  [sessionID: SessionID]: SessionNewContent;
57
48
  };
58
- expectContentUntil?: {
59
- [sessionID: SessionID]: number;
60
- };
49
+ expectContentUntil?: KnownStateSessions;
61
50
  };
62
51
 
63
52
  export type SessionNewContent = {
@@ -97,31 +86,6 @@ export interface Peer {
97
86
  persistent?: boolean;
98
87
  }
99
88
 
100
- export function combinedKnownStates(
101
- stateA: CoValueKnownState,
102
- stateB: CoValueKnownState,
103
- ): CoValueKnownState {
104
- const sessionStates: CoValueKnownState["sessions"] = {};
105
-
106
- const allSessions = new Set([
107
- ...Object.keys(stateA.sessions),
108
- ...Object.keys(stateB.sessions),
109
- ] as SessionID[]);
110
-
111
- for (const sessionID of allSessions) {
112
- const stateAValue = stateA.sessions[sessionID];
113
- const stateBValue = stateB.sessions[sessionID];
114
-
115
- sessionStates[sessionID] = Math.max(stateAValue || 0, stateBValue || 0);
116
- }
117
-
118
- return {
119
- id: stateA.id,
120
- header: stateA.header || stateB.header,
121
- sessions: sessionStates,
122
- };
123
- }
124
-
125
89
  export type ServerPeerSelector = (
126
90
  id: RawCoID,
127
91
  serverPeers: PeerState[],
@@ -446,7 +410,7 @@ export class SyncManager {
446
410
  * This way we can track part of the data loss that may occur when the other peer is restarted
447
411
  *
448
412
  */
449
- peer.setKnownState(msg.id, knownStateIn(msg));
413
+ peer.setKnownState(msg.id, knownStateFrom(msg));
450
414
  const coValue = this.local.getCoValue(msg.id);
451
415
 
452
416
  if (coValue.isAvailable()) {
@@ -482,7 +446,7 @@ export class SyncManager {
482
446
  handleKnownState(msg: KnownStateMessage, peer: PeerState) {
483
447
  const coValue = this.local.getCoValue(msg.id);
484
448
 
485
- peer.combineWith(msg.id, knownStateIn(msg));
449
+ peer.combineWith(msg.id, knownStateFrom(msg));
486
450
 
487
451
  // The header is a boolean value that tells us if the other peer do have information about the header.
488
452
  // If it's false in this point it means that the coValue is unavailable on the other peer.
@@ -706,12 +670,10 @@ export class SyncManager {
706
670
  contentToStore.new[sessionID] = newContentForSession;
707
671
  }
708
672
 
709
- peer?.updateSessionCounter(
710
- msg.id,
711
- sessionID,
673
+ const transactionsCount =
712
674
  newContentForSession.after +
713
- newContentForSession.newTransactions.length,
714
- );
675
+ newContentForSession.newTransactions.length;
676
+ peer?.updateSessionCounter(msg.id, sessionID, transactionsCount);
715
677
  }
716
678
 
717
679
  /**
@@ -804,7 +766,7 @@ export class SyncManager {
804
766
  }
805
767
 
806
768
  handleCorrection(msg: KnownStateMessage, peer: PeerState) {
807
- peer.setKnownState(msg.id, knownStateIn(msg));
769
+ peer.setKnownState(msg.id, knownStateFrom(msg));
808
770
 
809
771
  return this.sendNewContent(msg.id, peer);
810
772
  }
@@ -952,14 +914,6 @@ export class SyncManager {
952
914
  }
953
915
  }
954
916
 
955
- function knownStateIn(msg: LoadMessage | KnownStateMessage) {
956
- return {
957
- id: msg.id,
958
- header: msg.header,
959
- sessions: msg.sessions,
960
- };
961
- }
962
-
963
917
  /**
964
918
  * Returns a ServerPeerSelector that implements the Highest Weighted Random (HWR) algorithm.
965
919
  *
@@ -1,7 +1,7 @@
1
1
  import { describe, expect, test, vi } from "vitest";
2
2
  import { PeerKnownStates } from "../PeerKnownStates.js";
3
3
  import { RawCoID, SessionID } from "../ids.js";
4
- import { CoValueKnownState, emptyKnownState } from "../sync.js";
4
+ import { CoValueKnownState, emptyKnownState } from "../knownState.js";
5
5
 
6
6
  describe("PeerKnownStates", () => {
7
7
  test("should set and get a known state", () => {