cojson 0.4.8 → 0.5.0
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.
- package/CHANGELOG.md +7 -0
- package/dist/coValueCore.js +112 -45
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValues/coList.js +22 -6
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.js +1 -2
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coStream.js +12 -8
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/crypto.js +57 -31
- package/dist/crypto.js.map +1 -1
- package/dist/localNode.js +59 -16
- package/dist/localNode.js.map +1 -1
- package/dist/permissions.js +5 -3
- package/dist/permissions.js.map +1 -1
- package/dist/streamUtils.js +34 -8
- package/dist/streamUtils.js.map +1 -1
- package/dist/sync.js +180 -46
- package/dist/sync.js.map +1 -1
- package/dist/typeUtils/accountOrAgentIDfromSessionID.js +2 -1
- package/dist/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
- package/package.json +4 -3
- package/src/coValueCore.ts +144 -66
- package/src/coValues/coList.ts +34 -12
- package/src/coValues/coMap.ts +2 -5
- package/src/coValues/coStream.ts +36 -11
- package/src/crypto.ts +83 -41
- package/src/localNode.ts +128 -33
- package/src/permissions.ts +19 -24
- package/src/streamUtils.ts +41 -8
- package/src/sync.ts +221 -60
- package/src/tests/coValue.test.ts +3 -3
- package/src/tests/permissions.test.ts +124 -83
- package/src/tests/sync.test.ts +29 -20
- package/src/typeUtils/accountOrAgentIDfromSessionID.ts +2 -1
package/src/coValueCore.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { randomBytes } from "@noble/hashes/utils";
|
|
2
1
|
import { AnyCoValue, CoValue } from "./coValue.js";
|
|
3
2
|
import {
|
|
4
3
|
Encrypted,
|
|
@@ -28,11 +27,8 @@ import { Group } from "./coValues/group.js";
|
|
|
28
27
|
import { LocalNode } from "./localNode.js";
|
|
29
28
|
import { CoValueKnownState, NewContentMessage } from "./sync.js";
|
|
30
29
|
import { AgentID, RawCoID, SessionID, TransactionID } from "./ids.js";
|
|
31
|
-
import {
|
|
32
|
-
|
|
33
|
-
GeneralizedControlledAccount,
|
|
34
|
-
} from "./coValues/account.js";
|
|
35
|
-
import { Stringified, stableStringify } from "./jsonStringify.js";
|
|
30
|
+
import { AccountID, GeneralizedControlledAccount } from "./coValues/account.js";
|
|
31
|
+
import { Stringified, parseJSON, stableStringify } from "./jsonStringify.js";
|
|
36
32
|
import { coreToCoValue } from "./coreToCoValue.js";
|
|
37
33
|
import { expectGroup } from "./typeUtils/expectGroup.js";
|
|
38
34
|
import { isAccountID } from "./typeUtils/isAccountID.js";
|
|
@@ -54,7 +50,10 @@ export function idforHeader(header: CoValueHeader): RawCoID {
|
|
|
54
50
|
}
|
|
55
51
|
|
|
56
52
|
export function newRandomSessionID(accountID: AccountID | AgentID): SessionID {
|
|
57
|
-
|
|
53
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
+
return `${accountID}_session_z${base58.encode(
|
|
55
|
+
(globalThis as any).crypto.getRandomValues(new Uint8Array(8))
|
|
56
|
+
)}`;
|
|
58
57
|
}
|
|
59
58
|
|
|
60
59
|
type SessionLog = {
|
|
@@ -85,7 +84,7 @@ export type Transaction = PrivateTransaction | TrustingTransaction;
|
|
|
85
84
|
|
|
86
85
|
export type DecryptedTransaction = {
|
|
87
86
|
txID: TransactionID;
|
|
88
|
-
changes:
|
|
87
|
+
changes: JsonValue[];
|
|
89
88
|
madeAt: number;
|
|
90
89
|
};
|
|
91
90
|
|
|
@@ -95,23 +94,25 @@ export class CoValueCore {
|
|
|
95
94
|
id: RawCoID;
|
|
96
95
|
node: LocalNode;
|
|
97
96
|
header: CoValueHeader;
|
|
98
|
-
|
|
97
|
+
_sessionLogs: Map<SessionID, SessionLog>;
|
|
99
98
|
_cachedContent?: CoValue;
|
|
100
99
|
listeners: Set<(content?: CoValue) => void> = new Set();
|
|
101
100
|
_decryptionCache: {
|
|
102
|
-
[key: Encrypted<JsonValue[], JsonValue>]:
|
|
103
|
-
| Stringified<JsonValue[]>
|
|
104
|
-
| undefined;
|
|
101
|
+
[key: Encrypted<JsonValue[], JsonValue>]: JsonValue[] | undefined;
|
|
105
102
|
} = {};
|
|
103
|
+
currentlyAsyncApplyingTxDone?: Promise<void>;
|
|
104
|
+
_cachedKnownState?: CoValueKnownState;
|
|
105
|
+
_cachedDependentOn?: RawCoID[];
|
|
106
|
+
_cachedNewContentSinceEmpty?: NewContentMessage[] | undefined;
|
|
106
107
|
|
|
107
108
|
constructor(
|
|
108
109
|
header: CoValueHeader,
|
|
109
110
|
node: LocalNode,
|
|
110
|
-
internalInitSessions:
|
|
111
|
+
internalInitSessions: Map<SessionID, SessionLog> = new Map()
|
|
111
112
|
) {
|
|
112
113
|
this.id = idforHeader(header);
|
|
113
114
|
this.header = header;
|
|
114
|
-
this.
|
|
115
|
+
this._sessionLogs = internalInitSessions;
|
|
115
116
|
this.node = node;
|
|
116
117
|
|
|
117
118
|
if (header.ruleset.type == "ownedByGroup") {
|
|
@@ -127,8 +128,8 @@ export class CoValueCore {
|
|
|
127
128
|
}
|
|
128
129
|
}
|
|
129
130
|
|
|
130
|
-
get
|
|
131
|
-
return this.
|
|
131
|
+
get sessionLogs(): Map<SessionID, SessionLog> {
|
|
132
|
+
return this._sessionLogs;
|
|
132
133
|
}
|
|
133
134
|
|
|
134
135
|
testWithDifferentAccount(
|
|
@@ -144,11 +145,22 @@ export class CoValueCore {
|
|
|
144
145
|
}
|
|
145
146
|
|
|
146
147
|
knownState(): CoValueKnownState {
|
|
148
|
+
if (this._cachedKnownState) {
|
|
149
|
+
return this._cachedKnownState;
|
|
150
|
+
} else {
|
|
151
|
+
const knownState = this.knownStateUncached();
|
|
152
|
+
this._cachedKnownState = knownState;
|
|
153
|
+
return knownState;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/** @internal */
|
|
158
|
+
knownStateUncached(): CoValueKnownState {
|
|
147
159
|
return {
|
|
148
160
|
id: this.id,
|
|
149
161
|
header: true,
|
|
150
162
|
sessions: Object.fromEntries(
|
|
151
|
-
|
|
163
|
+
[...this.sessionLogs.entries()].map(([k, v]) => [
|
|
152
164
|
k,
|
|
153
165
|
v.transactions.length,
|
|
154
166
|
])
|
|
@@ -172,7 +184,7 @@ export class CoValueCore {
|
|
|
172
184
|
|
|
173
185
|
return {
|
|
174
186
|
sessionID,
|
|
175
|
-
txIndex: this.
|
|
187
|
+
txIndex: this.sessionLogs.get(sessionID)?.transactions.length || 0,
|
|
176
188
|
};
|
|
177
189
|
}
|
|
178
190
|
|
|
@@ -250,8 +262,17 @@ export class CoValueCore {
|
|
|
250
262
|
givenExpectedNewHash: Hash | undefined,
|
|
251
263
|
newSignature: Signature
|
|
252
264
|
): Promise<boolean> {
|
|
265
|
+
if (this.currentlyAsyncApplyingTxDone) {
|
|
266
|
+
await this.currentlyAsyncApplyingTxDone;
|
|
267
|
+
}
|
|
268
|
+
let resolveDone!: () => void;
|
|
269
|
+
|
|
270
|
+
this.currentlyAsyncApplyingTxDone = new Promise((resolve) => {
|
|
271
|
+
resolveDone = resolve;
|
|
272
|
+
});
|
|
273
|
+
|
|
253
274
|
const signerID = getAgentSignerID(
|
|
254
|
-
this.node.
|
|
275
|
+
await this.node.resolveAccountAgentAsync(
|
|
255
276
|
accountOrAgentIDfromSessionID(sessionID),
|
|
256
277
|
"Expected to know signer of transaction"
|
|
257
278
|
)
|
|
@@ -262,10 +283,11 @@ export class CoValueCore {
|
|
|
262
283
|
"Unknown agent",
|
|
263
284
|
accountOrAgentIDfromSessionID(sessionID)
|
|
264
285
|
);
|
|
286
|
+
resolveDone();
|
|
265
287
|
return false;
|
|
266
288
|
}
|
|
267
289
|
|
|
268
|
-
const nTxBefore = this.
|
|
290
|
+
const nTxBefore = this.sessionLogs.get(sessionID)?.transactions.length ?? 0;
|
|
269
291
|
|
|
270
292
|
// const beforeHash = performance.now();
|
|
271
293
|
const { expectedNewHash, newStreamingHash } =
|
|
@@ -276,7 +298,7 @@ export class CoValueCore {
|
|
|
276
298
|
// afterHash - beforeHash
|
|
277
299
|
// );
|
|
278
300
|
|
|
279
|
-
const nTxAfter = this.
|
|
301
|
+
const nTxAfter = this.sessionLogs.get(sessionID)?.transactions.length ?? 0;
|
|
280
302
|
|
|
281
303
|
if (nTxAfter !== nTxBefore) {
|
|
282
304
|
const newTransactionLengthBefore = newTransactions.length;
|
|
@@ -294,6 +316,7 @@ export class CoValueCore {
|
|
|
294
316
|
expectedNewHash,
|
|
295
317
|
givenExpectedNewHash,
|
|
296
318
|
});
|
|
319
|
+
resolveDone();
|
|
297
320
|
return false;
|
|
298
321
|
}
|
|
299
322
|
|
|
@@ -306,6 +329,7 @@ export class CoValueCore {
|
|
|
306
329
|
expectedNewHash,
|
|
307
330
|
signerID
|
|
308
331
|
);
|
|
332
|
+
resolveDone();
|
|
309
333
|
return false;
|
|
310
334
|
}
|
|
311
335
|
// const afterVerify = performance.now();
|
|
@@ -322,6 +346,7 @@ export class CoValueCore {
|
|
|
322
346
|
newStreamingHash
|
|
323
347
|
);
|
|
324
348
|
|
|
349
|
+
resolveDone();
|
|
325
350
|
return true;
|
|
326
351
|
}
|
|
327
352
|
|
|
@@ -332,10 +357,10 @@ export class CoValueCore {
|
|
|
332
357
|
expectedNewHash: Hash,
|
|
333
358
|
newStreamingHash: StreamingHash
|
|
334
359
|
) {
|
|
335
|
-
const transactions = this.
|
|
360
|
+
const transactions = this.sessionLogs.get(sessionID)?.transactions ?? [];
|
|
336
361
|
transactions.push(...newTransactions);
|
|
337
362
|
|
|
338
|
-
const signatureAfter = this.
|
|
363
|
+
const signatureAfter = this.sessionLogs.get(sessionID)?.signatureAfter ?? {};
|
|
339
364
|
|
|
340
365
|
const lastInbetweenSignatureIdx = Object.keys(signatureAfter).reduce(
|
|
341
366
|
(max, idx) => (parseInt(idx) > max ? parseInt(idx) : max),
|
|
@@ -363,15 +388,18 @@ export class CoValueCore {
|
|
|
363
388
|
signatureAfter[transactions.length - 1] = newSignature;
|
|
364
389
|
}
|
|
365
390
|
|
|
366
|
-
this.
|
|
391
|
+
this._sessionLogs.set(sessionID, {
|
|
367
392
|
transactions,
|
|
368
393
|
lastHash: expectedNewHash,
|
|
369
394
|
streamingHash: newStreamingHash,
|
|
370
395
|
lastSignature: newSignature,
|
|
371
396
|
signatureAfter: signatureAfter,
|
|
372
|
-
};
|
|
397
|
+
});
|
|
373
398
|
|
|
374
399
|
this._cachedContent = undefined;
|
|
400
|
+
this._cachedKnownState = undefined;
|
|
401
|
+
this._cachedDependentOn = undefined;
|
|
402
|
+
this._cachedNewContentSinceEmpty = undefined;
|
|
375
403
|
|
|
376
404
|
if (this.listeners.size > 0) {
|
|
377
405
|
const content = this.getCurrentContent();
|
|
@@ -395,7 +423,7 @@ export class CoValueCore {
|
|
|
395
423
|
newTransactions: Transaction[]
|
|
396
424
|
): { expectedNewHash: Hash; newStreamingHash: StreamingHash } {
|
|
397
425
|
const streamingHash =
|
|
398
|
-
this.
|
|
426
|
+
this.sessionLogs.get(sessionID)?.streamingHash.clone() ??
|
|
399
427
|
new StreamingHash();
|
|
400
428
|
for (const transaction of newTransactions) {
|
|
401
429
|
streamingHash.update(transaction);
|
|
@@ -414,7 +442,7 @@ export class CoValueCore {
|
|
|
414
442
|
newTransactions: Transaction[]
|
|
415
443
|
): Promise<{ expectedNewHash: Hash; newStreamingHash: StreamingHash }> {
|
|
416
444
|
const streamingHash =
|
|
417
|
-
this.
|
|
445
|
+
this.sessionLogs.get(sessionID)?.streamingHash.clone() ??
|
|
418
446
|
new StreamingHash();
|
|
419
447
|
let before = performance.now();
|
|
420
448
|
for (const transaction of newTransactions) {
|
|
@@ -457,7 +485,7 @@ export class CoValueCore {
|
|
|
457
485
|
tx: this.nextTransactionID(),
|
|
458
486
|
});
|
|
459
487
|
|
|
460
|
-
this._decryptionCache[encrypted] =
|
|
488
|
+
this._decryptionCache[encrypted] = changes;
|
|
461
489
|
|
|
462
490
|
transaction = {
|
|
463
491
|
privacy: "private",
|
|
@@ -530,7 +558,7 @@ export class CoValueCore {
|
|
|
530
558
|
return {
|
|
531
559
|
txID,
|
|
532
560
|
madeAt: tx.madeAt,
|
|
533
|
-
changes: tx.changes,
|
|
561
|
+
changes: parseJSON(tx.changes),
|
|
534
562
|
};
|
|
535
563
|
} else {
|
|
536
564
|
if (options?.ignorePrivateTransactions) {
|
|
@@ -545,7 +573,7 @@ export class CoValueCore {
|
|
|
545
573
|
this._decryptionCache[tx.encryptedChanges];
|
|
546
574
|
|
|
547
575
|
if (!decrytedChanges) {
|
|
548
|
-
|
|
576
|
+
const decryptedString = decryptRawForTransaction(
|
|
549
577
|
tx.encryptedChanges,
|
|
550
578
|
readKey,
|
|
551
579
|
{
|
|
@@ -553,6 +581,8 @@ export class CoValueCore {
|
|
|
553
581
|
tx: txID,
|
|
554
582
|
}
|
|
555
583
|
);
|
|
584
|
+
decrytedChanges =
|
|
585
|
+
decryptedString && parseJSON(decryptedString);
|
|
556
586
|
this._decryptionCache[tx.encryptedChanges] =
|
|
557
587
|
decrytedChanges;
|
|
558
588
|
}
|
|
@@ -724,12 +754,18 @@ export class CoValueCore {
|
|
|
724
754
|
}
|
|
725
755
|
|
|
726
756
|
getTx(txID: TransactionID): Transaction | undefined {
|
|
727
|
-
return this.
|
|
757
|
+
return this.sessionLogs.get(txID.sessionID)?.transactions[txID.txIndex];
|
|
728
758
|
}
|
|
729
759
|
|
|
730
760
|
newContentSince(
|
|
731
761
|
knownState: CoValueKnownState | undefined
|
|
732
762
|
): NewContentMessage[] | undefined {
|
|
763
|
+
const isKnownStateEmpty = !knownState?.header && !knownState?.sessions;
|
|
764
|
+
|
|
765
|
+
if (isKnownStateEmpty && this._cachedNewContentSinceEmpty) {
|
|
766
|
+
return this._cachedNewContentSinceEmpty;
|
|
767
|
+
}
|
|
768
|
+
|
|
733
769
|
let currentPiece: NewContentMessage = {
|
|
734
770
|
action: "content",
|
|
735
771
|
id: this.id,
|
|
@@ -739,44 +775,55 @@ export class CoValueCore {
|
|
|
739
775
|
|
|
740
776
|
const pieces = [currentPiece];
|
|
741
777
|
|
|
742
|
-
const sentState: CoValueKnownState["sessions"] = {
|
|
743
|
-
...knownState?.sessions,
|
|
744
|
-
};
|
|
778
|
+
const sentState: CoValueKnownState["sessions"] = {};
|
|
745
779
|
|
|
746
|
-
let newTxsWereAdded = true;
|
|
747
780
|
let pieceSize = 0;
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
const
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
781
|
+
|
|
782
|
+
let sessionsTodoAgain: Set<SessionID> | undefined | "first" = "first";
|
|
783
|
+
|
|
784
|
+
while (sessionsTodoAgain === "first" || (sessionsTodoAgain?.size || 0 > 0)) {
|
|
785
|
+
if (sessionsTodoAgain === "first") {
|
|
786
|
+
sessionsTodoAgain = undefined;
|
|
787
|
+
}
|
|
788
|
+
const sessionsTodo = sessionsTodoAgain ?? this.sessionLogs.keys();
|
|
789
|
+
|
|
790
|
+
for (const sessionIDKey of sessionsTodo) {
|
|
791
|
+
const sessionID = sessionIDKey as SessionID;
|
|
792
|
+
const log = this.sessionLogs.get(sessionID)!;
|
|
793
|
+
const knownStateForSessionID = knownState?.sessions[sessionID];
|
|
794
|
+
const sentStateForSessionID = sentState[sessionID];
|
|
795
|
+
const nextKnownSignatureIdx = getNextKnownSignatureIdx(
|
|
796
|
+
log,
|
|
797
|
+
knownStateForSessionID,
|
|
798
|
+
sentStateForSessionID
|
|
765
799
|
);
|
|
766
800
|
|
|
767
|
-
|
|
801
|
+
const firstNewTxIdx = sentStateForSessionID ?? knownStateForSessionID ?? 0;
|
|
802
|
+
const afterLastNewTxIdx = nextKnownSignatureIdx === undefined
|
|
803
|
+
? log.transactions.length
|
|
804
|
+
: nextKnownSignatureIdx + 1;
|
|
768
805
|
|
|
769
|
-
|
|
806
|
+
const nNewTx = Math.max(0, afterLastNewTxIdx - firstNewTxIdx);
|
|
807
|
+
|
|
808
|
+
if (nNewTx === 0) {
|
|
809
|
+
sessionsTodoAgain?.delete(sessionID);
|
|
810
|
+
continue;
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
if (afterLastNewTxIdx < log.transactions.length) {
|
|
814
|
+
if (!sessionsTodoAgain) {
|
|
815
|
+
sessionsTodoAgain = new Set();
|
|
816
|
+
}
|
|
817
|
+
sessionsTodoAgain.add(sessionID);
|
|
818
|
+
}
|
|
770
819
|
|
|
771
820
|
const oldPieceSize = pieceSize;
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
0
|
|
779
|
-
);
|
|
821
|
+
for (let txIdx = firstNewTxIdx; txIdx < afterLastNewTxIdx; txIdx++) {
|
|
822
|
+
const tx = log.transactions[txIdx]!;
|
|
823
|
+
pieceSize += (tx.privacy === "private"
|
|
824
|
+
? tx.encryptedChanges.length
|
|
825
|
+
: tx.changes.length);
|
|
826
|
+
}
|
|
780
827
|
|
|
781
828
|
if (pieceSize >= MAX_RECOMMENDED_TX_SIZE) {
|
|
782
829
|
currentPiece = {
|
|
@@ -792,21 +839,26 @@ export class CoValueCore {
|
|
|
792
839
|
let sessionEntry = currentPiece.new[sessionID];
|
|
793
840
|
if (!sessionEntry) {
|
|
794
841
|
sessionEntry = {
|
|
795
|
-
after:
|
|
842
|
+
after: sentStateForSessionID ?? knownStateForSessionID ?? 0,
|
|
796
843
|
newTransactions: [],
|
|
797
844
|
lastSignature: "WILL_BE_REPLACED" as Signature,
|
|
798
845
|
};
|
|
799
846
|
currentPiece.new[sessionID] = sessionEntry;
|
|
800
847
|
}
|
|
801
848
|
|
|
802
|
-
|
|
849
|
+
for (let txIdx = firstNewTxIdx; txIdx < afterLastNewTxIdx; txIdx++) {
|
|
850
|
+
const tx = log.transactions[txIdx]!;
|
|
851
|
+
sessionEntry.newTransactions.push(tx);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
|
|
803
855
|
sessionEntry.lastSignature =
|
|
804
856
|
nextKnownSignatureIdx === undefined
|
|
805
857
|
? log.lastSignature!
|
|
806
858
|
: log.signatureAfter[nextKnownSignatureIdx]!;
|
|
807
859
|
|
|
808
860
|
sentState[sessionID] =
|
|
809
|
-
(
|
|
861
|
+
(sentStateForSessionID ?? knownStateForSessionID ?? 0) + nNewTx;
|
|
810
862
|
}
|
|
811
863
|
}
|
|
812
864
|
|
|
@@ -818,10 +870,25 @@ export class CoValueCore {
|
|
|
818
870
|
return undefined;
|
|
819
871
|
}
|
|
820
872
|
|
|
873
|
+
if (isKnownStateEmpty) {
|
|
874
|
+
this._cachedNewContentSinceEmpty = piecesWithContent;
|
|
875
|
+
}
|
|
876
|
+
|
|
821
877
|
return piecesWithContent;
|
|
822
878
|
}
|
|
823
879
|
|
|
824
880
|
getDependedOnCoValues(): RawCoID[] {
|
|
881
|
+
if (this._cachedDependentOn) {
|
|
882
|
+
return this._cachedDependentOn;
|
|
883
|
+
} else {
|
|
884
|
+
const dependentOn = this.getDependedOnCoValuesUncached();
|
|
885
|
+
this._cachedDependentOn = dependentOn;
|
|
886
|
+
return dependentOn;
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
/** @internal */
|
|
891
|
+
getDependedOnCoValuesUncached(): RawCoID[] {
|
|
825
892
|
return this.header.ruleset.type === "group"
|
|
826
893
|
? expectGroup(this.getCurrentContent())
|
|
827
894
|
.keys()
|
|
@@ -830,7 +897,7 @@ export class CoValueCore {
|
|
|
830
897
|
? [
|
|
831
898
|
this.header.ruleset.group,
|
|
832
899
|
...new Set(
|
|
833
|
-
|
|
900
|
+
[...this.sessionLogs.keys()]
|
|
834
901
|
.map((sessionID) =>
|
|
835
902
|
accountOrAgentIDfromSessionID(
|
|
836
903
|
sessionID as SessionID
|
|
@@ -845,3 +912,14 @@ export class CoValueCore {
|
|
|
845
912
|
: [];
|
|
846
913
|
}
|
|
847
914
|
}
|
|
915
|
+
|
|
916
|
+
function getNextKnownSignatureIdx(
|
|
917
|
+
log: SessionLog,
|
|
918
|
+
knownStateForSessionID?: number,
|
|
919
|
+
sentStateForSessionID?: number,
|
|
920
|
+
) {
|
|
921
|
+
return Object.keys(log.signatureAfter)
|
|
922
|
+
.map(Number)
|
|
923
|
+
.sort((a, b) => a - b)
|
|
924
|
+
.find((idx) => idx >= (sentStateForSessionID ?? knownStateForSessionID ?? -1));
|
|
925
|
+
}
|
package/src/coValues/coList.ts
CHANGED
|
@@ -5,7 +5,6 @@ import { CoValueCore } from "../coValueCore.js";
|
|
|
5
5
|
import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
|
|
6
6
|
import { AgentID, SessionID, TransactionID } from "../ids.js";
|
|
7
7
|
import { AccountID } from "./account.js";
|
|
8
|
-
import { parseJSON } from "../jsonStringify.js";
|
|
9
8
|
import { Group } from "./group.js";
|
|
10
9
|
|
|
11
10
|
type OpID = TransactionID & { changeIdx: number };
|
|
@@ -76,6 +75,13 @@ export class CoListView<
|
|
|
76
75
|
/** @category 6. Meta */
|
|
77
76
|
readonly _item!: Item;
|
|
78
77
|
|
|
78
|
+
/** @internal */
|
|
79
|
+
_cachedEntries?: {
|
|
80
|
+
value: Item;
|
|
81
|
+
madeAt: number;
|
|
82
|
+
opID: OpID;
|
|
83
|
+
}[];
|
|
84
|
+
|
|
79
85
|
/** @internal */
|
|
80
86
|
constructor(core: CoValueCore) {
|
|
81
87
|
this.id = core.id as CoID<this>;
|
|
@@ -95,9 +101,7 @@ export class CoListView<
|
|
|
95
101
|
changes,
|
|
96
102
|
madeAt,
|
|
97
103
|
} of this.core.getValidSortedTransactions()) {
|
|
98
|
-
for (const [changeIdx, changeUntyped] of
|
|
99
|
-
changes
|
|
100
|
-
).entries()) {
|
|
104
|
+
for (const [changeIdx, changeUntyped] of changes.entries()) {
|
|
101
105
|
const change = changeUntyped as ListOpPayload<Item>;
|
|
102
106
|
|
|
103
107
|
if (change.op === "pre" || change.op === "app") {
|
|
@@ -129,10 +133,11 @@ export class CoListView<
|
|
|
129
133
|
change.before.txIndex
|
|
130
134
|
]?.[change.before.changeIdx];
|
|
131
135
|
if (!beforeEntry) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
);
|
|
136
|
+
// console.error(
|
|
137
|
+
// "Insertion before missing op " +
|
|
138
|
+
// change.before
|
|
139
|
+
// );
|
|
140
|
+
continue;
|
|
136
141
|
}
|
|
137
142
|
beforeEntry.predecessors.splice(0, 0, {
|
|
138
143
|
...txID,
|
|
@@ -151,10 +156,10 @@ export class CoListView<
|
|
|
151
156
|
change.after.txIndex
|
|
152
157
|
]?.[change.after.changeIdx];
|
|
153
158
|
if (!afterEntry) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
159
|
+
// console.error(
|
|
160
|
+
// "Insertion after missing op " + change.after
|
|
161
|
+
// );
|
|
162
|
+
continue;
|
|
158
163
|
}
|
|
159
164
|
afterEntry.successors.push({
|
|
160
165
|
...txID,
|
|
@@ -244,6 +249,20 @@ export class CoListView<
|
|
|
244
249
|
value: Item;
|
|
245
250
|
madeAt: number;
|
|
246
251
|
opID: OpID;
|
|
252
|
+
}[] {
|
|
253
|
+
if (this._cachedEntries) {
|
|
254
|
+
return this._cachedEntries;
|
|
255
|
+
}
|
|
256
|
+
const arr = this.entriesUncached();
|
|
257
|
+
this._cachedEntries = arr;
|
|
258
|
+
return arr;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/** @internal */
|
|
262
|
+
entriesUncached(): {
|
|
263
|
+
value: Item;
|
|
264
|
+
madeAt: number;
|
|
265
|
+
opID: OpID;
|
|
247
266
|
}[] {
|
|
248
267
|
const arr: {
|
|
249
268
|
value: Item;
|
|
@@ -545,6 +564,7 @@ export class MutableCoList<
|
|
|
545
564
|
this.beforeEnd = listAfter.beforeEnd;
|
|
546
565
|
this.insertions = listAfter.insertions;
|
|
547
566
|
this.deletionsByInsertion = listAfter.deletionsByInsertion;
|
|
567
|
+
this._cachedEntries = undefined;
|
|
548
568
|
}
|
|
549
569
|
|
|
550
570
|
/** Prepends `item` before the item currently at index `before`.
|
|
@@ -570,6 +590,7 @@ export class MutableCoList<
|
|
|
570
590
|
this.beforeEnd = listAfter.beforeEnd;
|
|
571
591
|
this.insertions = listAfter.insertions;
|
|
572
592
|
this.deletionsByInsertion = listAfter.deletionsByInsertion;
|
|
593
|
+
this._cachedEntries = undefined;
|
|
573
594
|
}
|
|
574
595
|
|
|
575
596
|
/** Deletes the item at index `at` from the list.
|
|
@@ -590,5 +611,6 @@ export class MutableCoList<
|
|
|
590
611
|
this.beforeEnd = listAfter.beforeEnd;
|
|
591
612
|
this.insertions = listAfter.insertions;
|
|
592
613
|
this.deletionsByInsertion = listAfter.deletionsByInsertion;
|
|
614
|
+
this._cachedEntries = undefined;
|
|
593
615
|
}
|
|
594
616
|
}
|
package/src/coValues/coMap.ts
CHANGED
|
@@ -5,7 +5,6 @@ import { isCoValue } from "../typeUtils/isCoValue.js";
|
|
|
5
5
|
import { CoValueCore } from "../coValueCore.js";
|
|
6
6
|
import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
|
|
7
7
|
import { AccountID } from "./account.js";
|
|
8
|
-
import { parseJSON } from "../jsonStringify.js";
|
|
9
8
|
import type { Group } from "./group.js";
|
|
10
9
|
|
|
11
10
|
type MapOp<K extends string, V extends JsonValue | undefined> = {
|
|
@@ -60,9 +59,7 @@ export class CoMapView<
|
|
|
60
59
|
for (const { txID, changes, madeAt } of core.getValidSortedTransactions(
|
|
61
60
|
options
|
|
62
61
|
)) {
|
|
63
|
-
for (const [changeIdx, changeUntyped] of
|
|
64
|
-
changes
|
|
65
|
-
).entries()) {
|
|
62
|
+
for (const [changeIdx, changeUntyped] of changes.entries()) {
|
|
66
63
|
const change = changeUntyped as MapOpPayload<
|
|
67
64
|
keyof Shape & string,
|
|
68
65
|
Shape[keyof Shape & string]
|
|
@@ -123,7 +120,7 @@ export class CoMapView<
|
|
|
123
120
|
* Get all keys currently in the map.
|
|
124
121
|
*
|
|
125
122
|
* @category 1. Reading */
|
|
126
|
-
keys<K extends
|
|
123
|
+
keys<K extends keyof Shape & string = keyof Shape & string>(): K[] {
|
|
127
124
|
const keys = Object.keys(this.ops) as K[];
|
|
128
125
|
|
|
129
126
|
if (this.atTimeFilter) {
|