jazz-run 0.8.14 → 0.8.16
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +394 -376
- package/dist/createWorkerAccount.js +39 -17
- package/dist/createWorkerAccount.js.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/startSyncServer.js +4 -4
- package/dist/startSyncServer.js.map +1 -1
- package/dist/test/createWorkerAccount.test.js +17 -7
- package/dist/test/createWorkerAccount.test.js.map +1 -1
- package/package.json +9 -12
- package/src/createWorkerAccount.ts +126 -95
- package/src/index.ts +42 -46
- package/src/startSyncServer.ts +75 -77
- package/src/test/createWorkerAccount.test.ts +30 -20
- package/tsconfig.json +1 -1
- package/.eslintrc.cjs +0 -24
- package/.prettierrc.js +0 -9
@@ -1,7 +1,7 @@
|
|
1
1
|
import { createWebSocketPeer } from "cojson-transport-ws";
|
2
|
-
import { WebSocket } from "ws";
|
3
2
|
import { Account, WasmCrypto, createJazzContext, isControlledAccount, } from "jazz-tools";
|
4
3
|
import { fixedCredentialsAuth, randomSessionProvider } from "jazz-tools";
|
4
|
+
import { WebSocket } from "ws";
|
5
5
|
export const createWorkerAccount = async ({ name, peer: peerAddr, }) => {
|
6
6
|
const crypto = await WasmCrypto.create();
|
7
7
|
const peer = createWebSocketPeer({
|
@@ -24,25 +24,31 @@ export const createWorkerAccount = async ({ name, peer: peerAddr, }) => {
|
|
24
24
|
syncManager.syncCoValue(accountCoValue),
|
25
25
|
syncManager.syncCoValue(accountProfileCoValue),
|
26
26
|
]);
|
27
|
-
await Promise.
|
28
|
-
|
29
|
-
|
27
|
+
await Promise.race([
|
28
|
+
Promise.all([
|
29
|
+
waitForSync(account, peer, accountCoValue),
|
30
|
+
waitForSync(account, peer, accountProfileCoValue),
|
31
|
+
]),
|
32
|
+
failAfter(4000, "Timeout: Didn't manage to upload the account and profile"),
|
30
33
|
]);
|
31
34
|
// Spawn a second peer to double check that the account is fully synced
|
32
35
|
const peer2 = createWebSocketPeer({
|
33
|
-
id: "
|
36
|
+
id: "verifyingPeer",
|
34
37
|
websocket: new WebSocket(peerAddr),
|
35
38
|
role: "server",
|
36
39
|
});
|
37
|
-
await
|
38
|
-
|
39
|
-
|
40
|
-
|
40
|
+
await Promise.race([
|
41
|
+
createJazzContext({
|
42
|
+
auth: fixedCredentialsAuth({
|
43
|
+
accountID: account.id,
|
44
|
+
secret: account._raw.agentSecret,
|
45
|
+
}),
|
46
|
+
sessionProvider: randomSessionProvider,
|
47
|
+
peersToLoadFrom: [peer2],
|
48
|
+
crypto,
|
41
49
|
}),
|
42
|
-
|
43
|
-
|
44
|
-
crypto,
|
45
|
-
});
|
50
|
+
failAfter(4000, "Timeout: Account loading check failed"),
|
51
|
+
]);
|
46
52
|
return {
|
47
53
|
accountId: account.id,
|
48
54
|
agentSecret: account._raw.agentSecret,
|
@@ -51,13 +57,24 @@ export const createWorkerAccount = async ({ name, peer: peerAddr, }) => {
|
|
51
57
|
function waitForSync(account, peer, coValue) {
|
52
58
|
const syncManager = account._raw.core.node.syncManager;
|
53
59
|
const peerState = syncManager.peers[peer.id];
|
60
|
+
if (!peerState) {
|
61
|
+
throw new Error(`Peer state for ${peer.id} not found`);
|
62
|
+
}
|
63
|
+
const isSynced = () => {
|
64
|
+
const knownState = coValue.knownState();
|
65
|
+
if (!peerState.optimisticKnownStates.get(coValue.id)) {
|
66
|
+
return false;
|
67
|
+
}
|
68
|
+
return isEqualSession(knownState.sessions, peerState.optimisticKnownStates.get(coValue.id)?.sessions ?? {});
|
69
|
+
};
|
70
|
+
if (isSynced()) {
|
71
|
+
return Promise.resolve(true);
|
72
|
+
}
|
54
73
|
return new Promise((resolve) => {
|
55
|
-
const unsubscribe = peerState?.optimisticKnownStates.subscribe((id,
|
74
|
+
const unsubscribe = peerState?.optimisticKnownStates.subscribe((id, knownState) => {
|
56
75
|
if (id !== coValue.id)
|
57
76
|
return;
|
58
|
-
|
59
|
-
const synced = isEqualSession(knownState.sessions, peerKnownState.sessions);
|
60
|
-
if (synced) {
|
77
|
+
if (isSynced()) {
|
61
78
|
resolve(true);
|
62
79
|
unsubscribe?.();
|
63
80
|
}
|
@@ -77,4 +94,9 @@ function isEqualSession(a, b) {
|
|
77
94
|
}
|
78
95
|
return true;
|
79
96
|
}
|
97
|
+
function failAfter(ms, errorMessage) {
|
98
|
+
return new Promise((_, reject) => {
|
99
|
+
setTimeout(() => reject(new Error(errorMessage)), ms);
|
100
|
+
});
|
101
|
+
}
|
80
102
|
//# sourceMappingURL=createWorkerAccount.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"createWorkerAccount.js","sourceRoot":"","sources":["../src/createWorkerAccount.ts"],"names":[],"mappings":"
|
1
|
+
{"version":3,"file":"createWorkerAccount.js","sourceRoot":"","sources":["../src/createWorkerAccount.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js.map
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,uCAAuC;AACvC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AAC/C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AACjE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AACzC,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAEvD,MAAM,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;AAE7C,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC;AACrE,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC;
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/startSyncServer.js
CHANGED
@@ -1,10 +1,10 @@
|
|
1
|
+
import { createServer } from "http";
|
1
2
|
import { ControlledAgent, LocalNode, WasmCrypto } from "cojson";
|
2
3
|
import { WebSocketServer } from "ws";
|
3
|
-
import { createServer } from "http";
|
4
|
-
import { createWebSocketPeer } from "cojson-transport-ws";
|
5
|
-
import { SQLiteStorage } from "cojson-storage-sqlite";
|
6
|
-
import { dirname } from "node:path";
|
7
4
|
import { mkdir } from "node:fs/promises";
|
5
|
+
import { dirname } from "node:path";
|
6
|
+
import { SQLiteStorage } from "cojson-storage-sqlite";
|
7
|
+
import { createWebSocketPeer } from "cojson-transport-ws";
|
8
8
|
export const startSyncServer = async ({ port, inMemory, db, }) => {
|
9
9
|
const crypto = await WasmCrypto.create();
|
10
10
|
const server = createServer((req, res) => {
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"startSyncServer.js","sourceRoot":"","sources":["../src/startSyncServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;
|
1
|
+
{"version":3,"file":"startSyncServer.js","sourceRoot":"","sources":["../src/startSyncServer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,MAAM,CAAC;AACpC,OAAO,EAAE,eAAe,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,IAAI,CAAC;AAErC,OAAO,EAAE,KAAK,EAAE,MAAM,kBAAkB,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAE1D,MAAM,CAAC,MAAM,eAAe,GAAG,KAAK,EAAE,EACpC,IAAI,EACJ,QAAQ,EACR,EAAE,GAKH,EAAE,EAAE;IACH,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC;IAEzC,MAAM,MAAM,GAAG,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;QACvC,IAAI,GAAG,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC1B,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;YACnB,GAAG,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAChB,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,GAAG,GAAG,IAAI,eAAe,CAAC,EAAE,QAAQ,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpD,MAAM,WAAW,GAAG,MAAM,CAAC,oBAAoB,EAAE,CAAC;IAClD,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC;IAE/C,MAAM,SAAS,GAAG,IAAI,SAAS,CAC7B,IAAI,eAAe,CAAC,WAAW,EAAE,MAAM,CAAC,EACxC,MAAM,CAAC,kBAAkB,CAAC,OAAO,CAAC,EAClC,MAAM,CACP,CAAC;IAEF,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,MAAM,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAE9C,MAAM,OAAO,GAAG,MAAM,aAAa,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC,CAAC;QAE7D,SAAS,CAAC,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,GAAG,CAAC,EAAE,CAAC,YAAY,EAAE,SAAS,UAAU,CAAC,EAAE,EAAE,GAAG;QAC9C,wCAAwC;QACxC,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,EAAE;YAC/B,EAAE,CAAC,IAAI,CACL,IAAI,CAAC,SAAS,CAAC;gBACb,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE;gBAChB,EAAE,EAAE,SAAS;aACd,CAAC,CACH,CAAC;QACJ,CAAC,EAAE,IAAI,CAAC,CAAC;QAET,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,GAAG,EAAE;YAClB,aAAa,CAAC,OAAO,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;QAEH,MAAM,aAAa,GAChB,GAAG,CAAC,OAAO,CAAC,iBAAiB,CAAwB;YACpD,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACf,EAAE,IAAI,EAAE,IAAI,GAAG,CAAC,MAAM,CAAC,aAAa,CAAC;QAEzC,MAAM,QAAQ,GAAG,aAAa,GAAG,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QAEhE,SAAS,CAAC,WAAW,CAAC,OAAO,CAC3B,mBAAmB,CAAC;YAClB,EAAE,EAAE,QAAQ;YACZ,IAAI,EAAE,QAAQ;YACd,SAAS,EAAE,EAAE;YACb,WAAW,EAAE,KAAK;YAClB,iBAAiB,EAAE,KAAK;SACzB,CAAC,CACH,CAAC;QAEF,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,uBAAuB,QAAQ,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC;IAC9E,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,EAAE,CAAC,SAAS,EAAE,SAAS,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI;QACrD,IAAI,GAAG,CAAC,GAAG,KAAK,SAAS,EAAE,CAAC;YAC1B,GAAG,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,IAAI,CAAC,EAAE;gBACnD,GAAG,CAAC,IAAI,CAAC,YAAY,EAAE,EAAE,EAAE,GAAG,CAAC,CAAC;YAClC,CAAC,CAAC,CAAC;QACL,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC;IAEjD,OAAO,MAAM,CAAC;AAChB,CAAC,CAAC"}
|
@@ -1,23 +1,33 @@
|
|
1
|
-
import { describe,
|
2
|
-
import { startSyncServer } from "../startSyncServer.js";
|
1
|
+
import { describe, expect, it, onTestFinished } from "vitest";
|
3
2
|
import { createWorkerAccount } from "../createWorkerAccount.js";
|
3
|
+
import { startSyncServer } from "../startSyncServer.js";
|
4
4
|
describe("createWorkerAccount - integration tests", () => {
|
5
5
|
it("should create a worker account using the local sync server", async () => {
|
6
6
|
// Pass port: undefined to let the server choose a random port
|
7
|
-
const server = await startSyncServer({
|
7
|
+
const server = await startSyncServer({
|
8
|
+
port: undefined,
|
9
|
+
inMemory: true,
|
10
|
+
db: "",
|
11
|
+
});
|
8
12
|
onTestFinished(() => {
|
9
13
|
server.close();
|
10
14
|
});
|
11
15
|
const address = server.address();
|
12
|
-
if (typeof address !==
|
13
|
-
throw new Error(
|
16
|
+
if (typeof address !== "object" || address === null) {
|
17
|
+
throw new Error("Server address is not an object");
|
14
18
|
}
|
15
|
-
const { accountId, agentSecret } = await createWorkerAccount({
|
19
|
+
const { accountId, agentSecret } = await createWorkerAccount({
|
20
|
+
name: "test",
|
21
|
+
peer: `ws://localhost:${address.port}`,
|
22
|
+
});
|
16
23
|
expect(accountId).toBeDefined();
|
17
24
|
expect(agentSecret).toBeDefined();
|
18
25
|
});
|
19
26
|
it("should create a worker account using the Jazz cloud", async () => {
|
20
|
-
const { accountId, agentSecret } = await createWorkerAccount({
|
27
|
+
const { accountId, agentSecret } = await createWorkerAccount({
|
28
|
+
name: "test",
|
29
|
+
peer: `wss://cloud.jazz.tools`,
|
30
|
+
});
|
21
31
|
expect(accountId).toBeDefined();
|
22
32
|
expect(agentSecret).toBeDefined();
|
23
33
|
});
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"createWorkerAccount.test.js","sourceRoot":"","sources":["../../src/test/createWorkerAccount.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,
|
1
|
+
{"version":3,"file":"createWorkerAccount.test.js","sourceRoot":"","sources":["../../src/test/createWorkerAccount.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAExD,QAAQ,CAAC,yCAAyC,EAAE,GAAG,EAAE;IACvD,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QAC1E,8DAA8D;QAC9D,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC;YACnC,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE,IAAI;YACd,EAAE,EAAE,EAAE;SACP,CAAC,CAAC;QAEH,cAAc,CAAC,GAAG,EAAE;YAClB,MAAM,CAAC,KAAK,EAAE,CAAC;QACjB,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;QAEjC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACpD,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACrD,CAAC;QAED,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,MAAM,mBAAmB,CAAC;YAC3D,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,kBAAkB,OAAO,CAAC,IAAI,EAAE;SACvC,CAAC,CAAC;QAEH,MAAM,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;QAChC,MAAM,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACnE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,MAAM,mBAAmB,CAAC;YAC3D,IAAI,EAAE,MAAM;YACZ,IAAI,EAAE,wBAAwB;SAC/B,CAAC,CAAC;QAEH,MAAM,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;QAChC,MAAM,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
package/package.json
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
"bin": "./dist/index.js",
|
4
4
|
"type": "module",
|
5
5
|
"license": "MIT",
|
6
|
-
"version": "0.8.
|
6
|
+
"version": "0.8.16",
|
7
7
|
"dependencies": {
|
8
8
|
"@effect/cli": "^0.41.2",
|
9
9
|
"@effect/platform-node": "^0.57.2",
|
@@ -11,24 +11,21 @@
|
|
11
11
|
"@effect/printer-ansi": "^0.34.5",
|
12
12
|
"@effect/schema": "^0.71.1",
|
13
13
|
"@effect/typeclass": "^0.25.5",
|
14
|
-
"cojson": "0.8.
|
15
|
-
"cojson-storage-sqlite": "0.8.
|
16
|
-
"cojson-transport-ws": "0.8.
|
14
|
+
"cojson": "0.8.16",
|
15
|
+
"cojson-storage-sqlite": "0.8.16",
|
16
|
+
"cojson-transport-ws": "0.8.16",
|
17
17
|
"effect": "^3.6.5",
|
18
|
-
"jazz-tools": "0.8.
|
18
|
+
"jazz-tools": "0.8.16",
|
19
19
|
"ws": "^8.14.2"
|
20
20
|
},
|
21
21
|
"devDependencies": {
|
22
22
|
"@types/ws": "^8.5.5",
|
23
23
|
"typescript": "^5.3.3"
|
24
24
|
},
|
25
|
-
"lint-staged": {
|
26
|
-
"*.{ts,tsx}": "eslint --fix",
|
27
|
-
"*.{js,jsx,mdx,json}": "prettier --write"
|
28
|
-
},
|
29
25
|
"scripts": {
|
30
|
-
"lint": "
|
31
|
-
"format": "
|
32
|
-
"build": "
|
26
|
+
"format-and-lint": "biome check .",
|
27
|
+
"format-and-lint:fix": "biome check . --write",
|
28
|
+
"build": "rm -rf ./dist && tsc --sourceMap --outDir dist",
|
29
|
+
"build-and-run": "pnpm turbo build && chmod +x ./dist/index.js && ./dist/index.js sync --in-memory"
|
33
30
|
}
|
34
31
|
}
|
@@ -1,114 +1,145 @@
|
|
1
|
+
import { CoValueCore, Profile } from "cojson";
|
1
2
|
import { createWebSocketPeer } from "cojson-transport-ws";
|
2
|
-
import { WebSocket } from "ws";
|
3
3
|
import {
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
Account,
|
5
|
+
CoMap,
|
6
|
+
Peer,
|
7
|
+
WasmCrypto,
|
8
|
+
createJazzContext,
|
9
|
+
isControlledAccount,
|
9
10
|
} from "jazz-tools";
|
10
11
|
import { fixedCredentialsAuth, randomSessionProvider } from "jazz-tools";
|
11
|
-
import {
|
12
|
+
import { WebSocket } from "ws";
|
12
13
|
|
13
14
|
export const createWorkerAccount = async ({
|
14
|
-
|
15
|
-
|
15
|
+
name,
|
16
|
+
peer: peerAddr,
|
16
17
|
}: {
|
17
|
-
|
18
|
-
|
18
|
+
name: string;
|
19
|
+
peer: string;
|
19
20
|
}) => {
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
21
|
+
const crypto = await WasmCrypto.create();
|
22
|
+
|
23
|
+
const peer = createWebSocketPeer({
|
24
|
+
id: "upstream",
|
25
|
+
websocket: new WebSocket(peerAddr),
|
26
|
+
role: "server",
|
27
|
+
});
|
28
|
+
|
29
|
+
const account = await Account.create({
|
30
|
+
creationProps: { name },
|
31
|
+
peersToLoadFrom: [peer],
|
32
|
+
crypto,
|
33
|
+
});
|
34
|
+
|
35
|
+
if (!isControlledAccount(account)) {
|
36
|
+
throw new Error("account is not a controlled account");
|
37
|
+
}
|
38
|
+
|
39
|
+
const accountCoValue = account._raw.core;
|
40
|
+
const accountProfileCoValue = account.profile!._raw.core;
|
41
|
+
const syncManager = account._raw.core.node.syncManager;
|
42
|
+
|
43
|
+
await Promise.all([
|
44
|
+
syncManager.syncCoValue(accountCoValue),
|
45
|
+
syncManager.syncCoValue(accountProfileCoValue),
|
46
|
+
]);
|
47
|
+
|
48
|
+
await Promise.race([
|
49
|
+
Promise.all([
|
50
|
+
waitForSync(account, peer, accountCoValue),
|
51
|
+
waitForSync(account, peer, accountProfileCoValue),
|
52
|
+
]),
|
53
|
+
failAfter(
|
54
|
+
4_000,
|
55
|
+
"Timeout: Didn't manage to upload the account and profile",
|
56
|
+
),
|
57
|
+
]);
|
58
|
+
|
59
|
+
// Spawn a second peer to double check that the account is fully synced
|
60
|
+
const peer2 = createWebSocketPeer({
|
61
|
+
id: "verifyingPeer",
|
62
|
+
websocket: new WebSocket(peerAddr),
|
63
|
+
role: "server",
|
64
|
+
});
|
65
|
+
|
66
|
+
await Promise.race([
|
67
|
+
createJazzContext({
|
68
|
+
auth: fixedCredentialsAuth({
|
69
|
+
accountID: account.id,
|
70
|
+
secret: account._raw.agentSecret,
|
71
|
+
}),
|
72
|
+
sessionProvider: randomSessionProvider,
|
73
|
+
peersToLoadFrom: [peer2],
|
74
|
+
crypto,
|
75
|
+
}),
|
76
|
+
failAfter(4_000, "Timeout: Account loading check failed"),
|
77
|
+
]);
|
78
|
+
|
79
|
+
return {
|
80
|
+
accountId: account.id,
|
81
|
+
agentSecret: account._raw.agentSecret,
|
82
|
+
};
|
73
83
|
};
|
74
84
|
|
75
85
|
function waitForSync(account: Account, peer: Peer, coValue: CoValueCore) {
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
return new Promise((resolve) => {
|
80
|
-
const unsubscribe = peerState?.optimisticKnownStates.subscribe(
|
81
|
-
(id, peerKnownState) => {
|
82
|
-
if (id !== coValue.id) return;
|
83
|
-
|
84
|
-
const knownState = coValue.knownState();
|
85
|
-
|
86
|
-
const synced = isEqualSession(
|
87
|
-
knownState.sessions,
|
88
|
-
peerKnownState.sessions,
|
89
|
-
);
|
90
|
-
if (synced) {
|
91
|
-
resolve(true);
|
92
|
-
unsubscribe?.();
|
93
|
-
}
|
94
|
-
},
|
95
|
-
);
|
96
|
-
});
|
97
|
-
}
|
86
|
+
const syncManager = account._raw.core.node.syncManager;
|
87
|
+
const peerState = syncManager.peers[peer.id];
|
98
88
|
|
99
|
-
|
100
|
-
|
101
|
-
|
89
|
+
if (!peerState) {
|
90
|
+
throw new Error(`Peer state for ${peer.id} not found`);
|
91
|
+
}
|
92
|
+
|
93
|
+
const isSynced = () => {
|
94
|
+
const knownState = coValue.knownState();
|
102
95
|
|
103
|
-
if (
|
104
|
-
|
96
|
+
if (!peerState.optimisticKnownStates.get(coValue.id)) {
|
97
|
+
return false;
|
105
98
|
}
|
106
99
|
|
107
|
-
|
108
|
-
|
109
|
-
|
100
|
+
return isEqualSession(
|
101
|
+
knownState.sessions,
|
102
|
+
peerState.optimisticKnownStates.get(coValue.id)?.sessions ?? {},
|
103
|
+
);
|
104
|
+
};
|
105
|
+
|
106
|
+
if (isSynced()) {
|
107
|
+
return Promise.resolve(true);
|
108
|
+
}
|
109
|
+
|
110
|
+
return new Promise((resolve) => {
|
111
|
+
const unsubscribe = peerState?.optimisticKnownStates.subscribe(
|
112
|
+
(id, knownState) => {
|
113
|
+
if (id !== coValue.id) return;
|
114
|
+
|
115
|
+
if (isSynced()) {
|
116
|
+
resolve(true);
|
117
|
+
unsubscribe?.();
|
110
118
|
}
|
119
|
+
},
|
120
|
+
);
|
121
|
+
});
|
122
|
+
}
|
123
|
+
|
124
|
+
function isEqualSession(a: Record<string, number>, b: Record<string, number>) {
|
125
|
+
const keysA = Object.keys(a);
|
126
|
+
const keysB = Object.keys(b);
|
127
|
+
|
128
|
+
if (keysA.length !== keysB.length) {
|
129
|
+
return false;
|
130
|
+
}
|
131
|
+
|
132
|
+
for (const sessionId of keysA) {
|
133
|
+
if (a[sessionId] !== b[sessionId]) {
|
134
|
+
return false;
|
111
135
|
}
|
136
|
+
}
|
137
|
+
|
138
|
+
return true;
|
139
|
+
}
|
112
140
|
|
113
|
-
|
141
|
+
function failAfter(ms: number, errorMessage: string) {
|
142
|
+
return new Promise((_, reject) => {
|
143
|
+
setTimeout(() => reject(new Error(errorMessage)), ms);
|
144
|
+
});
|
114
145
|
}
|
package/src/index.ts
CHANGED
@@ -10,80 +10,76 @@ const jazzTools = Command.make("jazz-tools");
|
|
10
10
|
|
11
11
|
const nameOption = Options.text("name").pipe(Options.withAlias("n"));
|
12
12
|
const peerOption = Options.text("peer")
|
13
|
-
|
14
|
-
|
13
|
+
.pipe(Options.withAlias("p"))
|
14
|
+
.pipe(Options.withDefault("wss://cloud.jazz.tools"));
|
15
15
|
|
16
16
|
const createAccountCommand = Command.make(
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
"create",
|
18
|
+
{ name: nameOption, peer: peerOption },
|
19
|
+
({ name, peer }) => {
|
20
|
+
return Effect.gen(function* () {
|
21
|
+
const { accountId, agentSecret } = yield* Effect.promise(() =>
|
22
|
+
createWorkerAccount({ name, peer }),
|
23
|
+
);
|
24
24
|
|
25
|
-
|
25
|
+
yield* Console.log(`# Credentials for Jazz account "${name}":
|
26
26
|
JAZZ_WORKER_ACCOUNT=${accountId}
|
27
27
|
JAZZ_WORKER_SECRET=${agentSecret}
|
28
28
|
`);
|
29
|
-
|
30
|
-
|
29
|
+
});
|
30
|
+
},
|
31
31
|
);
|
32
32
|
|
33
33
|
const accountCommand = Command.make("account").pipe(
|
34
|
-
|
34
|
+
Command.withSubcommands([createAccountCommand]),
|
35
35
|
);
|
36
36
|
|
37
37
|
const portOption = Options.text("port")
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
38
|
+
.pipe(Options.withAlias("p"))
|
39
|
+
.pipe(
|
40
|
+
Options.withDescription(
|
41
|
+
"Select a different port for the WebSocket server. Default is 4200",
|
42
|
+
),
|
43
|
+
)
|
44
|
+
.pipe(Options.withDefault("4200"));
|
45
45
|
|
46
46
|
const inMemoryOption = Options.boolean("in-memory").pipe(
|
47
|
-
|
47
|
+
Options.withDescription("Use an in-memory storage instead of file-based"),
|
48
48
|
);
|
49
49
|
|
50
50
|
const dbOption = Options.file("db")
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
51
|
+
.pipe(
|
52
|
+
Options.withDescription(
|
53
|
+
"The path to the file where to store the data. Default is 'sync-db/storage.db'",
|
54
|
+
),
|
55
|
+
)
|
56
|
+
.pipe(Options.withDefault("sync-db/storage.db"));
|
57
57
|
|
58
58
|
export const startSyncServerCommand = Command.make(
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
startSyncServer({ port, inMemory, db }),
|
65
|
-
);
|
59
|
+
"sync",
|
60
|
+
{ port: portOption, inMemory: inMemoryOption, db: dbOption },
|
61
|
+
({ port, inMemory, db }) => {
|
62
|
+
return Effect.gen(function* () {
|
63
|
+
yield* Effect.promise(() => startSyncServer({ port, inMemory, db }));
|
66
64
|
|
67
|
-
|
68
|
-
`COJSON sync server listening on ws://127.0.0.1:${port}`,
|
69
|
-
);
|
65
|
+
Console.log(`COJSON sync server listening on ws://127.0.0.1:${port}`);
|
70
66
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
67
|
+
// Keep the server up
|
68
|
+
yield* Effect.never;
|
69
|
+
});
|
70
|
+
},
|
75
71
|
);
|
76
72
|
|
77
73
|
const command = jazzTools.pipe(
|
78
|
-
|
74
|
+
Command.withSubcommands([accountCommand, startSyncServerCommand]),
|
79
75
|
);
|
80
76
|
|
81
77
|
const cli = Command.run(command, {
|
82
|
-
|
83
|
-
|
78
|
+
name: "Jazz CLI Tools",
|
79
|
+
version: "v0.8.11",
|
84
80
|
});
|
85
81
|
|
86
82
|
Effect.suspend(() => cli(process.argv)).pipe(
|
87
|
-
|
88
|
-
|
83
|
+
Effect.provide(NodeContext.layer),
|
84
|
+
NodeRuntime.runMain,
|
89
85
|
);
|