cojson 0.18.26 → 0.18.28

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 (161) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +17 -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 +5 -4
  23. package/dist/coValueCore/branching.d.ts.map +1 -1
  24. package/dist/coValueCore/branching.js +22 -4
  25. package/dist/coValueCore/branching.js.map +1 -1
  26. package/dist/coValueCore/coValueCore.d.ts +29 -25
  27. package/dist/coValueCore/coValueCore.d.ts.map +1 -1
  28. package/dist/coValueCore/coValueCore.js +163 -126
  29. package/dist/coValueCore/coValueCore.js.map +1 -1
  30. package/dist/coValueCore/decryptTransactionChangesAndMeta.d.ts +3 -0
  31. package/dist/coValueCore/decryptTransactionChangesAndMeta.d.ts.map +1 -0
  32. package/dist/coValueCore/decryptTransactionChangesAndMeta.js +34 -0
  33. package/dist/coValueCore/decryptTransactionChangesAndMeta.js.map +1 -0
  34. package/dist/coValueCore/verifiedState.d.ts +12 -6
  35. package/dist/coValueCore/verifiedState.d.ts.map +1 -1
  36. package/dist/coValueCore/verifiedState.js +28 -56
  37. package/dist/coValueCore/verifiedState.js.map +1 -1
  38. package/dist/coValues/coMap.d.ts.map +1 -1
  39. package/dist/coValues/coMap.js.map +1 -1
  40. package/dist/coValues/coStream.d.ts.map +1 -1
  41. package/dist/coValues/coStream.js.map +1 -1
  42. package/dist/exports.d.ts +3 -2
  43. package/dist/exports.d.ts.map +1 -1
  44. package/dist/exports.js +2 -1
  45. package/dist/exports.js.map +1 -1
  46. package/dist/knownState.d.ts +58 -2
  47. package/dist/knownState.d.ts.map +1 -1
  48. package/dist/knownState.js +79 -5
  49. package/dist/knownState.js.map +1 -1
  50. package/dist/localNode.js +1 -1
  51. package/dist/localNode.js.map +1 -1
  52. package/dist/permissions.d.ts.map +1 -1
  53. package/dist/permissions.js +18 -20
  54. package/dist/permissions.js.map +1 -1
  55. package/dist/storage/knownState.d.ts +1 -1
  56. package/dist/storage/knownState.d.ts.map +1 -1
  57. package/dist/storage/knownState.js +2 -3
  58. package/dist/storage/knownState.js.map +1 -1
  59. package/dist/storage/storageAsync.d.ts +2 -1
  60. package/dist/storage/storageAsync.d.ts.map +1 -1
  61. package/dist/storage/storageAsync.js +5 -6
  62. package/dist/storage/storageAsync.js.map +1 -1
  63. package/dist/storage/storageSync.d.ts +2 -1
  64. package/dist/storage/storageSync.d.ts.map +1 -1
  65. package/dist/storage/storageSync.js +5 -5
  66. package/dist/storage/storageSync.js.map +1 -1
  67. package/dist/storage/types.d.ts +2 -1
  68. package/dist/storage/types.d.ts.map +1 -1
  69. package/dist/sync.d.ts +2 -12
  70. package/dist/sync.d.ts.map +1 -1
  71. package/dist/sync.js +9 -38
  72. package/dist/sync.js.map +1 -1
  73. package/dist/tests/PeerKnownStates.test.js +1 -1
  74. package/dist/tests/PeerKnownStates.test.js.map +1 -1
  75. package/dist/tests/PeerState.test.js +19 -0
  76. package/dist/tests/PeerState.test.js.map +1 -1
  77. package/dist/tests/PureJSCrypto.test.js.map +1 -1
  78. package/dist/tests/StorageApiAsync.test.js +1 -1
  79. package/dist/tests/StorageApiAsync.test.js.map +1 -1
  80. package/dist/tests/StorageApiSync.test.js +1 -2
  81. package/dist/tests/StorageApiSync.test.js.map +1 -1
  82. package/dist/tests/StoreQueue.test.js.map +1 -1
  83. package/dist/tests/SyncStateManager.test.js +1 -1
  84. package/dist/tests/SyncStateManager.test.js.map +1 -1
  85. package/dist/tests/branching.test.js +237 -28
  86. package/dist/tests/branching.test.js.map +1 -1
  87. package/dist/tests/coValueContentMessage.test.js +1 -1
  88. package/dist/tests/coValueContentMessage.test.js.map +1 -1
  89. package/dist/tests/coValueCore.loadFromStorage.test.d.ts +2 -0
  90. package/dist/tests/coValueCore.loadFromStorage.test.d.ts.map +1 -0
  91. package/dist/tests/coValueCore.loadFromStorage.test.js +395 -0
  92. package/dist/tests/coValueCore.loadFromStorage.test.js.map +1 -0
  93. package/dist/tests/coValueCore.loadingState.test.d.ts +2 -0
  94. package/dist/tests/coValueCore.loadingState.test.d.ts.map +1 -0
  95. package/dist/tests/{coValueCoreLoadingState.test.js → coValueCore.loadingState.test.js} +4 -12
  96. package/dist/tests/coValueCore.loadingState.test.js.map +1 -0
  97. package/dist/tests/coValueCore.test.js +30 -5
  98. package/dist/tests/coValueCore.test.js.map +1 -1
  99. package/dist/tests/knownState.test.d.ts +2 -0
  100. package/dist/tests/knownState.test.d.ts.map +1 -0
  101. package/dist/tests/knownState.test.js +510 -0
  102. package/dist/tests/knownState.test.js.map +1 -0
  103. package/dist/tests/messagesTestUtils.d.ts.map +1 -1
  104. package/dist/tests/messagesTestUtils.js.map +1 -1
  105. package/dist/tests/priority.test.js.map +1 -1
  106. package/dist/tests/sync.mesh.test.js +4 -34
  107. package/dist/tests/sync.mesh.test.js.map +1 -1
  108. package/dist/tests/sync.storage.test.js.map +1 -1
  109. package/dist/tests/sync.upload.test.js.map +1 -1
  110. package/dist/tests/testUtils.d.ts +7 -1
  111. package/dist/tests/testUtils.d.ts.map +1 -1
  112. package/dist/tests/testUtils.js +12 -0
  113. package/dist/tests/testUtils.js.map +1 -1
  114. package/package.json +3 -3
  115. package/src/PeerKnownStates.ts +36 -22
  116. package/src/PeerState.ts +2 -1
  117. package/src/SyncStateManager.ts +4 -17
  118. package/src/coValueContentMessage.ts +2 -1
  119. package/src/coValueCore/SessionMap.ts +66 -11
  120. package/src/coValueCore/branching.ts +37 -13
  121. package/src/coValueCore/coValueCore.ts +224 -177
  122. package/src/coValueCore/decryptTransactionChangesAndMeta.ts +56 -0
  123. package/src/coValueCore/verifiedState.ts +32 -64
  124. package/src/coValues/coMap.ts +1 -5
  125. package/src/coValues/coStream.ts +1 -5
  126. package/src/exports.ts +2 -2
  127. package/src/knownState.ts +118 -12
  128. package/src/localNode.ts +1 -1
  129. package/src/permissions.ts +21 -22
  130. package/src/storage/knownState.ts +9 -3
  131. package/src/storage/storageAsync.ts +15 -7
  132. package/src/storage/storageSync.ts +16 -8
  133. package/src/storage/types.ts +2 -1
  134. package/src/sync.ts +14 -60
  135. package/src/tests/PeerKnownStates.test.ts +1 -1
  136. package/src/tests/PeerState.test.ts +29 -3
  137. package/src/tests/PureJSCrypto.test.ts +0 -1
  138. package/src/tests/StorageApiAsync.test.ts +3 -6
  139. package/src/tests/StorageApiSync.test.ts +2 -6
  140. package/src/tests/StoreQueue.test.ts +3 -2
  141. package/src/tests/SyncStateManager.test.ts +1 -1
  142. package/src/tests/branching.test.ts +392 -45
  143. package/src/tests/coValueContentMessage.test.ts +2 -2
  144. package/src/tests/coValueCore.loadFromStorage.test.ts +540 -0
  145. package/src/tests/{coValueCoreLoadingState.test.ts → coValueCore.loadingState.test.ts} +3 -16
  146. package/src/tests/coValueCore.test.ts +40 -5
  147. package/src/tests/knownState.test.ts +665 -0
  148. package/src/tests/messagesTestUtils.ts +2 -1
  149. package/src/tests/priority.test.ts +0 -2
  150. package/src/tests/sync.mesh.test.ts +11 -38
  151. package/src/tests/sync.storage.test.ts +0 -1
  152. package/src/tests/sync.upload.test.ts +0 -1
  153. package/src/tests/testUtils.ts +22 -2
  154. package/dist/coValueCore/decodeTransactionChangesAndMeta.d.ts +0 -3
  155. package/dist/coValueCore/decodeTransactionChangesAndMeta.d.ts.map +0 -1
  156. package/dist/coValueCore/decodeTransactionChangesAndMeta.js +0 -59
  157. package/dist/coValueCore/decodeTransactionChangesAndMeta.js.map +0 -1
  158. package/dist/tests/coValueCoreLoadingState.test.d.ts +0 -2
  159. package/dist/tests/coValueCoreLoadingState.test.d.ts.map +0 -1
  160. package/dist/tests/coValueCoreLoadingState.test.js.map +0 -1
  161. package/src/coValueCore/decodeTransactionChangesAndMeta.ts +0 -81
