cojson 0.4.13 → 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 +108 -42
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValues/coList.js +21 -4
- package/dist/coValues/coList.js.map +1 -1
- package/dist/crypto.js +57 -31
- package/dist/crypto.js.map +1 -1
- package/dist/localNode.js +58 -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 +162 -40
- 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 +140 -63
- package/src/coValues/coList.ts +33 -8
- package/src/crypto.ts +83 -41
- package/src/localNode.ts +127 -34
- package/src/permissions.ts +19 -24
- package/src/streamUtils.ts +41 -8
- package/src/sync.ts +206 -59
- 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,10 +27,7 @@ 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
|
-
AccountID,
|
|
33
|
-
GeneralizedControlledAccount,
|
|
34
|
-
} from "./coValues/account.js";
|
|
30
|
+
import { AccountID, GeneralizedControlledAccount } from "./coValues/account.js";
|
|
35
31
|
import { Stringified, parseJSON, stableStringify } from "./jsonStringify.js";
|
|
36
32
|
import { coreToCoValue } from "./coreToCoValue.js";
|
|
37
33
|
import { expectGroup } from "./typeUtils/expectGroup.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 = {
|
|
@@ -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
|
-
| 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) {
|
|
@@ -552,8 +580,9 @@ export class CoValueCore {
|
|
|
552
580
|
in: this.id,
|
|
553
581
|
tx: txID,
|
|
554
582
|
}
|
|
555
|
-
)
|
|
556
|
-
decrytedChanges =
|
|
583
|
+
);
|
|
584
|
+
decrytedChanges =
|
|
585
|
+
decryptedString && parseJSON(decryptedString);
|
|
557
586
|
this._decryptionCache[tx.encryptedChanges] =
|
|
558
587
|
decrytedChanges;
|
|
559
588
|
}
|
|
@@ -725,12 +754,18 @@ export class CoValueCore {
|
|
|
725
754
|
}
|
|
726
755
|
|
|
727
756
|
getTx(txID: TransactionID): Transaction | undefined {
|
|
728
|
-
return this.
|
|
757
|
+
return this.sessionLogs.get(txID.sessionID)?.transactions[txID.txIndex];
|
|
729
758
|
}
|
|
730
759
|
|
|
731
760
|
newContentSince(
|
|
732
761
|
knownState: CoValueKnownState | undefined
|
|
733
762
|
): NewContentMessage[] | undefined {
|
|
763
|
+
const isKnownStateEmpty = !knownState?.header && !knownState?.sessions;
|
|
764
|
+
|
|
765
|
+
if (isKnownStateEmpty && this._cachedNewContentSinceEmpty) {
|
|
766
|
+
return this._cachedNewContentSinceEmpty;
|
|
767
|
+
}
|
|
768
|
+
|
|
734
769
|
let currentPiece: NewContentMessage = {
|
|
735
770
|
action: "content",
|
|
736
771
|
id: this.id,
|
|
@@ -740,44 +775,55 @@ export class CoValueCore {
|
|
|
740
775
|
|
|
741
776
|
const pieces = [currentPiece];
|
|
742
777
|
|
|
743
|
-
const sentState: CoValueKnownState["sessions"] = {
|
|
744
|
-
...knownState?.sessions,
|
|
745
|
-
};
|
|
778
|
+
const sentState: CoValueKnownState["sessions"] = {};
|
|
746
779
|
|
|
747
|
-
let newTxsWereAdded = true;
|
|
748
780
|
let pieceSize = 0;
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
const
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
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
|
|
766
799
|
);
|
|
767
800
|
|
|
768
|
-
|
|
801
|
+
const firstNewTxIdx = sentStateForSessionID ?? knownStateForSessionID ?? 0;
|
|
802
|
+
const afterLastNewTxIdx = nextKnownSignatureIdx === undefined
|
|
803
|
+
? log.transactions.length
|
|
804
|
+
: nextKnownSignatureIdx + 1;
|
|
769
805
|
|
|
770
|
-
|
|
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
|
+
}
|
|
771
819
|
|
|
772
820
|
const oldPieceSize = pieceSize;
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
0
|
|
780
|
-
);
|
|
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
|
+
}
|
|
781
827
|
|
|
782
828
|
if (pieceSize >= MAX_RECOMMENDED_TX_SIZE) {
|
|
783
829
|
currentPiece = {
|
|
@@ -793,21 +839,26 @@ export class CoValueCore {
|
|
|
793
839
|
let sessionEntry = currentPiece.new[sessionID];
|
|
794
840
|
if (!sessionEntry) {
|
|
795
841
|
sessionEntry = {
|
|
796
|
-
after:
|
|
842
|
+
after: sentStateForSessionID ?? knownStateForSessionID ?? 0,
|
|
797
843
|
newTransactions: [],
|
|
798
844
|
lastSignature: "WILL_BE_REPLACED" as Signature,
|
|
799
845
|
};
|
|
800
846
|
currentPiece.new[sessionID] = sessionEntry;
|
|
801
847
|
}
|
|
802
848
|
|
|
803
|
-
|
|
849
|
+
for (let txIdx = firstNewTxIdx; txIdx < afterLastNewTxIdx; txIdx++) {
|
|
850
|
+
const tx = log.transactions[txIdx]!;
|
|
851
|
+
sessionEntry.newTransactions.push(tx);
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
|
|
804
855
|
sessionEntry.lastSignature =
|
|
805
856
|
nextKnownSignatureIdx === undefined
|
|
806
857
|
? log.lastSignature!
|
|
807
858
|
: log.signatureAfter[nextKnownSignatureIdx]!;
|
|
808
859
|
|
|
809
860
|
sentState[sessionID] =
|
|
810
|
-
(
|
|
861
|
+
(sentStateForSessionID ?? knownStateForSessionID ?? 0) + nNewTx;
|
|
811
862
|
}
|
|
812
863
|
}
|
|
813
864
|
|
|
@@ -819,10 +870,25 @@ export class CoValueCore {
|
|
|
819
870
|
return undefined;
|
|
820
871
|
}
|
|
821
872
|
|
|
873
|
+
if (isKnownStateEmpty) {
|
|
874
|
+
this._cachedNewContentSinceEmpty = piecesWithContent;
|
|
875
|
+
}
|
|
876
|
+
|
|
822
877
|
return piecesWithContent;
|
|
823
878
|
}
|
|
824
879
|
|
|
825
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[] {
|
|
826
892
|
return this.header.ruleset.type === "group"
|
|
827
893
|
? expectGroup(this.getCurrentContent())
|
|
828
894
|
.keys()
|
|
@@ -831,7 +897,7 @@ export class CoValueCore {
|
|
|
831
897
|
? [
|
|
832
898
|
this.header.ruleset.group,
|
|
833
899
|
...new Set(
|
|
834
|
-
|
|
900
|
+
[...this.sessionLogs.keys()]
|
|
835
901
|
.map((sessionID) =>
|
|
836
902
|
accountOrAgentIDfromSessionID(
|
|
837
903
|
sessionID as SessionID
|
|
@@ -846,3 +912,14 @@ export class CoValueCore {
|
|
|
846
912
|
: [];
|
|
847
913
|
}
|
|
848
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
|
@@ -75,6 +75,13 @@ export class CoListView<
|
|
|
75
75
|
/** @category 6. Meta */
|
|
76
76
|
readonly _item!: Item;
|
|
77
77
|
|
|
78
|
+
/** @internal */
|
|
79
|
+
_cachedEntries?: {
|
|
80
|
+
value: Item;
|
|
81
|
+
madeAt: number;
|
|
82
|
+
opID: OpID;
|
|
83
|
+
}[];
|
|
84
|
+
|
|
78
85
|
/** @internal */
|
|
79
86
|
constructor(core: CoValueCore) {
|
|
80
87
|
this.id = core.id as CoID<this>;
|
|
@@ -126,10 +133,11 @@ export class CoListView<
|
|
|
126
133
|
change.before.txIndex
|
|
127
134
|
]?.[change.before.changeIdx];
|
|
128
135
|
if (!beforeEntry) {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
);
|
|
136
|
+
// console.error(
|
|
137
|
+
// "Insertion before missing op " +
|
|
138
|
+
// change.before
|
|
139
|
+
// );
|
|
140
|
+
continue;
|
|
133
141
|
}
|
|
134
142
|
beforeEntry.predecessors.splice(0, 0, {
|
|
135
143
|
...txID,
|
|
@@ -148,10 +156,10 @@ export class CoListView<
|
|
|
148
156
|
change.after.txIndex
|
|
149
157
|
]?.[change.after.changeIdx];
|
|
150
158
|
if (!afterEntry) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
159
|
+
// console.error(
|
|
160
|
+
// "Insertion after missing op " + change.after
|
|
161
|
+
// );
|
|
162
|
+
continue;
|
|
155
163
|
}
|
|
156
164
|
afterEntry.successors.push({
|
|
157
165
|
...txID,
|
|
@@ -241,6 +249,20 @@ export class CoListView<
|
|
|
241
249
|
value: Item;
|
|
242
250
|
madeAt: number;
|
|
243
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;
|
|
244
266
|
}[] {
|
|
245
267
|
const arr: {
|
|
246
268
|
value: Item;
|
|
@@ -542,6 +564,7 @@ export class MutableCoList<
|
|
|
542
564
|
this.beforeEnd = listAfter.beforeEnd;
|
|
543
565
|
this.insertions = listAfter.insertions;
|
|
544
566
|
this.deletionsByInsertion = listAfter.deletionsByInsertion;
|
|
567
|
+
this._cachedEntries = undefined;
|
|
545
568
|
}
|
|
546
569
|
|
|
547
570
|
/** Prepends `item` before the item currently at index `before`.
|
|
@@ -567,6 +590,7 @@ export class MutableCoList<
|
|
|
567
590
|
this.beforeEnd = listAfter.beforeEnd;
|
|
568
591
|
this.insertions = listAfter.insertions;
|
|
569
592
|
this.deletionsByInsertion = listAfter.deletionsByInsertion;
|
|
593
|
+
this._cachedEntries = undefined;
|
|
570
594
|
}
|
|
571
595
|
|
|
572
596
|
/** Deletes the item at index `at` from the list.
|
|
@@ -587,5 +611,6 @@ export class MutableCoList<
|
|
|
587
611
|
this.beforeEnd = listAfter.beforeEnd;
|
|
588
612
|
this.insertions = listAfter.insertions;
|
|
589
613
|
this.deletionsByInsertion = listAfter.deletionsByInsertion;
|
|
614
|
+
this._cachedEntries = undefined;
|
|
590
615
|
}
|
|
591
616
|
}
|