cojson 0.13.17 → 0.13.20

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 (180) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/CHANGELOG.md +18 -0
  3. package/dist/PeerState.d.ts +4 -1
  4. package/dist/PeerState.d.ts.map +1 -1
  5. package/dist/PeerState.js +16 -36
  6. package/dist/PeerState.js.map +1 -1
  7. package/dist/SyncStateManager.d.ts.map +1 -1
  8. package/dist/SyncStateManager.js +2 -3
  9. package/dist/SyncStateManager.js.map +1 -1
  10. package/dist/coValue.d.ts +4 -4
  11. package/dist/coValue.d.ts.map +1 -1
  12. package/dist/coValue.js +4 -4
  13. package/dist/coValue.js.map +1 -1
  14. package/dist/coValueCore/coValueCore.d.ts +143 -0
  15. package/dist/coValueCore/coValueCore.d.ts.map +1 -0
  16. package/dist/{coValueCore.js → coValueCore/coValueCore.js} +325 -253
  17. package/dist/coValueCore/coValueCore.js.map +1 -0
  18. package/dist/coValueCore/verifiedState.d.ts +65 -0
  19. package/dist/coValueCore/verifiedState.d.ts.map +1 -0
  20. package/dist/coValueCore/verifiedState.js +210 -0
  21. package/dist/coValueCore/verifiedState.js.map +1 -0
  22. package/dist/coValues/account.d.ts +8 -10
  23. package/dist/coValues/account.d.ts.map +1 -1
  24. package/dist/coValues/account.js +12 -13
  25. package/dist/coValues/account.js.map +1 -1
  26. package/dist/coValues/coList.d.ts +3 -3
  27. package/dist/coValues/coList.d.ts.map +1 -1
  28. package/dist/coValues/coList.js +6 -3
  29. package/dist/coValues/coList.js.map +1 -1
  30. package/dist/coValues/coMap.d.ts +3 -3
  31. package/dist/coValues/coMap.d.ts.map +1 -1
  32. package/dist/coValues/coMap.js +3 -3
  33. package/dist/coValues/coMap.js.map +1 -1
  34. package/dist/coValues/coPlainText.d.ts +2 -2
  35. package/dist/coValues/coPlainText.d.ts.map +1 -1
  36. package/dist/coValues/coPlainText.js +4 -4
  37. package/dist/coValues/coPlainText.js.map +1 -1
  38. package/dist/coValues/coStream.d.ts +3 -3
  39. package/dist/coValues/coStream.d.ts.map +1 -1
  40. package/dist/coValues/coStream.js +3 -3
  41. package/dist/coValues/coStream.js.map +1 -1
  42. package/dist/coValues/group.d.ts +7 -2
  43. package/dist/coValues/group.d.ts.map +1 -1
  44. package/dist/coValues/group.js +29 -26
  45. package/dist/coValues/group.js.map +1 -1
  46. package/dist/coreToCoValue.d.ts +2 -2
  47. package/dist/coreToCoValue.d.ts.map +1 -1
  48. package/dist/coreToCoValue.js +10 -14
  49. package/dist/coreToCoValue.js.map +1 -1
  50. package/dist/exports.d.ts +6 -5
  51. package/dist/exports.d.ts.map +1 -1
  52. package/dist/exports.js +3 -4
  53. package/dist/exports.js.map +1 -1
  54. package/dist/localNode.d.ts +30 -24
  55. package/dist/localNode.d.ts.map +1 -1
  56. package/dist/localNode.js +147 -173
  57. package/dist/localNode.js.map +1 -1
  58. package/dist/permissions.d.ts +2 -1
  59. package/dist/permissions.d.ts.map +1 -1
  60. package/dist/permissions.js +15 -11
  61. package/dist/permissions.js.map +1 -1
  62. package/dist/priority.d.ts +1 -1
  63. package/dist/priority.d.ts.map +1 -1
  64. package/dist/streamUtils.d.ts +5 -5
  65. package/dist/streamUtils.d.ts.map +1 -1
  66. package/dist/streamUtils.js +5 -20
  67. package/dist/streamUtils.js.map +1 -1
  68. package/dist/sync.d.ts +8 -6
  69. package/dist/sync.d.ts.map +1 -1
  70. package/dist/sync.js +121 -74
  71. package/dist/sync.js.map +1 -1
  72. package/dist/tests/PeerState.test.js +0 -31
  73. package/dist/tests/PeerState.test.js.map +1 -1
  74. package/dist/tests/SyncStateManager.test.js +41 -6
  75. package/dist/tests/SyncStateManager.test.js.map +1 -1
  76. package/dist/tests/account.test.js +16 -0
  77. package/dist/tests/account.test.js.map +1 -1
  78. package/dist/tests/coList.test.js +19 -16
  79. package/dist/tests/coList.test.js.map +1 -1
  80. package/dist/tests/coMap.test.js +12 -13
  81. package/dist/tests/coMap.test.js.map +1 -1
  82. package/dist/tests/coPlainText.test.js +9 -10
  83. package/dist/tests/coPlainText.test.js.map +1 -1
  84. package/dist/tests/coStream.test.js +22 -17
  85. package/dist/tests/coStream.test.js.map +1 -1
  86. package/dist/tests/coValueCore.test.js +22 -28
  87. package/dist/tests/coValueCore.test.js.map +1 -1
  88. package/dist/tests/coValueCoreLoadingState.test.d.ts +2 -0
  89. package/dist/tests/coValueCoreLoadingState.test.d.ts.map +1 -0
  90. package/dist/tests/{coValueState.test.js → coValueCoreLoadingState.test.js} +62 -46
  91. package/dist/tests/coValueCoreLoadingState.test.js.map +1 -0
  92. package/dist/tests/group.test.js +42 -43
  93. package/dist/tests/group.test.js.map +1 -1
  94. package/dist/tests/messagesTestUtils.d.ts +2 -2
  95. package/dist/tests/messagesTestUtils.d.ts.map +1 -1
  96. package/dist/tests/messagesTestUtils.js +1 -1
  97. package/dist/tests/messagesTestUtils.js.map +1 -1
  98. package/dist/tests/permissions.test.js +224 -292
  99. package/dist/tests/permissions.test.js.map +1 -1
  100. package/dist/tests/priority.test.js +13 -14
  101. package/dist/tests/priority.test.js.map +1 -1
  102. package/dist/tests/sync.auth.test.d.ts +2 -0
  103. package/dist/tests/sync.auth.test.d.ts.map +1 -0
  104. package/dist/tests/sync.auth.test.js +190 -0
  105. package/dist/tests/sync.auth.test.js.map +1 -0
  106. package/dist/tests/sync.load.test.js +6 -6
  107. package/dist/tests/sync.load.test.js.map +1 -1
  108. package/dist/tests/sync.mesh.test.js +25 -12
  109. package/dist/tests/sync.mesh.test.js.map +1 -1
  110. package/dist/tests/sync.peerReconciliation.test.js +19 -19
  111. package/dist/tests/sync.peerReconciliation.test.js.map +1 -1
  112. package/dist/tests/sync.storage.test.js +20 -13
  113. package/dist/tests/sync.storage.test.js.map +1 -1
  114. package/dist/tests/sync.test.js +32 -39
  115. package/dist/tests/sync.test.js.map +1 -1
  116. package/dist/tests/sync.upload.test.js +126 -37
  117. package/dist/tests/sync.upload.test.js.map +1 -1
  118. package/dist/tests/testUtils.d.ts +35 -17
  119. package/dist/tests/testUtils.d.ts.map +1 -1
  120. package/dist/tests/testUtils.js +103 -79
  121. package/dist/tests/testUtils.js.map +1 -1
  122. package/dist/typeUtils/expectGroup.js +1 -1
  123. package/dist/typeUtils/expectGroup.js.map +1 -1
  124. package/package.json +1 -1
  125. package/src/PeerState.ts +19 -40
  126. package/src/SyncStateManager.ts +2 -3
  127. package/src/coValue.ts +11 -8
  128. package/src/{coValueCore.ts → coValueCore/coValueCore.ts} +478 -422
  129. package/src/coValueCore/verifiedState.ts +376 -0
  130. package/src/coValues/account.ts +20 -25
  131. package/src/coValues/coList.ts +12 -6
  132. package/src/coValues/coMap.ts +9 -6
  133. package/src/coValues/coPlainText.ts +9 -6
  134. package/src/coValues/coStream.ts +9 -6
  135. package/src/coValues/group.ts +50 -28
  136. package/src/coreToCoValue.ts +14 -15
  137. package/src/exports.ts +9 -7
  138. package/src/localNode.ts +236 -275
  139. package/src/permissions.ts +18 -12
  140. package/src/priority.ts +1 -1
  141. package/src/streamUtils.ts +7 -34
  142. package/src/sync.ts +146 -84
  143. package/src/tests/PeerState.test.ts +0 -37
  144. package/src/tests/SyncStateManager.test.ts +56 -6
  145. package/src/tests/account.test.ts +24 -0
  146. package/src/tests/coList.test.ts +21 -15
  147. package/src/tests/coMap.test.ts +12 -13
  148. package/src/tests/coPlainText.test.ts +12 -9
  149. package/src/tests/coStream.test.ts +25 -16
  150. package/src/tests/coValueCore.test.ts +30 -27
  151. package/src/tests/{coValueState.test.ts → coValueCoreLoadingState.test.ts} +67 -57
  152. package/src/tests/group.test.ts +44 -69
  153. package/src/tests/messagesTestUtils.ts +3 -8
  154. package/src/tests/permissions.test.ts +283 -449
  155. package/src/tests/priority.test.ts +17 -13
  156. package/src/tests/sync.auth.test.ts +246 -0
  157. package/src/tests/sync.load.test.ts +7 -6
  158. package/src/tests/sync.mesh.test.ts +25 -12
  159. package/src/tests/sync.peerReconciliation.test.ts +25 -25
  160. package/src/tests/sync.storage.test.ts +20 -13
  161. package/src/tests/sync.test.ts +43 -43
  162. package/src/tests/sync.upload.test.ts +157 -37
  163. package/src/tests/testUtils.ts +143 -96
  164. package/src/typeUtils/expectGroup.ts +1 -1
  165. package/dist/CoValuesStore.d.ts +0 -14
  166. package/dist/CoValuesStore.d.ts.map +0 -1
  167. package/dist/CoValuesStore.js +0 -32
  168. package/dist/CoValuesStore.js.map +0 -1
  169. package/dist/coValueCore.d.ts +0 -142
  170. package/dist/coValueCore.d.ts.map +0 -1
  171. package/dist/coValueCore.js.map +0 -1
  172. package/dist/coValueState.d.ts +0 -34
  173. package/dist/coValueState.d.ts.map +0 -1
  174. package/dist/coValueState.js +0 -190
  175. package/dist/coValueState.js.map +0 -1
  176. package/dist/tests/coValueState.test.d.ts +0 -2
  177. package/dist/tests/coValueState.test.d.ts.map +0 -1
  178. package/dist/tests/coValueState.test.js.map +0 -1
  179. package/src/CoValuesStore.ts +0 -41
  180. package/src/coValueState.ts +0 -245