@@ -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
  }
package/src/localNode.ts CHANGED
@@ -116,7 +116,7 @@ export class LocalNode {
116
116
  let entry = this.coValues.get(id);
117
117
 
118
118
  if (!entry) {
119
- entry = CoValueCore.fromID(id, this);
119
+ entry = new CoValueCore(id, this);
120
120
  this.coValues.set(id, entry);
121
121
  }
122
122
 
@@ -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 =
@@ -118,9 +115,9 @@ export function determineValidTransactions(coValue: CoValueCore) {
118
115
  }
119
116
 
120
117
  tx.isValidated = true;
121
- const wasValid = tx.isValid;
122
-
123
- const groupAtTime = groupContent.atTime(tx.madeAt);
118
+ // We use the original made at to get the group at the original time when the transaction was made
119
+ // madeAt might be changed by the meta field (e.g. merged transactions), and so can't be used for permissions checks
120
+ const groupAtTime = groupContent.atTime(tx.currentMadeAt);
124
121
  const effectiveTransactor = agentInAccountOrMemberInGroup(
125
122
  tx.author,
126
123
  groupAtTime,
@@ -134,6 +131,21 @@ export function determineValidTransactions(coValue: CoValueCore) {
134
131
  const transactorRoleAtTxTime =
135
132
  groupAtTime.roleOfInternal(effectiveTransactor);
136
133
 
134
+ if (
135
+ transactorRoleAtTxTime === "reader" &&
136
+ tx.meta?.branch &&
137
+ tx.meta?.ownerId
138
+ ) {
139
+ // Force the changes and meta to only contain the branch pointer information
140
+ tx.meta = {
141
+ branch: tx.meta.branch,
142
+ ownerId: tx.meta.ownerId,
143
+ };
144
+ tx.changes = [];
145
+ tx.isValid = true;
146
+ continue;
147
+ }
148
+
137
149
  if (
138
150
  transactorRoleAtTxTime !== "admin" &&
139
151
  transactorRoleAtTxTime !== "writer" &&
@@ -220,13 +232,10 @@ function determineValidTransactionsForGroup(
220
232
  initialAdmin: RawAccountID | AgentID,
221
233
  extendChain?: Set<CoValueCore["id"]>,
222
234
  ): { memberState: MemberState } {
223
- coValue.verifiedTransactions.sort((a, b) => {
224
- return a.madeAt - b.madeAt;
225
- });
235
+ coValue.verifiedTransactions.sort(coValue.compareTransactions);
226
236
 
227
237
  const memberState: MemberState = {};
228
238
  const writeOnlyKeys: Record<RawAccountID | AgentID, KeyID> = {};
229
- const validTransactions: ValidTransactionsResult[] = [];
230
239
 
231
240
  const writeKeys = new Set<string>();
232
241
 
@@ -249,20 +258,10 @@ function determineValidTransactionsForGroup(
249
258
  }
250
259
  }
251
260
 
252
- let changes = transaction.changes;
261
+ const changes = transaction.changes;
253
262
 
254
263
  if (!changes) {
255
- try {
256
- changes = parseJSON(tx.changes);
257
- transaction.changes = changes;
258
- } catch (e) {
259
- logPermissionError("Invalid JSON in transaction", {
260
- id: coValue.id,
261
- tx,
262
- });
263
- transaction.hasInvalidChanges = true;
264
- continue;
265
- }
264
+ continue;
266
265
  }
267
266
 
268
267
  const change = changes[0] as
@@ -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,