cojson 0.7.0-alpha.7 → 0.7.9
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/.eslintrc.cjs +3 -2
- package/.prettierrc.js +9 -0
- package/.turbo/turbo-build.log +3 -30
- package/.turbo/turbo-lint.log +4 -0
- package/.turbo/turbo-test.log +1106 -0
- package/CHANGELOG.md +118 -14
- package/LICENSE.txt +1 -1
- package/README.md +3 -1
- package/dist/base64url.test.js +25 -0
- package/dist/base64url.test.js.map +1 -0
- package/dist/coValueCore.js +60 -37
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValues/account.js +20 -15
- package/dist/coValues/account.js.map +1 -1
- package/dist/coValues/coList.js +1 -1
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.js +17 -8
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/group.js +13 -14
- package/dist/coValues/group.js.map +1 -1
- package/dist/coreToCoValue.js.map +1 -1
- package/dist/crypto/PureJSCrypto.js +89 -0
- package/dist/crypto/PureJSCrypto.js.map +1 -0
- package/dist/crypto/WasmCrypto.js +127 -0
- package/dist/crypto/WasmCrypto.js.map +1 -0
- package/dist/crypto/crypto.js +151 -0
- package/dist/crypto/crypto.js.map +1 -0
- package/dist/ids.js +4 -2
- package/dist/ids.js.map +1 -1
- package/dist/index.js +6 -8
- package/dist/index.js.map +1 -1
- package/dist/jsonStringify.js.map +1 -1
- package/dist/localNode.js +41 -38
- package/dist/localNode.js.map +1 -1
- package/dist/permissions.js +6 -6
- package/dist/permissions.js.map +1 -1
- package/dist/storage/FileSystem.js +61 -0
- package/dist/storage/FileSystem.js.map +1 -0
- package/dist/storage/chunksAndKnownStates.js +97 -0
- package/dist/storage/chunksAndKnownStates.js.map +1 -0
- package/dist/storage/index.js +265 -0
- package/dist/storage/index.js.map +1 -0
- package/dist/sync.js +29 -25
- package/dist/sync.js.map +1 -1
- package/dist/tests/account.test.js +58 -0
- package/dist/tests/account.test.js.map +1 -0
- package/dist/tests/coList.test.js +76 -0
- package/dist/tests/coList.test.js.map +1 -0
- package/dist/tests/coMap.test.js +136 -0
- package/dist/tests/coMap.test.js.map +1 -0
- package/dist/tests/coStream.test.js +172 -0
- package/dist/tests/coStream.test.js.map +1 -0
- package/dist/tests/coValueCore.test.js +114 -0
- package/dist/tests/coValueCore.test.js.map +1 -0
- package/dist/tests/crypto.test.js +118 -0
- package/dist/tests/crypto.test.js.map +1 -0
- package/dist/tests/cryptoImpl.test.js +113 -0
- package/dist/tests/cryptoImpl.test.js.map +1 -0
- package/dist/tests/group.test.js +34 -0
- package/dist/tests/group.test.js.map +1 -0
- package/dist/tests/permissions.test.js +1060 -0
- package/dist/tests/permissions.test.js.map +1 -0
- package/dist/tests/sync.test.js +816 -0
- package/dist/tests/sync.test.js.map +1 -0
- package/dist/tests/testUtils.js +12 -11
- package/dist/tests/testUtils.js.map +1 -1
- package/dist/typeUtils/accountOrAgentIDfromSessionID.js.map +1 -1
- package/dist/typeUtils/isAccountID.js.map +1 -1
- package/dist/typeUtils/isCoValue.js.map +1 -1
- package/package.json +14 -27
- package/src/base64url.test.ts +6 -5
- package/src/coValue.ts +1 -1
- package/src/coValueCore.ts +179 -126
- package/src/coValues/account.ts +37 -32
- package/src/coValues/coList.ts +11 -11
- package/src/coValues/coMap.ts +27 -17
- package/src/coValues/coStream.ts +17 -17
- package/src/coValues/group.ts +93 -109
- package/src/coreToCoValue.ts +5 -2
- package/src/crypto/PureJSCrypto.ts +200 -0
- package/src/crypto/WasmCrypto.ts +259 -0
- package/src/crypto/crypto.ts +336 -0
- package/src/ids.ts +8 -7
- package/src/index.ts +24 -24
- package/src/jsonStringify.ts +6 -4
- package/src/jsonValue.ts +2 -2
- package/src/localNode.ts +103 -109
- package/src/media.ts +3 -3
- package/src/permissions.ts +19 -21
- package/src/storage/FileSystem.ts +152 -0
- package/src/storage/chunksAndKnownStates.ts +139 -0
- package/src/storage/index.ts +479 -0
- package/src/streamUtils.ts +12 -12
- package/src/sync.ts +79 -63
- package/src/tests/account.test.ts +15 -15
- package/src/tests/coList.test.ts +94 -0
- package/src/tests/coMap.test.ts +162 -0
- package/src/tests/coStream.test.ts +246 -0
- package/src/tests/coValueCore.test.ts +36 -37
- package/src/tests/crypto.test.ts +66 -72
- package/src/tests/cryptoImpl.test.ts +183 -0
- package/src/tests/group.test.ts +16 -17
- package/src/tests/permissions.test.ts +269 -283
- package/src/tests/sync.test.ts +122 -123
- package/src/tests/testUtils.ts +24 -21
- package/src/typeUtils/accountOrAgentIDfromSessionID.ts +1 -2
- package/src/typeUtils/expectGroup.ts +1 -1
- package/src/typeUtils/isAccountID.ts +0 -1
- package/src/typeUtils/isCoValue.ts +1 -2
- package/tsconfig.json +0 -1
- package/dist/crypto.js +0 -254
- package/dist/crypto.js.map +0 -1
- package/src/crypto.ts +0 -484
- package/src/tests/coValue.test.ts +0 -497
package/src/sync.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Signature } from "./crypto.js";
|
|
1
|
+
import { Signature } from "./crypto/crypto.js";
|
|
2
2
|
import { CoValueHeader, Transaction } from "./coValueCore.js";
|
|
3
3
|
import { CoValueCore } from "./coValueCore.js";
|
|
4
4
|
import { LocalNode } from "./localNode.js";
|
|
@@ -82,7 +82,7 @@ export interface PeerState {
|
|
|
82
82
|
|
|
83
83
|
export function combinedKnownStates(
|
|
84
84
|
stateA: CoValueKnownState,
|
|
85
|
-
stateB: CoValueKnownState
|
|
85
|
+
stateB: CoValueKnownState,
|
|
86
86
|
): CoValueKnownState {
|
|
87
87
|
const sessionStates: CoValueKnownState["sessions"] = {};
|
|
88
88
|
|
|
@@ -108,7 +108,11 @@ export function combinedKnownStates(
|
|
|
108
108
|
export class SyncManager {
|
|
109
109
|
peers: { [key: PeerID]: PeerState } = {};
|
|
110
110
|
local: LocalNode;
|
|
111
|
-
requestedSyncs: {
|
|
111
|
+
requestedSyncs: {
|
|
112
|
+
[id: RawCoID]:
|
|
113
|
+
| { done: Promise<void>; nRequestsThisTick: number }
|
|
114
|
+
| undefined;
|
|
115
|
+
} = {};
|
|
112
116
|
|
|
113
117
|
constructor(local: LocalNode) {
|
|
114
118
|
this.local = local;
|
|
@@ -151,31 +155,31 @@ export class SyncManager {
|
|
|
151
155
|
throw new Error("Expected firstPeerState to be waiting " + id);
|
|
152
156
|
}
|
|
153
157
|
await new Promise<void>((resolve) => {
|
|
154
|
-
const timeout = setTimeout(() => {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
}, 1000);
|
|
158
|
+
// const timeout = setTimeout(() => {
|
|
159
|
+
// if (this.local.coValues[id]?.state === "loading") {
|
|
160
|
+
// console.warn(
|
|
161
|
+
// "Timeout waiting for peer to load",
|
|
162
|
+
// id,
|
|
163
|
+
// "from",
|
|
164
|
+
// peer.id,
|
|
165
|
+
// "and it hasn't loaded from other peers yet"
|
|
166
|
+
// );
|
|
167
|
+
// }
|
|
168
|
+
// resolve();
|
|
169
|
+
// }, 1000);
|
|
166
170
|
firstStateEntry.done
|
|
167
171
|
.then(() => {
|
|
168
|
-
clearTimeout(timeout);
|
|
172
|
+
// clearTimeout(timeout);
|
|
169
173
|
resolve();
|
|
170
174
|
})
|
|
171
175
|
.catch((e) => {
|
|
172
|
-
clearTimeout(timeout);
|
|
176
|
+
// clearTimeout(timeout);
|
|
173
177
|
console.error(
|
|
174
178
|
"Error waiting for peer to load",
|
|
175
179
|
id,
|
|
176
180
|
"from",
|
|
177
181
|
peer.id,
|
|
178
|
-
e
|
|
182
|
+
e,
|
|
179
183
|
);
|
|
180
184
|
resolve();
|
|
181
185
|
});
|
|
@@ -203,7 +207,7 @@ export class SyncManager {
|
|
|
203
207
|
throw new Error(
|
|
204
208
|
`Unknown message type ${
|
|
205
209
|
(msg as { action: "string" }).action
|
|
206
|
-
}
|
|
210
|
+
}`,
|
|
207
211
|
);
|
|
208
212
|
}
|
|
209
213
|
}
|
|
@@ -243,15 +247,21 @@ export class SyncManager {
|
|
|
243
247
|
async tellUntoldKnownStateIncludingDependencies(
|
|
244
248
|
id: RawCoID,
|
|
245
249
|
peer: PeerState,
|
|
246
|
-
asDependencyOf?: RawCoID
|
|
250
|
+
asDependencyOf?: RawCoID,
|
|
247
251
|
) {
|
|
248
252
|
const coValue = this.local.expectCoValueLoaded(id);
|
|
249
253
|
|
|
250
|
-
await Promise.all(
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
254
|
+
await Promise.all(
|
|
255
|
+
coValue
|
|
256
|
+
.getDependedOnCoValues()
|
|
257
|
+
.map((dependentCoID) =>
|
|
258
|
+
this.tellUntoldKnownStateIncludingDependencies(
|
|
259
|
+
dependentCoID,
|
|
260
|
+
peer,
|
|
261
|
+
asDependencyOf || id,
|
|
262
|
+
),
|
|
263
|
+
),
|
|
264
|
+
);
|
|
255
265
|
|
|
256
266
|
if (!peer.toldKnownState.has(id)) {
|
|
257
267
|
await this.trySendToPeer(peer, {
|
|
@@ -267,10 +277,16 @@ export class SyncManager {
|
|
|
267
277
|
async sendNewContentIncludingDependencies(id: RawCoID, peer: PeerState) {
|
|
268
278
|
const coValue = this.local.expectCoValueLoaded(id);
|
|
269
279
|
|
|
270
|
-
await Promise.all(
|
|
280
|
+
await Promise.all(
|
|
281
|
+
coValue
|
|
282
|
+
.getDependedOnCoValues()
|
|
283
|
+
.map((id) =>
|
|
284
|
+
this.sendNewContentIncludingDependencies(id, peer),
|
|
285
|
+
),
|
|
286
|
+
);
|
|
271
287
|
|
|
272
288
|
const newContentPieces = coValue.newContentSince(
|
|
273
|
-
peer.optimisticKnownStates[id]
|
|
289
|
+
peer.optimisticKnownStates[id],
|
|
274
290
|
);
|
|
275
291
|
|
|
276
292
|
if (newContentPieces) {
|
|
@@ -302,7 +318,7 @@ export class SyncManager {
|
|
|
302
318
|
|
|
303
319
|
peer.optimisticKnownStates[id] = combinedKnownStates(
|
|
304
320
|
optimisticKnownStateBefore,
|
|
305
|
-
coValue.knownState()
|
|
321
|
+
coValue.knownState(),
|
|
306
322
|
);
|
|
307
323
|
}
|
|
308
324
|
}
|
|
@@ -323,7 +339,7 @@ export class SyncManager {
|
|
|
323
339
|
if (peer.role === "server") {
|
|
324
340
|
const initialSync = async () => {
|
|
325
341
|
for (const id of Object.keys(
|
|
326
|
-
this.local.coValues
|
|
342
|
+
this.local.coValues,
|
|
327
343
|
) as RawCoID[]) {
|
|
328
344
|
// console.log("subscribing to after peer added", id, peer.id)
|
|
329
345
|
await this.subscribeToIncludingDependencies(id, peerState);
|
|
@@ -350,9 +366,9 @@ export class SyncManager {
|
|
|
350
366
|
JSON.stringify(msg, (k, v) =>
|
|
351
367
|
k === "changes" || k === "encryptedChanges"
|
|
352
368
|
? v.slice(0, 20) + "..."
|
|
353
|
-
: v
|
|
369
|
+
: v,
|
|
354
370
|
),
|
|
355
|
-
e
|
|
371
|
+
e,
|
|
356
372
|
);
|
|
357
373
|
});
|
|
358
374
|
// await new Promise<void>((resolve) => {
|
|
@@ -365,9 +381,9 @@ export class SyncManager {
|
|
|
365
381
|
JSON.stringify(msg, (k, v) =>
|
|
366
382
|
k === "changes" || k === "encryptedChanges"
|
|
367
383
|
? v.slice(0, 20) + "..."
|
|
368
|
-
: v
|
|
384
|
+
: v,
|
|
369
385
|
),
|
|
370
|
-
e
|
|
386
|
+
e,
|
|
371
387
|
);
|
|
372
388
|
if (peerState.delayOnError) {
|
|
373
389
|
await new Promise<void>((resolve) => {
|
|
@@ -417,8 +433,8 @@ export class SyncManager {
|
|
|
417
433
|
`Error writing to peer ${peer.id}, disconnecting`,
|
|
418
434
|
{
|
|
419
435
|
cause: e,
|
|
420
|
-
}
|
|
421
|
-
)
|
|
436
|
+
},
|
|
437
|
+
),
|
|
422
438
|
);
|
|
423
439
|
delete this.peers[peer.id];
|
|
424
440
|
});
|
|
@@ -470,7 +486,7 @@ export class SyncManager {
|
|
|
470
486
|
|
|
471
487
|
peer.optimisticKnownStates[msg.id] = combinedKnownStates(
|
|
472
488
|
peer.optimisticKnownStates[msg.id] || emptyKnownState(msg.id),
|
|
473
|
-
knownStateIn(msg)
|
|
489
|
+
knownStateIn(msg),
|
|
474
490
|
);
|
|
475
491
|
|
|
476
492
|
if (!entry) {
|
|
@@ -481,18 +497,18 @@ export class SyncManager {
|
|
|
481
497
|
.catch((e) => {
|
|
482
498
|
console.error(
|
|
483
499
|
`Error loading coValue ${msg.id} to create loading state, as dependency of ${msg.asDependencyOf}`,
|
|
484
|
-
e
|
|
500
|
+
e,
|
|
485
501
|
);
|
|
486
502
|
});
|
|
487
503
|
entry = this.local.coValues[msg.id]!; // must exist after loadCoValueCore
|
|
488
504
|
} else {
|
|
489
505
|
throw new Error(
|
|
490
|
-
"Expected coValue dependency entry to be created, missing subscribe?"
|
|
506
|
+
"Expected coValue dependency entry to be created, missing subscribe?",
|
|
491
507
|
);
|
|
492
508
|
}
|
|
493
509
|
} else {
|
|
494
510
|
throw new Error(
|
|
495
|
-
"Expected coValue entry to be created, missing subscribe?"
|
|
511
|
+
"Expected coValue entry to be created, missing subscribe?",
|
|
496
512
|
);
|
|
497
513
|
}
|
|
498
514
|
}
|
|
@@ -516,7 +532,7 @@ export class SyncManager {
|
|
|
516
532
|
// );
|
|
517
533
|
if (
|
|
518
534
|
Object.values(entry.firstPeerState).every(
|
|
519
|
-
(s) => s.type === "unavailable"
|
|
535
|
+
(s) => s.type === "unavailable",
|
|
520
536
|
)
|
|
521
537
|
) {
|
|
522
538
|
entry.resolve("unavailable");
|
|
@@ -533,7 +549,7 @@ export class SyncManager {
|
|
|
533
549
|
|
|
534
550
|
if (!entry) {
|
|
535
551
|
throw new Error(
|
|
536
|
-
"Expected coValue entry to be created, missing subscribe?"
|
|
552
|
+
"Expected coValue entry to be created, missing subscribe?",
|
|
537
553
|
);
|
|
538
554
|
}
|
|
539
555
|
|
|
@@ -543,7 +559,7 @@ export class SyncManager {
|
|
|
543
559
|
|
|
544
560
|
if (!peerOptimisticKnownState) {
|
|
545
561
|
throw new Error(
|
|
546
|
-
"Expected optimisticKnownState to be set for coValue we receive new content for"
|
|
562
|
+
"Expected optimisticKnownState to be set for coValue we receive new content for",
|
|
547
563
|
);
|
|
548
564
|
}
|
|
549
565
|
|
|
@@ -578,7 +594,7 @@ export class SyncManager {
|
|
|
578
594
|
let invalidStateAssumed = false;
|
|
579
595
|
|
|
580
596
|
for (const [sessionID, newContentForSession] of Object.entries(
|
|
581
|
-
msg.new
|
|
597
|
+
msg.new,
|
|
582
598
|
) as [SessionID, SessionNewContent][]) {
|
|
583
599
|
const ourKnownTxIdx =
|
|
584
600
|
coValue.sessionLogs.get(sessionID)?.transactions.length;
|
|
@@ -605,7 +621,7 @@ export class SyncManager {
|
|
|
605
621
|
sessionID,
|
|
606
622
|
newTransactions,
|
|
607
623
|
undefined,
|
|
608
|
-
newContentForSession.lastSignature
|
|
624
|
+
newContentForSession.lastSignature,
|
|
609
625
|
);
|
|
610
626
|
const after = performance.now();
|
|
611
627
|
if (after - before > 80) {
|
|
@@ -613,7 +629,7 @@ export class SyncManager {
|
|
|
613
629
|
.map((t) =>
|
|
614
630
|
t.privacy === "private"
|
|
615
631
|
? t.encryptedChanges.length
|
|
616
|
-
: t.changes.length
|
|
632
|
+
: t.changes.length,
|
|
617
633
|
)
|
|
618
634
|
.reduce((a, b) => a + b, 0);
|
|
619
635
|
console.log(
|
|
@@ -623,16 +639,16 @@ export class SyncManager {
|
|
|
623
639
|
(1000 * totalTxLength) /
|
|
624
640
|
(after - before) /
|
|
625
641
|
(1024 * 1024)
|
|
626
|
-
).toFixed(2)} MB/s
|
|
642
|
+
).toFixed(2)} MB/s`,
|
|
627
643
|
);
|
|
628
644
|
}
|
|
629
645
|
|
|
630
646
|
const theirTotalnTxs = Object.values(
|
|
631
|
-
peer.optimisticKnownStates[msg.id]?.sessions || {}
|
|
647
|
+
peer.optimisticKnownStates[msg.id]?.sessions || {},
|
|
632
648
|
).reduce((sum, nTxs) => sum + nTxs, 0);
|
|
633
649
|
const ourTotalnTxs = [...coValue.sessionLogs.values()].reduce(
|
|
634
650
|
(sum, session) => sum + session.transactions.length,
|
|
635
|
-
0
|
|
651
|
+
0,
|
|
636
652
|
);
|
|
637
653
|
|
|
638
654
|
entry.onProgress?.(ourTotalnTxs / theirTotalnTxs);
|
|
@@ -644,8 +660,8 @@ export class SyncManager {
|
|
|
644
660
|
JSON.stringify(newTransactions, (k, v) =>
|
|
645
661
|
k === "changes" || k === "encryptedChanges"
|
|
646
662
|
? v.slice(0, 20) + "..."
|
|
647
|
-
: v
|
|
648
|
-
)
|
|
663
|
+
: v,
|
|
664
|
+
),
|
|
649
665
|
);
|
|
650
666
|
continue;
|
|
651
667
|
}
|
|
@@ -653,7 +669,7 @@ export class SyncManager {
|
|
|
653
669
|
peerOptimisticKnownState.sessions[sessionID] = Math.max(
|
|
654
670
|
peerOptimisticKnownState.sessions[sessionID] || 0,
|
|
655
671
|
newContentForSession.after +
|
|
656
|
-
newContentForSession.newTransactions.length
|
|
672
|
+
newContentForSession.newTransactions.length,
|
|
657
673
|
);
|
|
658
674
|
}
|
|
659
675
|
|
|
@@ -688,14 +704,14 @@ export class SyncManager {
|
|
|
688
704
|
return this.requestedSyncs[coValue.id]!.done;
|
|
689
705
|
} else {
|
|
690
706
|
const done = new Promise<void>((resolve) => {
|
|
691
|
-
|
|
707
|
+
queueMicrotask(async () => {
|
|
692
708
|
delete this.requestedSyncs[coValue.id];
|
|
693
709
|
// if (entry.nRequestsThisTick >= 2) {
|
|
694
710
|
// console.log("Syncing", coValue.id, "for", entry.nRequestsThisTick, "requests");
|
|
695
711
|
// }
|
|
696
712
|
await this.actuallySyncCoValue(coValue);
|
|
697
713
|
resolve();
|
|
698
|
-
}
|
|
714
|
+
});
|
|
699
715
|
});
|
|
700
716
|
const entry = {
|
|
701
717
|
done,
|
|
@@ -707,30 +723,30 @@ export class SyncManager {
|
|
|
707
723
|
}
|
|
708
724
|
|
|
709
725
|
async actuallySyncCoValue(coValue: CoValueCore) {
|
|
710
|
-
let blockingSince = performance.now();
|
|
726
|
+
// let blockingSince = performance.now();
|
|
711
727
|
for (const peer of this.peersInPriorityOrder()) {
|
|
712
|
-
if (performance.now() - blockingSince > 5) {
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
}
|
|
728
|
+
// if (performance.now() - blockingSince > 5) {
|
|
729
|
+
// await new Promise<void>((resolve) => {
|
|
730
|
+
// setTimeout(resolve, 0);
|
|
731
|
+
// });
|
|
732
|
+
// blockingSince = performance.now();
|
|
733
|
+
// }
|
|
718
734
|
const optimisticKnownState = peer.optimisticKnownStates[coValue.id];
|
|
719
735
|
|
|
720
736
|
if (optimisticKnownState) {
|
|
721
737
|
await this.tellUntoldKnownStateIncludingDependencies(
|
|
722
738
|
coValue.id,
|
|
723
|
-
peer
|
|
739
|
+
peer,
|
|
724
740
|
);
|
|
725
741
|
await this.sendNewContentIncludingDependencies(
|
|
726
742
|
coValue.id,
|
|
727
|
-
peer
|
|
743
|
+
peer,
|
|
728
744
|
);
|
|
729
745
|
} else if (peer.role === "server") {
|
|
730
746
|
await this.subscribeToIncludingDependencies(coValue.id, peer);
|
|
731
747
|
await this.sendNewContentIncludingDependencies(
|
|
732
748
|
coValue.id,
|
|
733
|
-
peer
|
|
749
|
+
peer,
|
|
734
750
|
);
|
|
735
751
|
}
|
|
736
752
|
}
|
|
@@ -1,22 +1,17 @@
|
|
|
1
|
-
import { expect, test
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
2
|
import { newRandomSessionID } from "../coValueCore.js";
|
|
3
|
-
import { cojsonReady } from "../index.js";
|
|
4
3
|
import { LocalNode } from "../localNode.js";
|
|
5
4
|
import { connectedPeers } from "../streamUtils.js";
|
|
5
|
+
import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
if (!("crypto" in globalThis)) {
|
|
9
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
10
|
-
(globalThis as any).crypto = webcrypto;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
beforeEach(async () => {
|
|
14
|
-
await cojsonReady;
|
|
15
|
-
});
|
|
7
|
+
const Crypto = await WasmCrypto.create();
|
|
16
8
|
|
|
17
9
|
test("Can create a node while creating a new account with profile", async () => {
|
|
18
10
|
const { node, accountID, accountSecret, sessionID } =
|
|
19
|
-
await LocalNode.withNewlyCreatedAccount({
|
|
11
|
+
await LocalNode.withNewlyCreatedAccount({
|
|
12
|
+
creationProps: { name: "Hermes Puggington" },
|
|
13
|
+
crypto: Crypto,
|
|
14
|
+
});
|
|
20
15
|
|
|
21
16
|
expect(node).not.toBeNull();
|
|
22
17
|
expect(accountID).not.toBeNull();
|
|
@@ -24,13 +19,14 @@ test("Can create a node while creating a new account with profile", async () =>
|
|
|
24
19
|
expect(sessionID).not.toBeNull();
|
|
25
20
|
|
|
26
21
|
expect(node.expectProfileLoaded(accountID).get("name")).toEqual(
|
|
27
|
-
"Hermes Puggington"
|
|
22
|
+
"Hermes Puggington",
|
|
28
23
|
);
|
|
29
24
|
});
|
|
30
25
|
|
|
31
26
|
test("A node with an account can create groups and and objects within them", async () => {
|
|
32
27
|
const { node, accountID } = await LocalNode.withNewlyCreatedAccount({
|
|
33
|
-
name: "Hermes Puggington",
|
|
28
|
+
creationProps: { name: "Hermes Puggington" },
|
|
29
|
+
crypto: Crypto,
|
|
34
30
|
});
|
|
35
31
|
|
|
36
32
|
const group = await node.createGroup();
|
|
@@ -44,7 +40,10 @@ test("A node with an account can create groups and and objects within them", asy
|
|
|
44
40
|
|
|
45
41
|
test("Can create account with one node, and then load it on another", async () => {
|
|
46
42
|
const { node, accountID, accountSecret } =
|
|
47
|
-
await LocalNode.withNewlyCreatedAccount({
|
|
43
|
+
await LocalNode.withNewlyCreatedAccount({
|
|
44
|
+
creationProps: { name: "Hermes Puggington" },
|
|
45
|
+
crypto: Crypto,
|
|
46
|
+
});
|
|
48
47
|
|
|
49
48
|
const group = await node.createGroup();
|
|
50
49
|
expect(group).not.toBeNull();
|
|
@@ -66,6 +65,7 @@ test("Can create account with one node, and then load it on another", async () =
|
|
|
66
65
|
accountSecret,
|
|
67
66
|
sessionID: newRandomSessionID(accountID),
|
|
68
67
|
peersToLoadFrom: [node1asPeer],
|
|
68
|
+
crypto: Crypto,
|
|
69
69
|
});
|
|
70
70
|
|
|
71
71
|
const map2 = await node2.load(map.id);
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { expectList } from "../coValue.js";
|
|
3
|
+
import { WasmCrypto } from "../index.js";
|
|
4
|
+
import { LocalNode } from "../localNode.js";
|
|
5
|
+
import { randomAnonymousAccountAndSessionID } from "./testUtils.js";
|
|
6
|
+
|
|
7
|
+
const Crypto = await WasmCrypto.create();
|
|
8
|
+
|
|
9
|
+
test("Empty CoList works", () => {
|
|
10
|
+
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
|
|
11
|
+
|
|
12
|
+
const coValue = node.createCoValue({
|
|
13
|
+
type: "colist",
|
|
14
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
15
|
+
meta: null,
|
|
16
|
+
...Crypto.createdNowUnique(),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const content = expectList(coValue.getCurrentContent());
|
|
20
|
+
|
|
21
|
+
expect(content.type).toEqual("colist");
|
|
22
|
+
expect(content.toJSON()).toEqual([]);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test("Can append, prepend, delete and replace items in CoList", () => {
|
|
26
|
+
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
|
|
27
|
+
|
|
28
|
+
const coValue = node.createCoValue({
|
|
29
|
+
type: "colist",
|
|
30
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
31
|
+
meta: null,
|
|
32
|
+
...Crypto.createdNowUnique(),
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const content = expectList(coValue.getCurrentContent());
|
|
36
|
+
|
|
37
|
+
content.append("hello", 0, "trusting");
|
|
38
|
+
expect(content.toJSON()).toEqual(["hello"]);
|
|
39
|
+
content.append("world", 0, "trusting");
|
|
40
|
+
expect(content.toJSON()).toEqual(["hello", "world"]);
|
|
41
|
+
content.prepend("beautiful", 1, "trusting");
|
|
42
|
+
expect(content.toJSON()).toEqual(["hello", "beautiful", "world"]);
|
|
43
|
+
content.prepend("hooray", 3, "trusting");
|
|
44
|
+
expect(content.toJSON()).toEqual(["hello", "beautiful", "world", "hooray"]);
|
|
45
|
+
content.replace(2, "universe", "trusting");
|
|
46
|
+
expect(content.toJSON()).toEqual([
|
|
47
|
+
"hello",
|
|
48
|
+
"beautiful",
|
|
49
|
+
"universe",
|
|
50
|
+
"hooray",
|
|
51
|
+
]);
|
|
52
|
+
content.delete(2, "trusting");
|
|
53
|
+
expect(content.toJSON()).toEqual(["hello", "beautiful", "hooray"]);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test("Push is equivalent to append after last item", () => {
|
|
57
|
+
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
|
|
58
|
+
|
|
59
|
+
const coValue = node.createCoValue({
|
|
60
|
+
type: "colist",
|
|
61
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
62
|
+
meta: null,
|
|
63
|
+
...Crypto.createdNowUnique(),
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const content = expectList(coValue.getCurrentContent());
|
|
67
|
+
|
|
68
|
+
expect(content.type).toEqual("colist");
|
|
69
|
+
|
|
70
|
+
content.append("hello", 0, "trusting");
|
|
71
|
+
expect(content.toJSON()).toEqual(["hello"]);
|
|
72
|
+
content.append("world", undefined, "trusting");
|
|
73
|
+
expect(content.toJSON()).toEqual(["hello", "world"]);
|
|
74
|
+
content.append("hooray", undefined, "trusting");
|
|
75
|
+
expect(content.toJSON()).toEqual(["hello", "world", "hooray"]);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test("Can push into empty list", () => {
|
|
79
|
+
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
|
|
80
|
+
|
|
81
|
+
const coValue = node.createCoValue({
|
|
82
|
+
type: "colist",
|
|
83
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
84
|
+
meta: null,
|
|
85
|
+
...Crypto.createdNowUnique(),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
const content = expectList(coValue.getCurrentContent());
|
|
89
|
+
|
|
90
|
+
expect(content.type).toEqual("colist");
|
|
91
|
+
|
|
92
|
+
content.append("hello", undefined, "trusting");
|
|
93
|
+
expect(content.toJSON()).toEqual(["hello"]);
|
|
94
|
+
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
import { expect, test } from "vitest";
|
|
2
|
+
import { expectMap } from "../coValue.js";
|
|
3
|
+
import { WasmCrypto } from "../index.js";
|
|
4
|
+
import { LocalNode } from "../localNode.js";
|
|
5
|
+
import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
|
|
6
|
+
import { randomAnonymousAccountAndSessionID } from "./testUtils.js";
|
|
7
|
+
|
|
8
|
+
const Crypto = await WasmCrypto.create();
|
|
9
|
+
|
|
10
|
+
test("Empty CoMap works", () => {
|
|
11
|
+
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
|
|
12
|
+
|
|
13
|
+
const coValue = node.createCoValue({
|
|
14
|
+
type: "comap",
|
|
15
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
16
|
+
meta: null,
|
|
17
|
+
...Crypto.createdNowUnique(),
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const content = expectMap(coValue.getCurrentContent());
|
|
21
|
+
|
|
22
|
+
expect(content.type).toEqual("comap");
|
|
23
|
+
expect([...content.keys()]).toEqual([]);
|
|
24
|
+
expect(content.toJSON()).toEqual({});
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("Can insert and delete CoMap entries in edit()", () => {
|
|
28
|
+
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
|
|
29
|
+
|
|
30
|
+
const coValue = node.createCoValue({
|
|
31
|
+
type: "comap",
|
|
32
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
33
|
+
meta: null,
|
|
34
|
+
...Crypto.createdNowUnique(),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const content = expectMap(coValue.getCurrentContent());
|
|
38
|
+
|
|
39
|
+
expect(content.type).toEqual("comap");
|
|
40
|
+
|
|
41
|
+
content.set("hello", "world", "trusting");
|
|
42
|
+
expect(content.get("hello")).toEqual("world");
|
|
43
|
+
content.set("foo", "bar", "trusting");
|
|
44
|
+
expect(content.get("foo")).toEqual("bar");
|
|
45
|
+
expect([...content.keys()]).toEqual(["hello", "foo"]);
|
|
46
|
+
content.delete("foo", "trusting");
|
|
47
|
+
expect(content.get("foo")).toEqual(undefined);
|
|
48
|
+
expect(content.keys()).toEqual(["hello"]);
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
test("Can get CoMap entry values at different points in time", () => {
|
|
52
|
+
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
|
|
53
|
+
|
|
54
|
+
const coValue = node.createCoValue({
|
|
55
|
+
type: "comap",
|
|
56
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
57
|
+
meta: null,
|
|
58
|
+
...Crypto.createdNowUnique(),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const content = expectMap(coValue.getCurrentContent());
|
|
62
|
+
|
|
63
|
+
expect(content.type).toEqual("comap");
|
|
64
|
+
|
|
65
|
+
const beforeA = Date.now();
|
|
66
|
+
while (Date.now() < beforeA + 10) {
|
|
67
|
+
/* hot sleep */
|
|
68
|
+
}
|
|
69
|
+
content.set("hello", "A", "trusting");
|
|
70
|
+
const beforeB = Date.now();
|
|
71
|
+
while (Date.now() < beforeB + 10) {
|
|
72
|
+
/* hot sleep */
|
|
73
|
+
}
|
|
74
|
+
content.set("hello", "B", "trusting");
|
|
75
|
+
const beforeC = Date.now();
|
|
76
|
+
while (Date.now() < beforeC + 10) {
|
|
77
|
+
/* hot sleep */
|
|
78
|
+
}
|
|
79
|
+
content.set("hello", "C", "trusting");
|
|
80
|
+
expect(content.get("hello")).toEqual("C");
|
|
81
|
+
expect(content.atTime(Date.now()).get("hello")).toEqual("C");
|
|
82
|
+
expect(content.atTime(beforeA).get("hello")).toEqual(undefined);
|
|
83
|
+
expect(content.atTime(beforeB).get("hello")).toEqual("A");
|
|
84
|
+
expect(content.atTime(beforeC).get("hello")).toEqual("B");
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
test("Can get all historic values of key in CoMap", () => {
|
|
88
|
+
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
|
|
89
|
+
|
|
90
|
+
const coValue = node.createCoValue({
|
|
91
|
+
type: "comap",
|
|
92
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
93
|
+
meta: null,
|
|
94
|
+
...Crypto.createdNowUnique(),
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
const content = expectMap(coValue.getCurrentContent());
|
|
98
|
+
|
|
99
|
+
expect(content.type).toEqual("comap");
|
|
100
|
+
|
|
101
|
+
content.set("hello", "A", "trusting");
|
|
102
|
+
const editA = content.lastEditAt("hello");
|
|
103
|
+
content.set("hello", "B", "trusting");
|
|
104
|
+
const editB = content.lastEditAt("hello");
|
|
105
|
+
content.delete("hello", "trusting");
|
|
106
|
+
const editDel = content.lastEditAt("hello");
|
|
107
|
+
content.set("hello", "C", "trusting");
|
|
108
|
+
const editC = content.lastEditAt("hello");
|
|
109
|
+
expect([...content.editsAt("hello")]).toEqual([
|
|
110
|
+
{
|
|
111
|
+
tx: editA!.tx,
|
|
112
|
+
by: node.account.id,
|
|
113
|
+
value: "A",
|
|
114
|
+
at: editA?.at,
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
tx: editB!.tx,
|
|
118
|
+
by: node.account.id,
|
|
119
|
+
value: "B",
|
|
120
|
+
at: editB?.at,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
tx: editDel!.tx,
|
|
124
|
+
by: node.account.id,
|
|
125
|
+
value: undefined,
|
|
126
|
+
at: editDel?.at,
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
tx: editC!.tx,
|
|
130
|
+
by: node.account.id,
|
|
131
|
+
value: "C",
|
|
132
|
+
at: editC?.at,
|
|
133
|
+
},
|
|
134
|
+
]);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test("Can get last tx ID for a key in CoMap", () => {
|
|
138
|
+
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
|
|
139
|
+
|
|
140
|
+
const coValue = node.createCoValue({
|
|
141
|
+
type: "comap",
|
|
142
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
143
|
+
meta: null,
|
|
144
|
+
...Crypto.createdNowUnique(),
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
const content = expectMap(coValue.getCurrentContent());
|
|
148
|
+
|
|
149
|
+
expect(content.type).toEqual("comap");
|
|
150
|
+
|
|
151
|
+
expect(content.lastEditAt("hello")).toEqual(undefined);
|
|
152
|
+
content.set("hello", "A", "trusting");
|
|
153
|
+
const sessionID = content.lastEditAt("hello")?.tx.sessionID;
|
|
154
|
+
expect(sessionID && accountOrAgentIDfromSessionID(sessionID)).toEqual(
|
|
155
|
+
node.account.id,
|
|
156
|
+
);
|
|
157
|
+
expect(content.lastEditAt("hello")?.tx.txIndex).toEqual(0);
|
|
158
|
+
content.set("hello", "B", "trusting");
|
|
159
|
+
expect(content.lastEditAt("hello")?.tx.txIndex).toEqual(1);
|
|
160
|
+
content.set("hello", "C", "trusting");
|
|
161
|
+
expect(content.lastEditAt("hello")?.tx.txIndex).toEqual(2);
|
|
162
|
+
});
|