@@ -0,0 +1,376 @@
1
+ import { Result, err, ok } from "neverthrow";
2
+ import { AnyRawCoValue } from "../coValue.js";
3
+ import {
4
+ CryptoProvider,
5
+ Encrypted,
6
+ Hash,
7
+ KeyID,
8
+ Signature,
9
+ SignerID,
10
+ StreamingHash,
11
+ } from "../crypto/crypto.js";
12
+ import { RawCoID, SessionID, TransactionID } from "../ids.js";
13
+ import { Stringified } from "../jsonStringify.js";
14
+ import { JsonObject, JsonValue } from "../jsonValue.js";
15
+ import { PermissionsDef as RulesetDef } from "../permissions.js";
16
+ import { getPriorityFromHeader } from "../priority.js";
17
+ import { CoValueKnownState, NewContentMessage } from "../sync.js";
18
+ import {
19
+ InvalidHashError,
20
+ InvalidSignatureError,
21
+ MAX_RECOMMENDED_TX_SIZE,
22
+ } from "./coValueCore.js";
23
+ import { TryAddTransactionsError } from "./coValueCore.js";
24
+
25
+ export type CoValueHeader = {
26
+ type: AnyRawCoValue["type"];
27
+ ruleset: RulesetDef;
28
+ meta: JsonObject | null;
29
+ } & CoValueUniqueness;
30
+
31
+ export type CoValueUniqueness = {
32
+ uniqueness: JsonValue;
33
+ createdAt?: `2${string}` | null;
34
+ };
35
+
36
+ export type PrivateTransaction = {
37
+ privacy: "private";
38
+ madeAt: number;
39
+ keyUsed: KeyID;
40
+ encryptedChanges: Encrypted<JsonValue[], { in: RawCoID; tx: TransactionID }>;
41
+ };
42
+
43
+ export type TrustingTransaction = {
44
+ privacy: "trusting";
45
+ madeAt: number;
46
+ changes: Stringified<JsonValue[]>;
47
+ };
48
+
49
+ export type Transaction = PrivateTransaction | TrustingTransaction;
50
+
51
+ type SessionLog = {
52
+ readonly transactions: Transaction[];
53
+ lastHash?: Hash;
54
+ streamingHash: StreamingHash;
55
+ readonly signatureAfter: { [txIdx: number]: Signature | undefined };
56
+ lastSignature: Signature;
57
+ };
58
+
59
+ export type ValidatedSessions = Map<SessionID, SessionLog>;
60
+
61
+ export class VerifiedState {
62
+ readonly id: RawCoID;
63
+ readonly crypto: CryptoProvider;
64
+ readonly header: CoValueHeader;
65
+ readonly sessions: ValidatedSessions;
66
+ private _cachedKnownState?: CoValueKnownState;
67
+ private _cachedNewContentSinceEmpty: NewContentMessage[] | undefined;
68
+
69
+ constructor(
70
+ id: RawCoID,
71
+ crypto: CryptoProvider,
72
+ header: CoValueHeader,
73
+ sessions: ValidatedSessions,
74
+ ) {
75
+ this.id = id;
76
+ this.crypto = crypto;
77
+ this.header = header;
78
+ this.sessions = sessions;
79
+ }
80
+
81
+ clone(): VerifiedState {
82
+ // do a deep clone, including the sessions
83
+ const clonedSessions = new Map();
84
+ for (let [sessionID, sessionLog] of this.sessions) {
85
+ clonedSessions.set(sessionID, {
86
+ lastSignature: sessionLog.lastSignature,
87
+ lastHash: sessionLog.lastHash,
88
+ streamingHash: sessionLog.streamingHash.clone(),
89
+ signatureAfter: { ...sessionLog.signatureAfter },
90
+ transactions: sessionLog.transactions.slice(),
91
+ } satisfies SessionLog);
92
+ }
93
+ return new VerifiedState(this.id, this.crypto, this.header, clonedSessions);
94
+ }
95
+
96
+ tryAddTransactions(
97
+ sessionID: SessionID,
98
+ signerID: SignerID,
99
+ newTransactions: Transaction[],
100
+ givenExpectedNewHash: Hash | undefined,
101
+ newSignature: Signature,
102
+ skipVerify: boolean = false,
103
+ givenNewStreamingHash?: StreamingHash,
104
+ ): Result<true, TryAddTransactionsError> {
105
+ if (skipVerify === true && givenNewStreamingHash && givenExpectedNewHash) {
106
+ this.doAddTransactions(
107
+ sessionID,
108
+ newTransactions,
109
+ newSignature,
110
+ givenExpectedNewHash,
111
+ givenNewStreamingHash,
112
+ );
113
+ } else {
114
+ const { expectedNewHash, newStreamingHash } = this.expectedNewHashAfter(
115
+ sessionID,
116
+ newTransactions,
117
+ );
118
+
119
+ if (givenExpectedNewHash && givenExpectedNewHash !== expectedNewHash) {
120
+ return err({
121
+ type: "InvalidHash",
122
+ id: this.id,
123
+ expectedNewHash,
124
+ givenExpectedNewHash,
125
+ } satisfies InvalidHashError);
126
+ }
127
+
128
+ if (!this.crypto.verify(newSignature, expectedNewHash, signerID)) {
129
+ return err({
130
+ type: "InvalidSignature",
131
+ id: this.id,
132
+ newSignature,
133
+ sessionID,
134
+ signerID,
135
+ } satisfies InvalidSignatureError);
136
+ }
137
+
138
+ this.doAddTransactions(
139
+ sessionID,
140
+ newTransactions,
141
+ newSignature,
142
+ expectedNewHash,
143
+ newStreamingHash,
144
+ );
145
+ }
146
+
147
+ return ok(true as const);
148
+ }
149
+
150
+ private doAddTransactions(
151
+ sessionID: SessionID,
152
+ newTransactions: Transaction[],
153
+ newSignature: Signature,
154
+ expectedNewHash: Hash,
155
+ newStreamingHash: StreamingHash,
156
+ ) {
157
+ const transactions = this.sessions.get(sessionID)?.transactions ?? [];
158
+
159
+ for (const tx of newTransactions) {
160
+ transactions.push(tx);
161
+ }
162
+
163
+ const signatureAfter = this.sessions.get(sessionID)?.signatureAfter ?? {};
164
+
165
+ const lastInbetweenSignatureIdx = Object.keys(signatureAfter).reduce(
166
+ (max, idx) => (parseInt(idx) > max ? parseInt(idx) : max),
167
+ -1,
168
+ );
169
+
170
+ const sizeOfTxsSinceLastInbetweenSignature = transactions
171
+ .slice(lastInbetweenSignatureIdx + 1)
172
+ .reduce(
173
+ (sum, tx) =>
174
+ sum +
175
+ (tx.privacy === "private"
176
+ ? tx.encryptedChanges.length
177
+ : tx.changes.length),
178
+ 0,
179
+ );
180
+
181
+ if (sizeOfTxsSinceLastInbetweenSignature > MAX_RECOMMENDED_TX_SIZE) {
182
+ signatureAfter[transactions.length - 1] = newSignature;
183
+ }
184
+
185
+ this.sessions.set(sessionID, {
186
+ transactions,
187
+ lastHash: expectedNewHash,
188
+ streamingHash: newStreamingHash,
189
+ lastSignature: newSignature,
190
+ signatureAfter: signatureAfter,
191
+ });
192
+
193
+ this._cachedNewContentSinceEmpty = undefined;
194
+ this._cachedKnownState = undefined;
195
+ }
196
+
197
+ expectedNewHashAfter(
198
+ sessionID: SessionID,
199
+ newTransactions: Transaction[],
200
+ ): { expectedNewHash: Hash; newStreamingHash: StreamingHash } {
201
+ const streamingHash =
202
+ this.sessions.get(sessionID)?.streamingHash.clone() ??
203
+ new StreamingHash(this.crypto);
204
+
205
+ for (const transaction of newTransactions) {
206
+ streamingHash.update(transaction);
207
+ }
208
+
209
+ return {
210
+ expectedNewHash: streamingHash.digest(),
211
+ newStreamingHash: streamingHash,
212
+ };
213
+ }
214
+
215
+ newContentSince(
216
+ knownState: CoValueKnownState | undefined,
217
+ ): NewContentMessage[] | undefined {
218
+ const isKnownStateEmpty = !knownState?.header && !knownState?.sessions;
219
+
220
+ if (isKnownStateEmpty && this._cachedNewContentSinceEmpty) {
221
+ return this._cachedNewContentSinceEmpty;
222
+ }
223
+
224
+ let currentPiece: NewContentMessage = {
225
+ action: "content",
226
+ id: this.id,
227
+ header: knownState?.header ? undefined : this.header,
228
+ priority: getPriorityFromHeader(this.header),
229
+ new: {},
230
+ };
231
+
232
+ const pieces = [currentPiece];
233
+
234
+ const sentState: CoValueKnownState["sessions"] = {};
235
+
236
+ let pieceSize = 0;
237
+
238
+ let sessionsTodoAgain: Set<SessionID> | undefined | "first" = "first";
239
+
240
+ while (sessionsTodoAgain === "first" || sessionsTodoAgain?.size || 0 > 0) {
241
+ if (sessionsTodoAgain === "first") {
242
+ sessionsTodoAgain = undefined;
243
+ }
244
+ const sessionsTodo = sessionsTodoAgain ?? this.sessions.keys();
245
+
246
+ for (const sessionIDKey of sessionsTodo) {
247
+ const sessionID = sessionIDKey as SessionID;
248
+ const log = this.sessions.get(sessionID)!;
249
+ const knownStateForSessionID = knownState?.sessions[sessionID];
250
+ const sentStateForSessionID = sentState[sessionID];
251
+ const nextKnownSignatureIdx = getNextKnownSignatureIdx(
252
+ log,
253
+ knownStateForSessionID,
254
+ sentStateForSessionID,
255
+ );
256
+
257
+ const firstNewTxIdx =
258
+ sentStateForSessionID ?? knownStateForSessionID ?? 0;
259
+ const afterLastNewTxIdx =
260
+ nextKnownSignatureIdx === undefined
261
+ ? log.transactions.length
262
+ : nextKnownSignatureIdx + 1;
263
+
264
+ const nNewTx = Math.max(0, afterLastNewTxIdx - firstNewTxIdx);
265
+
266
+ if (nNewTx === 0) {
267
+ sessionsTodoAgain?.delete(sessionID);
268
+ continue;
269
+ }
270
+
271
+ if (afterLastNewTxIdx < log.transactions.length) {
272
+ if (!sessionsTodoAgain) {
273
+ sessionsTodoAgain = new Set();
274
+ }
275
+ sessionsTodoAgain.add(sessionID);
276
+ }
277
+
278
+ const oldPieceSize = pieceSize;
279
+ for (let txIdx = firstNewTxIdx; txIdx < afterLastNewTxIdx; txIdx++) {
280
+ const tx = log.transactions[txIdx]!;
281
+ pieceSize +=
282
+ tx.privacy === "private"
283
+ ? tx.encryptedChanges.length
284
+ : tx.changes.length;
285
+ }
286
+
287
+ if (pieceSize >= MAX_RECOMMENDED_TX_SIZE) {
288
+ currentPiece = {
289
+ action: "content",
290
+ id: this.id,
291
+ header: undefined,
292
+ new: {},
293
+ priority: getPriorityFromHeader(this.header),
294
+ };
295
+ pieces.push(currentPiece);
296
+ pieceSize = pieceSize - oldPieceSize;
297
+ }
298
+
299
+ let sessionEntry = currentPiece.new[sessionID];
300
+ if (!sessionEntry) {
301
+ sessionEntry = {
302
+ after: sentStateForSessionID ?? knownStateForSessionID ?? 0,
303
+ newTransactions: [],
304
+ lastSignature: "WILL_BE_REPLACED" as Signature,
305
+ };
306
+ currentPiece.new[sessionID] = sessionEntry;
307
+ }
308
+
309
+ for (let txIdx = firstNewTxIdx; txIdx < afterLastNewTxIdx; txIdx++) {
310
+ const tx = log.transactions[txIdx]!;
311
+ sessionEntry.newTransactions.push(tx);
312
+ }
313
+
314
+ sessionEntry.lastSignature =
315
+ nextKnownSignatureIdx === undefined
316
+ ? log.lastSignature!
317
+ : log.signatureAfter[nextKnownSignatureIdx]!;
318
+
319
+ sentState[sessionID] =
320
+ (sentStateForSessionID ?? knownStateForSessionID ?? 0) + nNewTx;
321
+ }
322
+ }
323
+
324
+ const piecesWithContent = pieces.filter(
325
+ (piece) => Object.keys(piece.new).length > 0 || piece.header,
326
+ );
327
+
328
+ if (piecesWithContent.length === 0) {
329
+ return undefined;
330
+ }
331
+
332
+ if (isKnownStateEmpty) {
333
+ this._cachedNewContentSinceEmpty = piecesWithContent;
334
+ }
335
+
336
+ return piecesWithContent;
337
+ }
338
+
339
+ knownState(): CoValueKnownState {
340
+ if (this._cachedKnownState) {
341
+ return this._cachedKnownState;
342
+ } else {
343
+ const knownState = this.knownStateUncached();
344
+ this._cachedKnownState = knownState;
345
+ return knownState;
346
+ }
347
+ }
348
+
349
+ /** @internal */
350
+ knownStateUncached(): CoValueKnownState {
351
+ const sessions: CoValueKnownState["sessions"] = {};
352
+
353
+ for (const [sessionID, sessionLog] of this.sessions.entries()) {
354
+ sessions[sessionID] = sessionLog.transactions.length;
355
+ }
356
+
357
+ return {
358
+ id: this.id,
359
+ header: true,
360
+ sessions,
361
+ };
362
+ }
363
+ }
364
+
365
+ function getNextKnownSignatureIdx(
366
+ log: SessionLog,
367
+ knownStateForSessionID?: number,
368
+ sentStateForSessionID?: number,
369
+ ) {
370
+ return Object.keys(log.signatureAfter)
371
+ .map(Number)
372
+ .sort((a, b) => a - b)
373
+ .find(
374
+ (idx) => idx >= (sentStateForSessionID ?? knownStateForSessionID ?? -1),
375
+ );
376
+ }
@@ -1,9 +1,12 @@
1
1
  import { CoID, RawCoValue } from "../coValue.js";
