cojson 0.13.15 → 0.13.17
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/.turbo/turbo-build.log +1 -1
- package/CHANGELOG.md +13 -0
- package/dist/coValue.d.ts +2 -0
- package/dist/coValue.d.ts.map +1 -1
- package/dist/coValue.js +1 -0
- package/dist/coValue.js.map +1 -1
- package/dist/coValueCore.d.ts +1 -0
- package/dist/coValueCore.d.ts.map +1 -1
- package/dist/coValueCore.js.map +1 -1
- package/dist/coValueState.d.ts.map +1 -1
- package/dist/coValueState.js +58 -96
- package/dist/coValueState.js.map +1 -1
- package/dist/coValues/coList.d.ts +7 -3
- package/dist/coValues/coList.d.ts.map +1 -1
- package/dist/coValues/coList.js +35 -12
- package/dist/coValues/coList.js.map +1 -1
- package/dist/coValues/coMap.d.ts +1 -0
- package/dist/coValues/coMap.d.ts.map +1 -1
- package/dist/coValues/coMap.js +2 -0
- package/dist/coValues/coMap.js.map +1 -1
- package/dist/coValues/coPlainText.d.ts.map +1 -1
- package/dist/coValues/coPlainText.js +1 -1
- package/dist/coValues/coPlainText.js.map +1 -1
- package/dist/coValues/coStream.d.ts +2 -1
- package/dist/coValues/coStream.d.ts.map +1 -1
- package/dist/coValues/coStream.js +2 -0
- package/dist/coValues/coStream.js.map +1 -1
- package/dist/coreToCoValue.d.ts +2 -1
- package/dist/coreToCoValue.d.ts.map +1 -1
- package/dist/localNode.d.ts.map +1 -1
- package/dist/localNode.js +21 -14
- package/dist/localNode.js.map +1 -1
- package/dist/sync.js +3 -3
- package/dist/sync.js.map +1 -1
- package/dist/tests/coList.test.js +119 -2
- package/dist/tests/coList.test.js.map +1 -1
- package/dist/tests/coMap.test.js +32 -2
- package/dist/tests/coMap.test.js.map +1 -1
- package/dist/tests/coStream.test.js +28 -2
- package/dist/tests/coStream.test.js.map +1 -1
- package/dist/tests/coValueState.test.js +10 -143
- package/dist/tests/coValueState.test.js.map +1 -1
- package/dist/tests/sync.load.test.js +59 -1
- package/dist/tests/sync.load.test.js.map +1 -1
- package/dist/tests/sync.mesh.test.js +48 -1
- package/dist/tests/sync.mesh.test.js.map +1 -1
- package/package.json +1 -1
- package/src/coValue.ts +3 -0
- package/src/coValueCore.ts +1 -0
- package/src/coValueState.ts +67 -122
- package/src/coValues/coList.ts +51 -23
- package/src/coValues/coMap.ts +4 -0
- package/src/coValues/coPlainText.ts +1 -2
- package/src/coValues/coStream.ts +3 -1
- package/src/localNode.ts +29 -18
- package/src/sync.ts +3 -3
- package/src/tests/coList.test.ts +184 -2
- package/src/tests/coMap.test.ts +54 -2
- package/src/tests/coStream.test.ts +56 -2
- package/src/tests/coValueState.test.ts +9 -207
- package/src/tests/sync.load.test.ts +78 -1
- package/src/tests/sync.mesh.test.ts +67 -0
package/src/coValues/coList.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { CoID, RawCoValue } from "../coValue.js";
|
|
|
2
2
|
import { CoValueCore } from "../coValueCore.js";
|
|
3
3
|
import { AgentID, SessionID, TransactionID } from "../ids.js";
|
|
4
4
|
import { JsonObject, JsonValue } from "../jsonValue.js";
|
|
5
|
+
import { CoValueKnownState } from "../sync.js";
|
|
5
6
|
import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
|
|
6
7
|
import { isCoValue } from "../typeUtils/isCoValue.js";
|
|
7
8
|
import { RawAccountID } from "./account.js";
|
|
@@ -41,7 +42,7 @@ type DeletionEntry = {
|
|
|
41
42
|
deletionID: OpID;
|
|
42
43
|
} & DeletionOpPayload;
|
|
43
44
|
|
|
44
|
-
export class
|
|
45
|
+
export class RawCoList<
|
|
45
46
|
Item extends JsonValue = JsonValue,
|
|
46
47
|
Meta extends JsonObject | null = null,
|
|
47
48
|
> implements RawCoValue
|
|
@@ -81,26 +82,52 @@ export class RawCoListView<
|
|
|
81
82
|
madeAt: number;
|
|
82
83
|
opID: OpID;
|
|
83
84
|
}[];
|
|
85
|
+
/** @internal */
|
|
86
|
+
totalValidTransactions = 0;
|
|
87
|
+
knownTransactions: CoValueKnownState["sessions"] = {};
|
|
88
|
+
lastValidTransaction: number | undefined;
|
|
84
89
|
|
|
85
90
|
/** @internal */
|
|
86
91
|
constructor(core: CoValueCore) {
|
|
87
92
|
this.id = core.id as CoID<this>;
|
|
88
93
|
this.core = core;
|
|
89
|
-
this.afterStart = [];
|
|
90
|
-
this.beforeEnd = [];
|
|
91
|
-
this.insertions = {};
|
|
92
|
-
this.deletionsByInsertion = {};
|
|
93
94
|
|
|
94
95
|
this.insertions = {};
|
|
95
96
|
this.deletionsByInsertion = {};
|
|
96
97
|
this.afterStart = [];
|
|
97
98
|
this.beforeEnd = [];
|
|
99
|
+
this.knownTransactions = {};
|
|
100
|
+
|
|
101
|
+
this.processNewTransactions();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
processNewTransactions() {
|
|
105
|
+
const transactions = this.core.getValidSortedTransactions({
|
|
106
|
+
ignorePrivateTransactions: false,
|
|
107
|
+
knownTransactions: this.knownTransactions,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
if (transactions.length === 0) {
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this.totalValidTransactions += transactions.length;
|
|
115
|
+
let lastValidTransaction: number | undefined = undefined;
|
|
116
|
+
let oldestValidTransaction: number | undefined = undefined;
|
|
117
|
+
this._cachedEntries = undefined;
|
|
118
|
+
|
|
119
|
+
for (const { txID, changes, madeAt } of transactions) {
|
|
120
|
+
lastValidTransaction = Math.max(lastValidTransaction ?? 0, madeAt);
|
|
121
|
+
oldestValidTransaction = Math.min(
|
|
122
|
+
oldestValidTransaction ?? Infinity,
|
|
123
|
+
madeAt,
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
this.knownTransactions[txID.sessionID] = Math.max(
|
|
127
|
+
this.knownTransactions[txID.sessionID] ?? 0,
|
|
128
|
+
txID.txIndex,
|
|
129
|
+
);
|
|
98
130
|
|
|
99
|
-
for (const {
|
|
100
|
-
txID,
|
|
101
|
-
changes,
|
|
102
|
-
madeAt,
|
|
103
|
-
} of this.core.getValidSortedTransactions()) {
|
|
104
131
|
for (const [changeIdx, changeUntyped] of changes.entries()) {
|
|
105
132
|
const change = changeUntyped as ListOpPayload<Item>;
|
|
106
133
|
|
|
@@ -193,6 +220,16 @@ export class RawCoListView<
|
|
|
193
220
|
}
|
|
194
221
|
}
|
|
195
222
|
}
|
|
223
|
+
|
|
224
|
+
if (
|
|
225
|
+
this.lastValidTransaction &&
|
|
226
|
+
oldestValidTransaction &&
|
|
227
|
+
oldestValidTransaction < this.lastValidTransaction
|
|
228
|
+
) {
|
|
229
|
+
this.rebuildFromCore();
|
|
230
|
+
} else {
|
|
231
|
+
this.lastValidTransaction = lastValidTransaction;
|
|
232
|
+
}
|
|
196
233
|
}
|
|
197
234
|
|
|
198
235
|
/** @category 6. Meta */
|
|
@@ -407,15 +444,7 @@ export class RawCoListView<
|
|
|
407
444
|
listener(content as this);
|
|
408
445
|
});
|
|
409
446
|
}
|
|
410
|
-
}
|
|
411
447
|
|
|
412
|
-
export class RawCoList<
|
|
413
|
-
Item extends JsonValue = JsonValue,
|
|
414
|
-
Meta extends JsonObject | null = JsonObject | null,
|
|
415
|
-
>
|
|
416
|
-
extends RawCoListView<Item, Meta>
|
|
417
|
-
implements RawCoValue
|
|
418
|
-
{
|
|
419
448
|
/** Appends `item` after the item currently at index `after`.
|
|
420
449
|
*
|
|
421
450
|
* If `privacy` is `"private"` **(default)**, `item` is encrypted in the transaction, only readable by other members of the group this `CoList` belongs to. Not even sync servers can see the content in plaintext.
|
|
@@ -480,8 +509,7 @@ export class RawCoList<
|
|
|
480
509
|
}
|
|
481
510
|
|
|
482
511
|
this.core.makeTransaction(changes, privacy);
|
|
483
|
-
|
|
484
|
-
this.rebuildFromCore();
|
|
512
|
+
this.processNewTransactions();
|
|
485
513
|
}
|
|
486
514
|
|
|
487
515
|
/**
|
|
@@ -528,7 +556,7 @@ export class RawCoList<
|
|
|
528
556
|
privacy,
|
|
529
557
|
);
|
|
530
558
|
|
|
531
|
-
this.
|
|
559
|
+
this.processNewTransactions();
|
|
532
560
|
}
|
|
533
561
|
|
|
534
562
|
/** Deletes the item at index `at`.
|
|
@@ -555,7 +583,7 @@ export class RawCoList<
|
|
|
555
583
|
privacy,
|
|
556
584
|
);
|
|
557
585
|
|
|
558
|
-
this.
|
|
586
|
+
this.processNewTransactions();
|
|
559
587
|
}
|
|
560
588
|
|
|
561
589
|
replace(
|
|
@@ -583,7 +611,7 @@ export class RawCoList<
|
|
|
583
611
|
],
|
|
584
612
|
privacy,
|
|
585
613
|
);
|
|
586
|
-
this.
|
|
614
|
+
this.processNewTransactions();
|
|
587
615
|
}
|
|
588
616
|
|
|
589
617
|
/** @internal */
|
package/src/coValues/coMap.ts
CHANGED
|
@@ -60,6 +60,8 @@ export class RawCoMapView<
|
|
|
60
60
|
/** @category 6. Meta */
|
|
61
61
|
readonly _shape!: Shape;
|
|
62
62
|
|
|
63
|
+
totalValidTransactions = 0;
|
|
64
|
+
|
|
63
65
|
/** @internal */
|
|
64
66
|
constructor(
|
|
65
67
|
core: CoValueCore,
|
|
@@ -133,6 +135,8 @@ export class RawCoMapView<
|
|
|
133
135
|
}
|
|
134
136
|
}
|
|
135
137
|
|
|
138
|
+
this.totalValidTransactions += newValidTransactions.length;
|
|
139
|
+
|
|
136
140
|
for (const entries of changedEntries.values()) {
|
|
137
141
|
entries.sort(this.core.compareTransactions);
|
|
138
142
|
}
|
package/src/coValues/coStream.ts
CHANGED
|
@@ -56,6 +56,7 @@ export class RawCoStreamView<
|
|
|
56
56
|
};
|
|
57
57
|
/** @internal */
|
|
58
58
|
knownTransactions: CoValueKnownState["sessions"];
|
|
59
|
+
totalValidTransactions = 0;
|
|
59
60
|
readonly _item!: Item;
|
|
60
61
|
|
|
61
62
|
constructor(core: CoValueCore) {
|
|
@@ -96,7 +97,7 @@ export class RawCoStreamView<
|
|
|
96
97
|
}
|
|
97
98
|
|
|
98
99
|
/** @internal */
|
|
99
|
-
|
|
100
|
+
processNewTransactions() {
|
|
100
101
|
const changeEntries = new Set<CoStreamItem<Item>[]>();
|
|
101
102
|
|
|
102
103
|
const newValidTransactions = this.core.getValidTransactions({
|
|
@@ -109,6 +110,7 @@ export class RawCoStreamView<
|
|
|
109
110
|
}
|
|
110
111
|
|
|
111
112
|
for (const { txID, madeAt, changes } of newValidTransactions) {
|
|
113
|
+
this.totalValidTransactions++;
|
|
112
114
|
for (const changeUntyped of changes) {
|
|
113
115
|
const change = changeUntyped as Item;
|
|
114
116
|
let entries = this.items[txID.sessionID];
|
package/src/localNode.ts
CHANGED
|
@@ -266,29 +266,40 @@ export class LocalNode {
|
|
|
266
266
|
});
|
|
267
267
|
}
|
|
268
268
|
|
|
269
|
-
|
|
269
|
+
let retries = 0;
|
|
270
270
|
|
|
271
|
-
|
|
272
|
-
entry
|
|
273
|
-
entry.highLevelState === "unavailable"
|
|
274
|
-
) {
|
|
275
|
-
const peers =
|
|
276
|
-
this.syncManager.getServerAndStoragePeers(skipLoadingFromPeer);
|
|
271
|
+
while (true) {
|
|
272
|
+
const entry = this.coValuesStore.get(id);
|
|
277
273
|
|
|
278
|
-
if (
|
|
279
|
-
|
|
280
|
-
|
|
274
|
+
if (
|
|
275
|
+
entry.highLevelState === "unknown" ||
|
|
276
|
+
entry.highLevelState === "unavailable"
|
|
277
|
+
) {
|
|
278
|
+
const peers =
|
|
279
|
+
this.syncManager.getServerAndStoragePeers(skipLoadingFromPeer);
|
|
281
280
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
281
|
+
if (peers.length === 0) {
|
|
282
|
+
return "unavailable";
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
entry.loadFromPeers(peers).catch((e) => {
|
|
286
|
+
logger.error("Error loading from peers", {
|
|
287
|
+
id,
|
|
288
|
+
err: e,
|
|
289
|
+
});
|
|
286
290
|
});
|
|
287
|
-
}
|
|
288
|
-
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const result = await entry.getCoValue();
|
|
289
294
|
|
|
290
|
-
|
|
291
|
-
|
|
295
|
+
if (result !== "unavailable" || retries >= 1) {
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
await new Promise((resolve) => setTimeout(resolve, 300));
|
|
300
|
+
|
|
301
|
+
retries++;
|
|
302
|
+
}
|
|
292
303
|
}
|
|
293
304
|
|
|
294
305
|
/**
|
package/src/sync.ts
CHANGED
|
@@ -163,13 +163,13 @@ export class SyncManager {
|
|
|
163
163
|
`Skipping message ${msg.action} on errored coValue ${msg.id} from peer ${peer.id}`,
|
|
164
164
|
);
|
|
165
165
|
return;
|
|
166
|
-
} else if (msg.id === undefined) {
|
|
167
|
-
logger.
|
|
166
|
+
} else if (msg.id === undefined || msg.id === null) {
|
|
167
|
+
logger.warn("Received sync message with undefined id", {
|
|
168
168
|
msg,
|
|
169
169
|
});
|
|
170
170
|
return;
|
|
171
171
|
} else if (!msg.id.startsWith("co_z")) {
|
|
172
|
-
logger.
|
|
172
|
+
logger.warn("Received sync message with invalid id", {
|
|
173
173
|
msg,
|
|
174
174
|
});
|
|
175
175
|
return;
|
package/src/tests/coList.test.ts
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
import { expect, test } from "vitest";
|
|
1
|
+
import { beforeEach, expect, test } from "vitest";
|
|
2
2
|
import { expectList } from "../coValue.js";
|
|
3
3
|
import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
4
4
|
import { LocalNode } from "../localNode.js";
|
|
5
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
loadCoValueOrFail,
|
|
7
|
+
randomAnonymousAccountAndSessionID,
|
|
8
|
+
setupTestNode,
|
|
9
|
+
waitFor,
|
|
10
|
+
} from "./testUtils.js";
|
|
6
11
|
|
|
7
12
|
const Crypto = await WasmCrypto.create();
|
|
8
13
|
|
|
14
|
+
beforeEach(async () => {
|
|
15
|
+
setupTestNode({ isSyncServer: true });
|
|
16
|
+
});
|
|
17
|
+
|
|
9
18
|
test("Empty CoList works", () => {
|
|
10
19
|
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
|
|
11
20
|
|
|
@@ -221,3 +230,176 @@ test("Items prepended to start appear with latest first", () => {
|
|
|
221
230
|
|
|
222
231
|
expect(content.toJSON()).toEqual(["third", "second", "first"]);
|
|
223
232
|
});
|
|
233
|
+
|
|
234
|
+
test("mixing prepend and append", () => {
|
|
235
|
+
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
|
|
236
|
+
|
|
237
|
+
const coValue = node.createCoValue({
|
|
238
|
+
type: "colist",
|
|
239
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
240
|
+
meta: null,
|
|
241
|
+
...Crypto.createdNowUnique(),
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const list = expectList(coValue.getCurrentContent());
|
|
245
|
+
|
|
246
|
+
list.append(2, undefined, "trusting");
|
|
247
|
+
list.prepend(1, undefined, "trusting");
|
|
248
|
+
list.append(3, undefined, "trusting");
|
|
249
|
+
|
|
250
|
+
expect(list.toJSON()).toEqual([1, 2, 3]);
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test("Items appended to start", () => {
|
|
254
|
+
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
|
|
255
|
+
|
|
256
|
+
const coValue = node.createCoValue({
|
|
257
|
+
type: "colist",
|
|
258
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
259
|
+
meta: null,
|
|
260
|
+
...Crypto.createdNowUnique(),
|
|
261
|
+
});
|
|
262
|
+
|
|
263
|
+
const content = expectList(coValue.getCurrentContent());
|
|
264
|
+
|
|
265
|
+
content.append("first", 0, "trusting");
|
|
266
|
+
content.append("second", 0, "trusting");
|
|
267
|
+
content.append("third", 0, "trusting");
|
|
268
|
+
|
|
269
|
+
// This result is correct because "third" is appended after "first"
|
|
270
|
+
// Using the Array methods this would be the same as doing content.splice(1, 0, "third")
|
|
271
|
+
expect(content.toJSON()).toEqual(["first", "third", "second"]);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
test("syncing appends with an older timestamp", async () => {
|
|
275
|
+
const client = setupTestNode({
|
|
276
|
+
connected: true,
|
|
277
|
+
});
|
|
278
|
+
const otherClient = setupTestNode({});
|
|
279
|
+
|
|
280
|
+
const otherClientConnection = otherClient.connectToSyncServer();
|
|
281
|
+
|
|
282
|
+
const coValue = client.node.createCoValue({
|
|
283
|
+
type: "colist",
|
|
284
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
285
|
+
meta: null,
|
|
286
|
+
...Crypto.createdNowUnique(),
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
const list = expectList(coValue.getCurrentContent());
|
|
290
|
+
|
|
291
|
+
list.append(1, undefined, "trusting");
|
|
292
|
+
list.append(2, undefined, "trusting");
|
|
293
|
+
|
|
294
|
+
const listOnOtherClient = await loadCoValueOrFail(otherClient.node, list.id);
|
|
295
|
+
|
|
296
|
+
otherClientConnection.peerState.gracefulShutdown();
|
|
297
|
+
|
|
298
|
+
listOnOtherClient.append(3, undefined, "trusting");
|
|
299
|
+
|
|
300
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
301
|
+
|
|
302
|
+
list.append(4, undefined, "trusting");
|
|
303
|
+
|
|
304
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
305
|
+
|
|
306
|
+
listOnOtherClient.append(5, undefined, "trusting");
|
|
307
|
+
|
|
308
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
309
|
+
|
|
310
|
+
list.append(6, undefined, "trusting");
|
|
311
|
+
|
|
312
|
+
otherClient.connectToSyncServer();
|
|
313
|
+
|
|
314
|
+
await waitFor(() => {
|
|
315
|
+
expect(list.toJSON()).toEqual([1, 2, 4, 6, 3, 5]);
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
expect(listOnOtherClient.toJSON()).toEqual(list.toJSON());
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
test("syncing prepends with an older timestamp", async () => {
|
|
322
|
+
const client = setupTestNode({
|
|
323
|
+
connected: true,
|
|
324
|
+
});
|
|
325
|
+
const otherClient = setupTestNode({});
|
|
326
|
+
|
|
327
|
+
const otherClientConnection = otherClient.connectToSyncServer();
|
|
328
|
+
|
|
329
|
+
const coValue = client.node.createCoValue({
|
|
330
|
+
type: "colist",
|
|
331
|
+
ruleset: { type: "unsafeAllowAll" },
|
|
332
|
+
meta: null,
|
|
333
|
+
...Crypto.createdNowUnique(),
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
const list = expectList(coValue.getCurrentContent());
|
|
337
|
+
|
|
338
|
+
list.prepend(1, undefined, "trusting");
|
|
339
|
+
list.prepend(2, undefined, "trusting");
|
|
340
|
+
|
|
341
|
+
const listOnOtherClient = await loadCoValueOrFail(otherClient.node, list.id);
|
|
342
|
+
|
|
343
|
+
otherClientConnection.peerState.gracefulShutdown();
|
|
344
|
+
|
|
345
|
+
listOnOtherClient.prepend(3, undefined, "trusting");
|
|
346
|
+
|
|
347
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
348
|
+
|
|
349
|
+
list.prepend(4, undefined, "trusting");
|
|
350
|
+
|
|
351
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
352
|
+
|
|
353
|
+
listOnOtherClient.prepend(5, undefined, "trusting");
|
|
354
|
+
|
|
355
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
356
|
+
|
|
357
|
+
list.prepend(6, undefined, "trusting");
|
|
358
|
+
|
|
359
|
+
otherClient.connectToSyncServer();
|
|
360
|
+
|
|
361
|
+
await waitFor(() => {
|
|
362
|
+
expect(list.toJSON()).toEqual([6, 4, 5, 3, 2, 1]);
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
expect(listOnOtherClient.toJSON()).toEqual(list.toJSON());
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
test("totalValidTransactions should return the number of valid transactions processed", async () => {
|
|
369
|
+
const client = setupTestNode({
|
|
370
|
+
connected: true,
|
|
371
|
+
});
|
|
372
|
+
const otherClient = setupTestNode({});
|
|
373
|
+
|
|
374
|
+
const otherClientConnection = otherClient.connectToSyncServer();
|
|
375
|
+
|
|
376
|
+
const group = client.node.createGroup();
|
|
377
|
+
group.addMember("everyone", "reader");
|
|
378
|
+
|
|
379
|
+
const list = group.createList([1, 2]);
|
|
380
|
+
|
|
381
|
+
const listOnOtherClient = await loadCoValueOrFail(otherClient.node, list.id);
|
|
382
|
+
|
|
383
|
+
otherClientConnection.peerState.gracefulShutdown();
|
|
384
|
+
|
|
385
|
+
group.addMember("everyone", "writer");
|
|
386
|
+
|
|
387
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
388
|
+
|
|
389
|
+
listOnOtherClient.append(3, undefined, "trusting");
|
|
390
|
+
|
|
391
|
+
expect(listOnOtherClient.toJSON()).toEqual([1, 2]);
|
|
392
|
+
expect(listOnOtherClient.totalValidTransactions).toEqual(1);
|
|
393
|
+
|
|
394
|
+
otherClient.connectToSyncServer();
|
|
395
|
+
|
|
396
|
+
await waitFor(() => {
|
|
397
|
+
expect(listOnOtherClient.core.getCurrentContent().toJSON()).toEqual([
|
|
398
|
+
1, 2, 3,
|
|
399
|
+
]);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
expect(
|
|
403
|
+
listOnOtherClient.core.getCurrentContent().totalValidTransactions,
|
|
404
|
+
).toEqual(2);
|
|
405
|
+
});
|
package/src/tests/coMap.test.ts
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
|
-
import { expect, test } from "vitest";
|
|
1
|
+
import { beforeEach, expect, test } from "vitest";
|
|
2
2
|
import { expectMap } from "../coValue.js";
|
|
3
3
|
import { operationToEditEntry } from "../coValues/coMap.js";
|
|
4
4
|
import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
5
5
|
import { LocalNode } from "../localNode.js";
|
|
6
6
|
import { accountOrAgentIDfromSessionID } from "../typeUtils/accountOrAgentIDfromSessionID.js";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
hotSleep,
|
|
9
|
+
loadCoValueOrFail,
|
|
10
|
+
randomAnonymousAccountAndSessionID,
|
|
11
|
+
setupTestNode,
|
|
12
|
+
waitFor,
|
|
13
|
+
} from "./testUtils.js";
|
|
8
14
|
|
|
9
15
|
const Crypto = await WasmCrypto.create();
|
|
10
16
|
|
|
17
|
+
beforeEach(async () => {
|
|
18
|
+
setupTestNode({ isSyncServer: true });
|
|
19
|
+
});
|
|
20
|
+
|
|
11
21
|
test("Empty CoMap works", () => {
|
|
12
22
|
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
|
|
13
23
|
|
|
@@ -207,3 +217,45 @@ test("Can set items in bulk with assign", () => {
|
|
|
207
217
|
key3: "assign3",
|
|
208
218
|
});
|
|
209
219
|
});
|
|
220
|
+
|
|
221
|
+
test("totalValidTransactions should return the number of valid transactions processed", async () => {
|
|
222
|
+
const client = setupTestNode({
|
|
223
|
+
connected: true,
|
|
224
|
+
});
|
|
225
|
+
const otherClient = setupTestNode({});
|
|
226
|
+
|
|
227
|
+
const otherClientConnection = otherClient.connectToSyncServer();
|
|
228
|
+
|
|
229
|
+
const group = client.node.createGroup();
|
|
230
|
+
group.addMember("everyone", "reader");
|
|
231
|
+
|
|
232
|
+
const map = group.createMap({ fromClient: true });
|
|
233
|
+
|
|
234
|
+
const mapOnOtherClient = await loadCoValueOrFail(otherClient.node, map.id);
|
|
235
|
+
|
|
236
|
+
otherClientConnection.peerState.gracefulShutdown();
|
|
237
|
+
|
|
238
|
+
group.addMember("everyone", "writer");
|
|
239
|
+
|
|
240
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
241
|
+
|
|
242
|
+
mapOnOtherClient.set("fromOtherClient", true, "trusting");
|
|
243
|
+
|
|
244
|
+
expect(mapOnOtherClient.totalValidTransactions).toEqual(1);
|
|
245
|
+
expect(mapOnOtherClient.toJSON()).toEqual({
|
|
246
|
+
fromClient: true,
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
otherClient.connectToSyncServer();
|
|
250
|
+
|
|
251
|
+
await waitFor(() => {
|
|
252
|
+
expect(mapOnOtherClient.core.getCurrentContent().toJSON()).toEqual({
|
|
253
|
+
fromClient: true,
|
|
254
|
+
fromOtherClient: true,
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
|
|
258
|
+
expect(
|
|
259
|
+
mapOnOtherClient.core.getCurrentContent().totalValidTransactions,
|
|
260
|
+
).toEqual(2);
|
|
261
|
+
});
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { describe, expect, test } from "vitest";
|
|
1
|
+
import { beforeEach, describe, expect, test } from "vitest";
|
|
2
2
|
import { expectStream } from "../coValue.js";
|
|
3
3
|
import { MAX_RECOMMENDED_TX_SIZE } from "../coValueCore.js";
|
|
4
4
|
import {
|
|
@@ -10,10 +10,19 @@ import {
|
|
|
10
10
|
import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
11
11
|
import { SessionID } from "../ids.js";
|
|
12
12
|
import { LocalNode } from "../localNode.js";
|
|
13
|
-
import {
|
|
13
|
+
import {
|
|
14
|
+
loadCoValueOrFail,
|
|
15
|
+
randomAnonymousAccountAndSessionID,
|
|
16
|
+
setupTestNode,
|
|
17
|
+
waitFor,
|
|
18
|
+
} from "./testUtils.js";
|
|
14
19
|
|
|
15
20
|
const Crypto = await WasmCrypto.create();
|
|
16
21
|
|
|
22
|
+
beforeEach(async () => {
|
|
23
|
+
setupTestNode({ isSyncServer: true });
|
|
24
|
+
});
|
|
25
|
+
|
|
17
26
|
test("Empty CoStream works", () => {
|
|
18
27
|
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
|
|
19
28
|
|
|
@@ -252,6 +261,51 @@ test("When adding large transactions (bigger than MAX_RECOMMENDED_TX_SIZE), we s
|
|
|
252
261
|
);
|
|
253
262
|
});
|
|
254
263
|
|
|
264
|
+
test("totalValidTransactions should return the number of valid transactions processed", async () => {
|
|
265
|
+
const client = setupTestNode({
|
|
266
|
+
connected: true,
|
|
267
|
+
});
|
|
268
|
+
const otherClient = setupTestNode({});
|
|
269
|
+
|
|
270
|
+
const otherClientConnection = otherClient.connectToSyncServer();
|
|
271
|
+
|
|
272
|
+
const group = client.node.createGroup();
|
|
273
|
+
group.addMember("everyone", "reader");
|
|
274
|
+
|
|
275
|
+
const stream = group.createStream();
|
|
276
|
+
stream.push(1, "trusting");
|
|
277
|
+
|
|
278
|
+
const streamOnOtherClient = await loadCoValueOrFail(
|
|
279
|
+
otherClient.node,
|
|
280
|
+
stream.id,
|
|
281
|
+
);
|
|
282
|
+
|
|
283
|
+
otherClientConnection.peerState.gracefulShutdown();
|
|
284
|
+
|
|
285
|
+
group.addMember("everyone", "writer");
|
|
286
|
+
|
|
287
|
+
await new Promise((resolve) => setTimeout(resolve, 50));
|
|
288
|
+
|
|
289
|
+
streamOnOtherClient.push(2, "trusting");
|
|
290
|
+
|
|
291
|
+
expect(streamOnOtherClient.totalValidTransactions).toEqual(1);
|
|
292
|
+
expect(Object.keys(streamOnOtherClient.toJSON()).length).toEqual(1);
|
|
293
|
+
|
|
294
|
+
otherClient.connectToSyncServer();
|
|
295
|
+
|
|
296
|
+
await waitFor(() => {
|
|
297
|
+
expect(
|
|
298
|
+
Object.keys(
|
|
299
|
+
streamOnOtherClient.core.getCurrentContent().toJSON() as object,
|
|
300
|
+
).length,
|
|
301
|
+
).toEqual(2);
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
expect(
|
|
305
|
+
streamOnOtherClient.core.getCurrentContent().totalValidTransactions,
|
|
306
|
+
).toEqual(2);
|
|
307
|
+
});
|
|
308
|
+
|
|
255
309
|
describe("isBinaryStreamEnded", () => {
|
|
256
310
|
function setup() {
|
|
257
311
|
const node = new LocalNode(...randomAnonymousAccountAndSessionID(), Crypto);
|