cojson 0.8.38 → 0.8.41
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 +15 -0
- package/dist/native/PeerState.js +11 -2
- package/dist/native/PeerState.js.map +1 -1
- package/dist/native/{SyncStateSubscriptionManager.js → SyncStateManager.js} +35 -24
- package/dist/native/SyncStateManager.js.map +1 -0
- package/dist/native/coValueCore.js +25 -5
- package/dist/native/coValueCore.js.map +1 -1
- package/dist/native/coValues/coMap.js +98 -103
- package/dist/native/coValues/coMap.js.map +1 -1
- package/dist/native/coValues/coStream.js +17 -6
- package/dist/native/coValues/coStream.js.map +1 -1
- package/dist/native/coValues/group.js +127 -39
- package/dist/native/coValues/group.js.map +1 -1
- package/dist/native/exports.js.map +1 -1
- package/dist/native/localNode.js +5 -2
- package/dist/native/localNode.js.map +1 -1
- package/dist/native/permissions.js +51 -3
- package/dist/native/permissions.js.map +1 -1
- package/dist/native/sync.js +34 -10
- package/dist/native/sync.js.map +1 -1
- package/dist/web/PeerState.js +11 -2
- package/dist/web/PeerState.js.map +1 -1
- package/dist/web/{SyncStateSubscriptionManager.js → SyncStateManager.js} +35 -24
- package/dist/web/SyncStateManager.js.map +1 -0
- package/dist/web/coValueCore.js +25 -5
- package/dist/web/coValueCore.js.map +1 -1
- package/dist/web/coValues/coMap.js +98 -103
- package/dist/web/coValues/coMap.js.map +1 -1
- package/dist/web/coValues/coStream.js +17 -6
- package/dist/web/coValues/coStream.js.map +1 -1
- package/dist/web/coValues/group.js +127 -39
- package/dist/web/coValues/group.js.map +1 -1
- package/dist/web/exports.js.map +1 -1
- package/dist/web/localNode.js +5 -2
- package/dist/web/localNode.js.map +1 -1
- package/dist/web/permissions.js +51 -3
- package/dist/web/permissions.js.map +1 -1
- package/dist/web/sync.js +34 -10
- package/dist/web/sync.js.map +1 -1
- package/package.json +3 -5
- package/src/PeerState.ts +12 -2
- package/src/{SyncStateSubscriptionManager.ts → SyncStateManager.ts} +48 -35
- package/src/coValueCore.ts +43 -9
- package/src/coValues/coMap.ts +126 -127
- package/src/coValues/coStream.ts +27 -10
- package/src/coValues/group.ts +218 -50
- package/src/exports.ts +2 -1
- package/src/localNode.ts +5 -2
- package/src/permissions.ts +71 -8
- package/src/sync.ts +57 -23
- package/src/tests/PeerState.test.ts +49 -0
- package/src/tests/PriorityBasedMessageQueue.test.ts +6 -73
- package/src/tests/{SyncStateSubscriptionManager.test.ts → SyncStateManager.test.ts} +109 -25
- package/src/tests/coMap.test.ts +2 -2
- package/src/tests/group.test.ts +338 -47
- package/src/tests/permissions.test.ts +324 -0
- package/src/tests/sync.test.ts +112 -71
- package/src/tests/testUtils.ts +126 -17
- package/dist/native/SyncStateSubscriptionManager.js.map +0 -1
- package/dist/web/SyncStateSubscriptionManager.js.map +0 -1
package/src/tests/testUtils.ts
CHANGED
|
@@ -1,11 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { metrics } from "@opentelemetry/api";
|
|
2
|
+
import {
|
|
3
|
+
AggregationTemporality,
|
|
4
|
+
InMemoryMetricExporter,
|
|
5
|
+
MeterProvider,
|
|
6
|
+
MetricReader,
|
|
7
|
+
} from "@opentelemetry/sdk-metrics";
|
|
8
|
+
import { expect, vi } from "vitest";
|
|
2
9
|
import { ControlledAgent } from "../coValues/account.js";
|
|
3
10
|
import { WasmCrypto } from "../crypto/WasmCrypto.js";
|
|
4
|
-
import { CoID, RawCoValue } from "../exports.js";
|
|
5
|
-
import { SessionID } from "../ids.js";
|
|
11
|
+
import type { CoID, RawCoValue } from "../exports.js";
|
|
12
|
+
import type { SessionID } from "../ids.js";
|
|
6
13
|
import { LocalNode } from "../localNode.js";
|
|
7
14
|
import { connectedPeers } from "../streamUtils.js";
|
|
8
|
-
import { Peer } from "../sync.js";
|
|
15
|
+
import type { Peer, SyncMessage } from "../sync.js";
|
|
9
16
|
import { expectGroup } from "../typeUtils/expectGroup.js";
|
|
10
17
|
|
|
11
18
|
const Crypto = await WasmCrypto.create();
|
|
@@ -60,17 +67,11 @@ export async function createTwoConnectedNodes(
|
|
|
60
67
|
};
|
|
61
68
|
}
|
|
62
69
|
|
|
63
|
-
export function createThreeConnectedNodes(
|
|
70
|
+
export async function createThreeConnectedNodes(
|
|
64
71
|
node1Role: Peer["role"],
|
|
65
72
|
node2Role: Peer["role"],
|
|
66
73
|
node3Role: Peer["role"],
|
|
67
74
|
) {
|
|
68
|
-
// Setup nodes
|
|
69
|
-
const node1 = createTestNode();
|
|
70
|
-
const node2 = createTestNode();
|
|
71
|
-
const node3 = createTestNode();
|
|
72
|
-
|
|
73
|
-
// Connect nodes initially
|
|
74
75
|
const [node1ToNode2Peer, node2ToNode1Peer] = connectedPeers(
|
|
75
76
|
"node1ToNode2",
|
|
76
77
|
"node2ToNode1",
|
|
@@ -98,12 +99,23 @@ export function createThreeConnectedNodes(
|
|
|
98
99
|
},
|
|
99
100
|
);
|
|
100
101
|
|
|
101
|
-
node1.
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
102
|
+
const node1 = await LocalNode.withNewlyCreatedAccount({
|
|
103
|
+
peersToLoadFrom: [node1ToNode2Peer, node1ToNode3Peer],
|
|
104
|
+
crypto: Crypto,
|
|
105
|
+
creationProps: { name: "Node 1" },
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const node2 = await LocalNode.withNewlyCreatedAccount({
|
|
109
|
+
peersToLoadFrom: [node2ToNode1Peer, node2ToNode3Peer],
|
|
110
|
+
crypto: Crypto,
|
|
111
|
+
creationProps: { name: "Node 2" },
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const node3 = await LocalNode.withNewlyCreatedAccount({
|
|
115
|
+
peersToLoadFrom: [node3ToNode1Peer, node3ToNode2Peer],
|
|
116
|
+
crypto: Crypto,
|
|
117
|
+
creationProps: { name: "Node 3" },
|
|
118
|
+
});
|
|
107
119
|
|
|
108
120
|
return {
|
|
109
121
|
node1,
|
|
@@ -233,6 +245,35 @@ export async function loadCoValueOrFail<V extends RawCoValue>(
|
|
|
233
245
|
return value;
|
|
234
246
|
}
|
|
235
247
|
|
|
248
|
+
export function blockMessageTypeOnOutgoingPeer(
|
|
249
|
+
peer: Peer,
|
|
250
|
+
messageType: SyncMessage["action"],
|
|
251
|
+
) {
|
|
252
|
+
const push = peer.outgoing.push;
|
|
253
|
+
const pushSpy = vi.spyOn(peer.outgoing, "push");
|
|
254
|
+
|
|
255
|
+
const blockedMessages: SyncMessage[] = [];
|
|
256
|
+
|
|
257
|
+
pushSpy.mockImplementation(async (msg) => {
|
|
258
|
+
if (msg.action === messageType) {
|
|
259
|
+
blockedMessages.push(msg);
|
|
260
|
+
return Promise.resolve();
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
return push.call(peer.outgoing, msg);
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
return {
|
|
267
|
+
sendBlockedMessages: async () => {
|
|
268
|
+
for (const msg of blockedMessages) {
|
|
269
|
+
await push.call(peer.outgoing, msg);
|
|
270
|
+
}
|
|
271
|
+
blockedMessages.length = 0;
|
|
272
|
+
},
|
|
273
|
+
unblock: () => pushSpy.mockRestore(),
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
|
|
236
277
|
export function hotSleep(ms: number) {
|
|
237
278
|
const before = Date.now();
|
|
238
279
|
while (Date.now() < before + ms) {
|
|
@@ -240,3 +281,71 @@ export function hotSleep(ms: number) {
|
|
|
240
281
|
}
|
|
241
282
|
return before;
|
|
242
283
|
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* This is a test metric reader that uses an in-memory metric exporter and exposes a method to get the value of a metric given its name and attributes.
|
|
287
|
+
*
|
|
288
|
+
* This is useful for testing the values of metrics that are collected by the SDK.
|
|
289
|
+
*
|
|
290
|
+
* TODO: We may want to rethink how we access metrics (see `getMetricValue` method) to make it more flexible.
|
|
291
|
+
*/
|
|
292
|
+
class TestMetricReader extends MetricReader {
|
|
293
|
+
private _exporter = new InMemoryMetricExporter(
|
|
294
|
+
AggregationTemporality.CUMULATIVE,
|
|
295
|
+
);
|
|
296
|
+
|
|
297
|
+
protected onShutdown(): Promise<void> {
|
|
298
|
+
throw new Error("Method not implemented.");
|
|
299
|
+
}
|
|
300
|
+
protected onForceFlush(): Promise<void> {
|
|
301
|
+
throw new Error("Method not implemented.");
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
async getMetricValue(
|
|
305
|
+
name: string,
|
|
306
|
+
attributes: { [key: string]: string | number } = {},
|
|
307
|
+
) {
|
|
308
|
+
await this.collectAndExport();
|
|
309
|
+
const metric = this._exporter
|
|
310
|
+
.getMetrics()[0]
|
|
311
|
+
?.scopeMetrics[0]?.metrics.find((m) => m.descriptor.name === name);
|
|
312
|
+
|
|
313
|
+
const dp = metric?.dataPoints.find(
|
|
314
|
+
(dp) => JSON.stringify(dp.attributes) === JSON.stringify(attributes),
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
this._exporter.reset();
|
|
318
|
+
|
|
319
|
+
return dp?.value;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
async collectAndExport(): Promise<void> {
|
|
323
|
+
const result = await this.collect();
|
|
324
|
+
await new Promise<void>((resolve, reject) => {
|
|
325
|
+
this._exporter.export(result.resourceMetrics, (result) => {
|
|
326
|
+
if (result.error != null) {
|
|
327
|
+
reject(result.error);
|
|
328
|
+
} else {
|
|
329
|
+
resolve();
|
|
330
|
+
}
|
|
331
|
+
});
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export function createTestMetricReader() {
|
|
337
|
+
const metricReader = new TestMetricReader();
|
|
338
|
+
const success = metrics.setGlobalMeterProvider(
|
|
339
|
+
new MeterProvider({
|
|
340
|
+
readers: [metricReader],
|
|
341
|
+
}),
|
|
342
|
+
);
|
|
343
|
+
|
|
344
|
+
expect(success).toBe(true);
|
|
345
|
+
|
|
346
|
+
return metricReader;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export function tearDownTestMetricReader() {
|
|
350
|
+
metrics.disable();
|
|
351
|
+
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SyncStateSubscriptionManager.js","sourceRoot":"","sources":["../../src/SyncStateSubscriptionManager.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"SyncStateSubscriptionManager.js","sourceRoot":"","sources":["../../src/SyncStateSubscriptionManager.ts"],"names":[],"mappings":"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"}
|