2
2
  import {
3
+ AvailableCoValueCore,
3
4
  CoValueCore,
5
+ } from "../coValueCore/coValueCore.js";
6
+ import {
4
7
  CoValueHeader,
5
8
  CoValueUniqueness,
6
- } from "../coValueCore.js";
9
+ } from "../coValueCore/verifiedState.js";
7
10
  import {
8
11
  AgentSecret,
9
12
  CryptoProvider,
@@ -85,35 +88,20 @@ export interface ControlledAccountOrAgent {
85
88
  }
86
89
 
87
90
  /** @hidden */
88
- export class RawControlledAccount<Meta extends AccountMeta = AccountMeta>
89
- extends RawAccount<Meta>
90
- implements ControlledAccountOrAgent
91
- {
91
+ export class ControlledAccount implements ControlledAccountOrAgent {
92
+ account: RawAccount<AccountMeta>;
92
93
  agentSecret: AgentSecret;
94
+ _cachedCurrentAgentID: AgentID | undefined;
93
95
  crypto: CryptoProvider;
94
96
 
95
- constructor(core: CoValueCore, agentSecret: AgentSecret) {
96
- super(core);
97
-
97
+ constructor(account: RawAccount<AccountMeta>, agentSecret: AgentSecret) {
98
+ this.account = account;
98
99
  this.agentSecret = agentSecret;
99
- this.crypto = core.node.crypto;
100
- }
101
-
102
- /**
103
- * Creates a new group (with the current account as the group's first admin).
104
- * @category 1. High-level
105
- */
106
- createGroup(
107
- uniqueness: CoValueUniqueness = this.core.crypto.createdNowUnique(),
108
- ) {
109
- return this.core.node.createGroup(uniqueness);
100
+ this.crypto = account.core.node.crypto;
110
101
  }
111
102
 
112
- async acceptInvite<T extends RawCoValue>(
113
- groupOrOwnedValueID: CoID<T>,
114
- inviteSecret: InviteSecret,
115
- ): Promise<void> {
116
- return this.core.node.acceptInvite(groupOrOwnedValueID, inviteSecret);
103
+ get id(): RawAccountID {
104
+ return this.account.id;
117
105
  }
118
106
 
119
107
  currentAgentID(): AgentID {
@@ -186,7 +174,14 @@ export class RawProfile<
186
174
  > extends RawCoMap<Shape, Meta> {}
187
175
 
188
176
  export type RawAccountMigration<Meta extends AccountMeta = AccountMeta> = (
189
- account: RawControlledAccount<Meta>,
177
+ account: RawAccount<Meta>,
190
178
  localNode: LocalNode,
191
179
  creationProps?: { name: string },
192
180
  ) => void | Promise<void>;
181
+
182
+ export function expectAccount(content: RawCoValue): RawAccount {
183
+ if (!(content instanceof RawAccount)) {
184
+ throw new Error("Expected an account");
185
+ }
186
+ return content;
187
+ }
@@ -1,5 +1,8 @@
1
1
  import { CoID, RawCoValue } from "../coValue.js";
2
- import { CoValueCore } from "../coValueCore.js";
2
+ import {
3
+ AvailableCoValueCore,
4
+ CoValueCore,
5
+ } from "../coValueCore/coValueCore.js";
3
6
  import { AgentID, SessionID, TransactionID } from "../ids.js";
4
7
  import { JsonObject, JsonValue } from "../jsonValue.js";
5
8
  import { CoValueKnownState } from "../sync.js";
@@ -52,7 +55,7 @@ export class RawCoList<
52
55
  /** @category 6. Meta */
53
56
  type: "colist" | "coplaintext" = "colist" as const;
54
57
  /** @category 6. Meta */
55
- core: CoValueCore;
58
+ core: AvailableCoValueCore;
56
59
  /** @internal */
57
60
  afterStart: OpID[];
58
61
  /** @internal */
@@ -88,7 +91,7 @@ export class RawCoList<
88
91
  lastValidTransaction: number | undefined;
89
92
 
90
93
  /** @internal */
91
- constructor(core: CoValueCore) {
94
+ constructor(core: AvailableCoValueCore) {
92
95
  this.id = core.id as CoID<this>;
93
96
  this.core = core;
94
97
 
@@ -234,7 +237,7 @@ export class RawCoList<
234
237
 
235
238
  /** @category 6. Meta */
236
239
  get headerMeta(): Meta {
237
- return this.core.header.meta as Meta;
240
+ return this.core.verified.header.meta as Meta;
238
241
  }
239
242
 
240
243
  /** @category 6. Meta */
@@ -440,8 +443,8 @@ export class RawCoList<
440
443
 
441
444
  /** @category 3. Subscription */
442
445
  subscribe(listener: (coList: this) => void): () => void {
443
- return this.core.subscribe((content) => {
444
- listener(content as this);
446
+ return this.core.subscribe((core) => {
447
+ listener(core.getCurrentContent() as this);
445
448
  });
446
449
  }
447
450
 
@@ -621,6 +624,9 @@ export class RawCoList<
621
624
  this.afterStart = listAfter.afterStart;
622
625
  this.beforeEnd = listAfter.beforeEnd;
623
626
  this.insertions = listAfter.insertions;
627
+ this.totalValidTransactions = listAfter.totalValidTransactions;
628
+ this.lastValidTransaction = listAfter.lastValidTransaction;
629
+ this.knownTransactions = listAfter.knownTransactions;
624
630
  this.deletionsByInsertion = listAfter.deletionsByInsertion;
625
631
  this._cachedEntries = undefined;
626
632
  }
@@ -1,5 +1,8 @@
1
1
  import { CoID, RawCoValue } from "../coValue.js";
2
- import { CoValueCore } from "../coValueCore.js";
2
+ import {
3
+ AvailableCoValueCore,
4
+ CoValueCore,
5
+ } from "../coValueCore/coValueCore.js";
3
6
  import { AgentID, TransactionID } from "../ids.js";
4
7
  import { JsonObject, JsonValue } from "../jsonValue.js";
5
8
  import { CoValueKnownState } from "../sync.js";
@@ -39,7 +42,7 @@ export class RawCoMapView<
39
42
  /** @category 6. Meta */
40
43
  type = "comap" as const;
41
44
  /** @category 6. Meta */
42
- core: CoValueCore;
45
+ core: AvailableCoValueCore;
43
46
  /** @internal */
44
47
  latest: {
45
48
  [Key in keyof Shape & string]?: MapOp<Key, Shape[Key]>;
@@ -64,7 +67,7 @@ export class RawCoMapView<
64
67
 
65
68
  /** @internal */
66
69
  constructor(
67
- core: CoValueCore,
70
+ core: AvailableCoValueCore,
68
71
  options?: {
69
72
  ignorePrivateTransactions: boolean;
70
73
  },
@@ -152,7 +155,7 @@ export class RawCoMapView<
152
155
 
153
156
  /** @category 6. Meta */
154
157
  get headerMeta(): Meta {
155
- return this.core.header.meta as Meta;
158
+ return this.core.verified.header.meta as Meta;
156
159
  }
157
160
 
158
161
  /** @category 6. Meta */
@@ -344,8 +347,8 @@ export class RawCoMapView<
344
347
 
345
348
  /** @category 3. Subscription */
346
349
  subscribe(listener: (coMap: this) => void): () => void {
347
- return this.core.subscribe((content) => {
348
- listener(content as this);
350
+ return this.core.subscribe((core) => {
351
+ listener(core.getCurrentContent() as this);
349
352
  });
350
353
  }
351
354
  }
@@ -1,4 +1,7 @@
1
- import { CoValueCore } from "../coValueCore.js";
1
+ import {
2
+ AvailableCoValueCore,
3
+ CoValueCore,
4
+ } from "../coValueCore/coValueCore.js";
2
5
  import { JsonObject } from "../jsonValue.js";
3
6
  import { DeletionOpPayload, OpID, RawCoList } from "./coList.js";
4
7
 
@@ -61,7 +64,7 @@ export class RawCoPlainText<
61
64
  PlaintextIdxMapping
62
65
  >;
63
66
 
64
- constructor(core: CoValueCore) {
67
+ constructor(core: AvailableCoValueCore) {
65
68
  super(core);
66
69
  this._cachedMapping = new WeakMap();
67
70
  if (!Intl.Segmenter) {
@@ -72,10 +75,10 @@ export class RawCoPlainText<
72
75
 
73
76
  // Use locale from meta if provided, fallback to browser locale, or 'en' as last resort
74
77
  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)
78
+ (core.verified.header.meta &&
79
+ typeof core.verified.header.meta === "object" &&
80
+ "locale" in core.verified.header.meta
81
+ ? (core.verified.header.meta.locale as string)
79
82
  : undefined) ||
80
83
  (typeof navigator !== "undefined" ? navigator.language : "en");
81
84
 
@@ -1,6 +1,9 @@
1
1
  import { base64URLtoBytes, bytesToBase64url } from "../base64url.js";
2
2
  import { CoID, RawCoValue } from "../coValue.js";
3
- import { CoValueCore } from "../coValueCore.js";
3
+ import {
4
+ AvailableCoValueCore,
5
+ CoValueCore,
6
+ } from "../coValueCore/coValueCore.js";
4
7
  import { AgentID, SessionID, TransactionID } from "../ids.js";
5
8
  import { JsonObject, JsonValue } from "../jsonValue.js";
6
9
  import { logger } from "../logger.js";
@@ -50,7 +53,7 @@ export class RawCoStreamView<
50
53
  {
51
54
  id: CoID<this>;
52
55
  type = "costream" as const;
53
- core: CoValueCore;
56
+ core: AvailableCoValueCore;
54
57
  items: {
55
58
  [key: SessionID]: CoStreamItem<Item>[];
56
59
  };
@@ -59,7 +62,7 @@ export class RawCoStreamView<
59
62
  totalValidTransactions = 0;
60
63
  readonly _item!: Item;
61
64
 
62
- constructor(core: CoValueCore) {
65
+ constructor(core: AvailableCoValueCore) {
63
66
  this.id = core.id as CoID<this>;
64
67
  this.core = core;
65
68
  this.items = {};
@@ -68,7 +71,7 @@ export class RawCoStreamView<
68
71
  }
69
72
 
70
73
  get headerMeta(): Meta {
71
- return this.core.header.meta as Meta;
74
+ return this.core.verified.header.meta as Meta;
72
75
  }
73
76
 
74
77
  get group(): RawGroup {
@@ -277,8 +280,8 @@ export class RawCoStreamView<
277
280
  }
278
281
 
279
282
  subscribe(listener: (coStream: this) => void): () => void {
280
- return this.core.subscribe((content) => {
281
- listener(content as this);
283
+ return this.core.subscribe((core) => {
284
+ listener(core.getCurrentContent() as this);
282
285
  });
283
286
  }
284
287
  }