jazz-run 0.8.11 → 0.8.13
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 +19 -0
 - package/dist/createWorkerAccount.js +80 -0
 - package/dist/createWorkerAccount.js.map +1 -0
 - package/dist/index.js +36 -6
 - package/dist/index.js.map +1 -1
 - package/dist/startSyncServer.js +60 -0
 - package/dist/startSyncServer.js.map +1 -0
 - package/dist/test/createWorkerAccount.test.js +25 -0
 - package/dist/test/createWorkerAccount.test.js.map +1 -0
 - package/package.json +5 -5
 - package/src/createWorkerAccount.ts +114 -0
 - package/src/index.ts +74 -6
 - package/src/startSyncServer.ts +95 -0
 - package/src/test/createWorkerAccount.test.ts +32 -0
 - package/dist/accountCreate.js +0 -88
 - package/dist/accountCreate.js.map +0 -1
 - package/dist/startSync.js +0 -76
 - package/dist/startSync.js.map +0 -1
 - package/src/accountCreate.ts +0 -125
 - package/src/startSync.ts +0 -125
 
    
        package/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,5 +1,24 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # jazz-run
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
      
 3 
     | 
    
         
            +
            ## 0.8.13
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            ### Patch Changes
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            -   Updated dependencies [fd011d7]
         
     | 
| 
      
 8 
     | 
    
         
            +
            -   Updated dependencies [e0dd006]
         
     | 
| 
      
 9 
     | 
    
         
            +
                -   jazz-tools@0.8.13
         
     | 
| 
      
 10 
     | 
    
         
            +
                -   cojson-transport-ws@0.8.13
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            ## 0.8.12
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            ### Patch Changes
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            -   Updated dependencies [6ed75eb]
         
     | 
| 
      
 17 
     | 
    
         
            +
                -   cojson-storage-sqlite@0.8.12
         
     | 
| 
      
 18 
     | 
    
         
            +
                -   cojson@0.8.12
         
     | 
| 
      
 19 
     | 
    
         
            +
                -   cojson-transport-ws@0.8.12
         
     | 
| 
      
 20 
     | 
    
         
            +
                -   jazz-tools@0.8.12
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
       3 
22 
     | 
    
         
             
            ## 0.8.11
         
     | 
| 
       4 
23 
     | 
    
         | 
| 
       5 
24 
     | 
    
         
             
            ### Patch Changes
         
     | 
| 
         @@ -0,0 +1,80 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import { createWebSocketPeer } from "cojson-transport-ws";
         
     | 
| 
      
 2 
     | 
    
         
            +
            import { WebSocket } from "ws";
         
     | 
| 
      
 3 
     | 
    
         
            +
            import { Account, WasmCrypto, createJazzContext, isControlledAccount, } from "jazz-tools";
         
     | 
| 
      
 4 
     | 
    
         
            +
            import { fixedCredentialsAuth, randomSessionProvider } from "jazz-tools";
         
     | 
| 
      
 5 
     | 
    
         
            +
            export const createWorkerAccount = async ({ name, peer: peerAddr, }) => {
         
     | 
| 
      
 6 
     | 
    
         
            +
                const crypto = await WasmCrypto.create();
         
     | 
| 
      
 7 
     | 
    
         
            +
                const peer = createWebSocketPeer({
         
     | 
| 
      
 8 
     | 
    
         
            +
                    id: "upstream",
         
     | 
| 
      
 9 
     | 
    
         
            +
                    websocket: new WebSocket(peerAddr),
         
     | 
| 
      
 10 
     | 
    
         
            +
                    role: "server",
         
     | 
| 
      
 11 
     | 
    
         
            +
                });
         
     | 
| 
      
 12 
     | 
    
         
            +
                const account = await Account.create({
         
     | 
| 
      
 13 
     | 
    
         
            +
                    creationProps: { name },
         
     | 
| 
      
 14 
     | 
    
         
            +
                    peersToLoadFrom: [peer],
         
     | 
| 
      
 15 
     | 
    
         
            +
                    crypto,
         
     | 
| 
      
 16 
     | 
    
         
            +
                });
         
     | 
| 
      
 17 
     | 
    
         
            +
                if (!isControlledAccount(account)) {
         
     | 
| 
      
 18 
     | 
    
         
            +
                    throw new Error("account is not a controlled account");
         
     | 
| 
      
 19 
     | 
    
         
            +
                }
         
     | 
| 
      
 20 
     | 
    
         
            +
                const accountCoValue = account._raw.core;
         
     | 
| 
      
 21 
     | 
    
         
            +
                const accountProfileCoValue = account.profile._raw.core;
         
     | 
| 
      
 22 
     | 
    
         
            +
                const syncManager = account._raw.core.node.syncManager;
         
     | 
| 
      
 23 
     | 
    
         
            +
                await Promise.all([
         
     | 
| 
      
 24 
     | 
    
         
            +
                    syncManager.syncCoValue(accountCoValue),
         
     | 
| 
      
 25 
     | 
    
         
            +
                    syncManager.syncCoValue(accountProfileCoValue),
         
     | 
| 
      
 26 
     | 
    
         
            +
                ]);
         
     | 
| 
      
 27 
     | 
    
         
            +
                await Promise.all([
         
     | 
| 
      
 28 
     | 
    
         
            +
                    waitForSync(account, peer, accountCoValue),
         
     | 
| 
      
 29 
     | 
    
         
            +
                    waitForSync(account, peer, accountProfileCoValue),
         
     | 
| 
      
 30 
     | 
    
         
            +
                ]);
         
     | 
| 
      
 31 
     | 
    
         
            +
                // Spawn a second peer to double check that the account is fully synced
         
     | 
| 
      
 32 
     | 
    
         
            +
                const peer2 = createWebSocketPeer({
         
     | 
| 
      
 33 
     | 
    
         
            +
                    id: "upstream2",
         
     | 
| 
      
 34 
     | 
    
         
            +
                    websocket: new WebSocket(peerAddr),
         
     | 
| 
      
 35 
     | 
    
         
            +
                    role: "server",
         
     | 
| 
      
 36 
     | 
    
         
            +
                });
         
     | 
| 
      
 37 
     | 
    
         
            +
                await createJazzContext({
         
     | 
| 
      
 38 
     | 
    
         
            +
                    auth: fixedCredentialsAuth({
         
     | 
| 
      
 39 
     | 
    
         
            +
                        accountID: account.id,
         
     | 
| 
      
 40 
     | 
    
         
            +
                        secret: account._raw.agentSecret,
         
     | 
| 
      
 41 
     | 
    
         
            +
                    }),
         
     | 
| 
      
 42 
     | 
    
         
            +
                    sessionProvider: randomSessionProvider,
         
     | 
| 
      
 43 
     | 
    
         
            +
                    peersToLoadFrom: [peer2],
         
     | 
| 
      
 44 
     | 
    
         
            +
                    crypto,
         
     | 
| 
      
 45 
     | 
    
         
            +
                });
         
     | 
| 
      
 46 
     | 
    
         
            +
                return {
         
     | 
| 
      
 47 
     | 
    
         
            +
                    accountId: account.id,
         
     | 
| 
      
 48 
     | 
    
         
            +
                    agentSecret: account._raw.agentSecret,
         
     | 
| 
      
 49 
     | 
    
         
            +
                };
         
     | 
| 
      
 50 
     | 
    
         
            +
            };
         
     | 
| 
      
 51 
     | 
    
         
            +
            function waitForSync(account, peer, coValue) {
         
     | 
| 
      
 52 
     | 
    
         
            +
                const syncManager = account._raw.core.node.syncManager;
         
     | 
| 
      
 53 
     | 
    
         
            +
                const peerState = syncManager.peers[peer.id];
         
     | 
| 
      
 54 
     | 
    
         
            +
                return new Promise((resolve) => {
         
     | 
| 
      
 55 
     | 
    
         
            +
                    const unsubscribe = peerState?.optimisticKnownStates.subscribe((id, peerKnownState) => {
         
     | 
| 
      
 56 
     | 
    
         
            +
                        if (id !== coValue.id)
         
     | 
| 
      
 57 
     | 
    
         
            +
                            return;
         
     | 
| 
      
 58 
     | 
    
         
            +
                        const knownState = coValue.knownState();
         
     | 
| 
      
 59 
     | 
    
         
            +
                        const synced = isEqualSession(knownState.sessions, peerKnownState.sessions);
         
     | 
| 
      
 60 
     | 
    
         
            +
                        if (synced) {
         
     | 
| 
      
 61 
     | 
    
         
            +
                            resolve(true);
         
     | 
| 
      
 62 
     | 
    
         
            +
                            unsubscribe?.();
         
     | 
| 
      
 63 
     | 
    
         
            +
                        }
         
     | 
| 
      
 64 
     | 
    
         
            +
                    });
         
     | 
| 
      
 65 
     | 
    
         
            +
                });
         
     | 
| 
      
 66 
     | 
    
         
            +
            }
         
     | 
| 
      
 67 
     | 
    
         
            +
            function isEqualSession(a, b) {
         
     | 
| 
      
 68 
     | 
    
         
            +
                const keysA = Object.keys(a);
         
     | 
| 
      
 69 
     | 
    
         
            +
                const keysB = Object.keys(b);
         
     | 
| 
      
 70 
     | 
    
         
            +
                if (keysA.length !== keysB.length) {
         
     | 
| 
      
 71 
     | 
    
         
            +
                    return false;
         
     | 
| 
      
 72 
     | 
    
         
            +
                }
         
     | 
| 
      
 73 
     | 
    
         
            +
                for (const sessionId of keysA) {
         
     | 
| 
      
 74 
     | 
    
         
            +
                    if (a[sessionId] !== b[sessionId]) {
         
     | 
| 
      
 75 
     | 
    
         
            +
                        return false;
         
     | 
| 
      
 76 
     | 
    
         
            +
                    }
         
     | 
| 
      
 77 
     | 
    
         
            +
                }
         
     | 
| 
      
 78 
     | 
    
         
            +
                return true;
         
     | 
| 
      
 79 
     | 
    
         
            +
            }
         
     | 
| 
      
 80 
     | 
    
         
            +
            //# sourceMappingURL=createWorkerAccount.js.map
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            {"version":3,"file":"createWorkerAccount.js","sourceRoot":"","sources":["../src/createWorkerAccount.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAC1D,OAAO,EAAE,SAAS,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,EACH,OAAO,EAEP,UAAU,EACV,iBAAiB,EACjB,mBAAmB,GACtB,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,oBAAoB,EAAE,qBAAqB,EAAE,MAAM,YAAY,CAAC;AAGzE,MAAM,CAAC,MAAM,mBAAmB,GAAG,KAAK,EAAE,EACtC,IAAI,EACJ,IAAI,EAAE,QAAQ,GAIjB,EAAE,EAAE;IACD,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,MAAM,EAAE,CAAC;IAEzC,MAAM,IAAI,GAAG,mBAAmB,CAAC;QAC7B,EAAE,EAAE,UAAU;QACd,SAAS,EAAE,IAAI,SAAS,CAAC,QAAQ,CAAC;QAClC,IAAI,EAAE,QAAQ;KACjB,CAAC,CAAC;IAEH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC;QACjC,aAAa,EAAE,EAAE,IAAI,EAAE;QACvB,eAAe,EAAE,CAAC,IAAI,CAAC;QACvB,MAAM;KACT,CAAC,CAAC;IAEH,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,EAAE,CAAC;QAChC,MAAM,IAAI,KAAK,CAAC,qCAAqC,CAAC,CAAC;IAC3D,CAAC;IAED,MAAM,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC;IACzC,MAAM,qBAAqB,GAAG,OAAO,CAAC,OAAQ,CAAC,IAAI,CAAC,IAAI,CAAC;IACzD,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;IAEvD,MAAM,OAAO,CAAC,GAAG,CAAC;QACd,WAAW,CAAC,WAAW,CAAC,cAAc,CAAC;QACvC,WAAW,CAAC,WAAW,CAAC,qBAAqB,CAAC;KACjD,CAAC,CAAC;IAEH,MAAM,OAAO,CAAC,GAAG,CAAC;QACd,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE,cAAc,CAAC;QAC1C,WAAW,CAAC,OAAO,EAAE,IAAI,EAAE,qBAAqB,CAAC;KACpD,CAAC,CAAC;IAEH,uEAAuE;IACvE,MAAM,KAAK,GAAG,mBAAmB,CAAC;QAC9B,EAAE,EAAE,WAAW;QACf,SAAS,EAAE,IAAI,SAAS,CAAC,QAAQ,CAAC;QAClC,IAAI,EAAE,QAAQ;KACjB,CAAC,CAAC;IAEH,MAAM,iBAAiB,CAAC;QACpB,IAAI,EAAE,oBAAoB,CAAC;YACvB,SAAS,EAAE,OAAO,CAAC,EAAE;YACrB,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW;SACnC,CAAC;QACF,eAAe,EAAE,qBAAqB;QACtC,eAAe,EAAE,CAAC,KAAK,CAAC;QACxB,MAAM;KACT,CAAC,CAAC;IAEH,OAAO;QACH,SAAS,EAAE,OAAO,CAAC,EAAE;QACrB,WAAW,EAAE,OAAO,CAAC,IAAI,CAAC,WAAW;KACxC,CAAC;AACN,CAAC,CAAC;AAEF,SAAS,WAAW,CAAC,OAAgB,EAAE,IAAU,EAAE,OAAoB;IACnE,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC;IACvD,MAAM,SAAS,GAAG,WAAW,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAE7C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC3B,MAAM,WAAW,GAAG,SAAS,EAAE,qBAAqB,CAAC,SAAS,CAC1D,CAAC,EAAE,EAAE,cAAc,EAAE,EAAE;YACnB,IAAI,EAAE,KAAK,OAAO,CAAC,EAAE;gBAAE,OAAO;YAE9B,MAAM,UAAU,GAAG,OAAO,CAAC,UAAU,EAAE,CAAC;YAExC,MAAM,MAAM,GAAG,cAAc,CACzB,UAAU,CAAC,QAAQ,EACnB,cAAc,CAAC,QAAQ,CAC1B,CAAC;YACF,IAAI,MAAM,EAAE,CAAC;gBACT,OAAO,CAAC,IAAI,CAAC,CAAC;gBACd,WAAW,EAAE,EAAE,CAAC;YACpB,CAAC;QACL,CAAC,CACJ,CAAC;IACN,CAAC,CAAC,CAAC;AACP,CAAC;AAED,SAAS,cAAc,CAAC,CAAyB,EAAE,CAAyB;IACxE,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC7B,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAE7B,IAAI,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC;QAChC,OAAO,KAAK,CAAC;IACjB,CAAC;IAED,KAAK,MAAM,SAAS,IAAI,KAAK,EAAE,CAAC;QAC5B,IAAI,CAAC,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC;YAChC,OAAO,KAAK,CAAC;QACjB,CAAC;IACL,CAAC;IAED,OAAO,IAAI,CAAC;AAChB,CAAC"}
         
     | 
    
        package/dist/index.js
    CHANGED
    
    | 
         @@ -1,15 +1,45 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            #!/usr/bin/env node
         
     | 
| 
       2 
2 
     | 
    
         
             
            /* istanbul ignore file -- @preserve */
         
     | 
| 
       3 
     | 
    
         
            -
            import { Command } from "@effect/cli";
         
     | 
| 
      
 3 
     | 
    
         
            +
            import { Command, Options } from "@effect/cli";
         
     | 
| 
       4 
4 
     | 
    
         
             
            import { NodeContext, NodeRuntime } from "@effect/platform-node";
         
     | 
| 
       5 
     | 
    
         
            -
            import { Effect } from "effect";
         
     | 
| 
       6 
     | 
    
         
            -
            import {  
     | 
| 
       7 
     | 
    
         
            -
            import {  
     | 
| 
      
 5 
     | 
    
         
            +
            import { Console, Effect } from "effect";
         
     | 
| 
      
 6 
     | 
    
         
            +
            import { createWorkerAccount } from "./createWorkerAccount.js";
         
     | 
| 
      
 7 
     | 
    
         
            +
            import { startSyncServer } from "./startSyncServer.js";
         
     | 
| 
       8 
8 
     | 
    
         
             
            const jazzTools = Command.make("jazz-tools");
         
     | 
| 
       9 
     | 
    
         
            -
            const  
     | 
| 
      
 9 
     | 
    
         
            +
            const nameOption = Options.text("name").pipe(Options.withAlias("n"));
         
     | 
| 
      
 10 
     | 
    
         
            +
            const peerOption = Options.text("peer")
         
     | 
| 
      
 11 
     | 
    
         
            +
                .pipe(Options.withAlias("p"))
         
     | 
| 
      
 12 
     | 
    
         
            +
                .pipe(Options.withDefault("wss://cloud.jazz.tools"));
         
     | 
| 
      
 13 
     | 
    
         
            +
            const createAccountCommand = Command.make("create", { name: nameOption, peer: peerOption }, ({ name, peer }) => {
         
     | 
| 
      
 14 
     | 
    
         
            +
                return Effect.gen(function* () {
         
     | 
| 
      
 15 
     | 
    
         
            +
                    const { accountId, agentSecret } = yield* Effect.promise(() => createWorkerAccount({ name, peer }));
         
     | 
| 
      
 16 
     | 
    
         
            +
                    yield* Console.log(`# Credentials for Jazz account "${name}":
         
     | 
| 
      
 17 
     | 
    
         
            +
            JAZZ_WORKER_ACCOUNT=${accountId}
         
     | 
| 
      
 18 
     | 
    
         
            +
            JAZZ_WORKER_SECRET=${agentSecret}
         
     | 
| 
      
 19 
     | 
    
         
            +
            `);
         
     | 
| 
      
 20 
     | 
    
         
            +
                });
         
     | 
| 
      
 21 
     | 
    
         
            +
            });
         
     | 
| 
      
 22 
     | 
    
         
            +
            const accountCommand = Command.make("account").pipe(Command.withSubcommands([createAccountCommand]));
         
     | 
| 
      
 23 
     | 
    
         
            +
            const portOption = Options.text("port")
         
     | 
| 
      
 24 
     | 
    
         
            +
                .pipe(Options.withAlias("p"))
         
     | 
| 
      
 25 
     | 
    
         
            +
                .pipe(Options.withDescription("Select a different port for the WebSocket server. Default is 4200"))
         
     | 
| 
      
 26 
     | 
    
         
            +
                .pipe(Options.withDefault("4200"));
         
     | 
| 
      
 27 
     | 
    
         
            +
            const inMemoryOption = Options.boolean("in-memory").pipe(Options.withDescription("Use an in-memory storage instead of file-based"));
         
     | 
| 
      
 28 
     | 
    
         
            +
            const dbOption = Options.file("db")
         
     | 
| 
      
 29 
     | 
    
         
            +
                .pipe(Options.withDescription("The path to the file where to store the data. Default is 'sync-db/storage.db'"))
         
     | 
| 
      
 30 
     | 
    
         
            +
                .pipe(Options.withDefault("sync-db/storage.db"));
         
     | 
| 
      
 31 
     | 
    
         
            +
            export const startSyncServerCommand = Command.make("sync", { port: portOption, inMemory: inMemoryOption, db: dbOption }, ({ port, inMemory, db }) => {
         
     | 
| 
      
 32 
     | 
    
         
            +
                return Effect.gen(function* () {
         
     | 
| 
      
 33 
     | 
    
         
            +
                    yield* Effect.promise(() => startSyncServer({ port, inMemory, db }));
         
     | 
| 
      
 34 
     | 
    
         
            +
                    Console.log(`COJSON sync server listening on ws://127.0.0.1:${port}`);
         
     | 
| 
      
 35 
     | 
    
         
            +
                    // Keep the server up
         
     | 
| 
      
 36 
     | 
    
         
            +
                    yield* Effect.never;
         
     | 
| 
      
 37 
     | 
    
         
            +
                });
         
     | 
| 
      
 38 
     | 
    
         
            +
            });
         
     | 
| 
      
 39 
     | 
    
         
            +
            const command = jazzTools.pipe(Command.withSubcommands([accountCommand, startSyncServerCommand]));
         
     | 
| 
       10 
40 
     | 
    
         
             
            const cli = Command.run(command, {
         
     | 
| 
       11 
41 
     | 
    
         
             
                name: "Jazz CLI Tools",
         
     | 
| 
       12 
     | 
    
         
            -
                version: "v0. 
     | 
| 
      
 42 
     | 
    
         
            +
                version: "v0.8.11",
         
     | 
| 
       13 
43 
     | 
    
         
             
            });
         
     | 
| 
       14 
44 
     | 
    
         
             
            Effect.suspend(() => cli(process.argv)).pipe(Effect.provide(NodeContext.layer), NodeRuntime.runMain);
         
     | 
| 
       15 
45 
     | 
    
         
             
            //# sourceMappingURL=index.js.map
         
     | 
    
        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,MAAM,aAAa,CAAC; 
     | 
| 
      
 1 
     | 
    
         
            +
            {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
         
     | 
| 
         @@ -0,0 +1,60 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import { ControlledAgent, LocalNode, WasmCrypto } from "cojson";
         
     | 
| 
      
 2 
     | 
    
         
            +
            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 
     | 
    
         
            +
            import { mkdir } from "node:fs/promises";
         
     | 
| 
      
 8 
     | 
    
         
            +
            export const startSyncServer = async ({ port, inMemory, db, }) => {
         
     | 
| 
      
 9 
     | 
    
         
            +
                const crypto = await WasmCrypto.create();
         
     | 
| 
      
 10 
     | 
    
         
            +
                const server = createServer((req, res) => {
         
     | 
| 
      
 11 
     | 
    
         
            +
                    if (req.url === "/health") {
         
     | 
| 
      
 12 
     | 
    
         
            +
                        res.writeHead(200);
         
     | 
| 
      
 13 
     | 
    
         
            +
                        res.end("ok");
         
     | 
| 
      
 14 
     | 
    
         
            +
                    }
         
     | 
| 
      
 15 
     | 
    
         
            +
                });
         
     | 
| 
      
 16 
     | 
    
         
            +
                const wss = new WebSocketServer({ noServer: true });
         
     | 
| 
      
 17 
     | 
    
         
            +
                const agentSecret = crypto.newRandomAgentSecret();
         
     | 
| 
      
 18 
     | 
    
         
            +
                const agentID = crypto.getAgentID(agentSecret);
         
     | 
| 
      
 19 
     | 
    
         
            +
                const localNode = new LocalNode(new ControlledAgent(agentSecret, crypto), crypto.newRandomSessionID(agentID), crypto);
         
     | 
| 
      
 20 
     | 
    
         
            +
                if (!inMemory) {
         
     | 
| 
      
 21 
     | 
    
         
            +
                    await mkdir(dirname(db), { recursive: true });
         
     | 
| 
      
 22 
     | 
    
         
            +
                    const storage = await SQLiteStorage.asPeer({ filename: db });
         
     | 
| 
      
 23 
     | 
    
         
            +
                    localNode.syncManager.addPeer(storage);
         
     | 
| 
      
 24 
     | 
    
         
            +
                }
         
     | 
| 
      
 25 
     | 
    
         
            +
                wss.on("connection", function connection(ws, req) {
         
     | 
| 
      
 26 
     | 
    
         
            +
                    // ping/pong for the connection liveness
         
     | 
| 
      
 27 
     | 
    
         
            +
                    const pinging = setInterval(() => {
         
     | 
| 
      
 28 
     | 
    
         
            +
                        ws.send(JSON.stringify({
         
     | 
| 
      
 29 
     | 
    
         
            +
                            type: "ping",
         
     | 
| 
      
 30 
     | 
    
         
            +
                            time: Date.now(),
         
     | 
| 
      
 31 
     | 
    
         
            +
                            dc: "unknown",
         
     | 
| 
      
 32 
     | 
    
         
            +
                        }));
         
     | 
| 
      
 33 
     | 
    
         
            +
                    }, 1500);
         
     | 
| 
      
 34 
     | 
    
         
            +
                    ws.on("close", () => {
         
     | 
| 
      
 35 
     | 
    
         
            +
                        clearInterval(pinging);
         
     | 
| 
      
 36 
     | 
    
         
            +
                    });
         
     | 
| 
      
 37 
     | 
    
         
            +
                    const clientAddress = req.headers["x-forwarded-for"]
         
     | 
| 
      
 38 
     | 
    
         
            +
                        ?.split(",")[0]
         
     | 
| 
      
 39 
     | 
    
         
            +
                        ?.trim() || req.socket.remoteAddress;
         
     | 
| 
      
 40 
     | 
    
         
            +
                    const clientId = clientAddress + "@" + new Date().toISOString();
         
     | 
| 
      
 41 
     | 
    
         
            +
                    localNode.syncManager.addPeer(createWebSocketPeer({
         
     | 
| 
      
 42 
     | 
    
         
            +
                        id: clientId,
         
     | 
| 
      
 43 
     | 
    
         
            +
                        role: "client",
         
     | 
| 
      
 44 
     | 
    
         
            +
                        websocket: ws,
         
     | 
| 
      
 45 
     | 
    
         
            +
                        expectPings: false,
         
     | 
| 
      
 46 
     | 
    
         
            +
                        batchingByDefault: false,
         
     | 
| 
      
 47 
     | 
    
         
            +
                    }));
         
     | 
| 
      
 48 
     | 
    
         
            +
                    ws.on("error", (e) => console.error(`Error on connection ${clientId}:`, e));
         
     | 
| 
      
 49 
     | 
    
         
            +
                });
         
     | 
| 
      
 50 
     | 
    
         
            +
                server.on("upgrade", function upgrade(req, socket, head) {
         
     | 
| 
      
 51 
     | 
    
         
            +
                    if (req.url !== "/health") {
         
     | 
| 
      
 52 
     | 
    
         
            +
                        wss.handleUpgrade(req, socket, head, function done(ws) {
         
     | 
| 
      
 53 
     | 
    
         
            +
                            wss.emit("connection", ws, req);
         
     | 
| 
      
 54 
     | 
    
         
            +
                        });
         
     | 
| 
      
 55 
     | 
    
         
            +
                    }
         
     | 
| 
      
 56 
     | 
    
         
            +
                });
         
     | 
| 
      
 57 
     | 
    
         
            +
                server.listen(port ? parseInt(port) : undefined);
         
     | 
| 
      
 58 
     | 
    
         
            +
                return server;
         
     | 
| 
      
 59 
     | 
    
         
            +
            };
         
     | 
| 
      
 60 
     | 
    
         
            +
            //# sourceMappingURL=startSyncServer.js.map
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            {"version":3,"file":"startSyncServer.js","sourceRoot":"","sources":["../src/startSyncServer.ts"],"names":[],"mappings":"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"}
         
     | 
| 
         @@ -0,0 +1,25 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import { describe, onTestFinished, it, expect } from "vitest";
         
     | 
| 
      
 2 
     | 
    
         
            +
            import { startSyncServer } from "../startSyncServer.js";
         
     | 
| 
      
 3 
     | 
    
         
            +
            import { createWorkerAccount } from "../createWorkerAccount.js";
         
     | 
| 
      
 4 
     | 
    
         
            +
            describe("createWorkerAccount - integration tests", () => {
         
     | 
| 
      
 5 
     | 
    
         
            +
                it("should create a worker account using the local sync server", async () => {
         
     | 
| 
      
 6 
     | 
    
         
            +
                    // Pass port: undefined to let the server choose a random port
         
     | 
| 
      
 7 
     | 
    
         
            +
                    const server = await startSyncServer({ port: undefined, inMemory: true, db: '' });
         
     | 
| 
      
 8 
     | 
    
         
            +
                    onTestFinished(() => {
         
     | 
| 
      
 9 
     | 
    
         
            +
                        server.close();
         
     | 
| 
      
 10 
     | 
    
         
            +
                    });
         
     | 
| 
      
 11 
     | 
    
         
            +
                    const address = server.address();
         
     | 
| 
      
 12 
     | 
    
         
            +
                    if (typeof address !== 'object' || address === null) {
         
     | 
| 
      
 13 
     | 
    
         
            +
                        throw new Error('Server address is not an object');
         
     | 
| 
      
 14 
     | 
    
         
            +
                    }
         
     | 
| 
      
 15 
     | 
    
         
            +
                    const { accountId, agentSecret } = await createWorkerAccount({ name: "test", peer: `ws://localhost:${address.port}` });
         
     | 
| 
      
 16 
     | 
    
         
            +
                    expect(accountId).toBeDefined();
         
     | 
| 
      
 17 
     | 
    
         
            +
                    expect(agentSecret).toBeDefined();
         
     | 
| 
      
 18 
     | 
    
         
            +
                });
         
     | 
| 
      
 19 
     | 
    
         
            +
                it("should create a worker account using the Jazz cloud", async () => {
         
     | 
| 
      
 20 
     | 
    
         
            +
                    const { accountId, agentSecret } = await createWorkerAccount({ name: "test", peer: `wss://cloud.jazz.tools` });
         
     | 
| 
      
 21 
     | 
    
         
            +
                    expect(accountId).toBeDefined();
         
     | 
| 
      
 22 
     | 
    
         
            +
                    expect(agentSecret).toBeDefined();
         
     | 
| 
      
 23 
     | 
    
         
            +
                });
         
     | 
| 
      
 24 
     | 
    
         
            +
            });
         
     | 
| 
      
 25 
     | 
    
         
            +
            //# sourceMappingURL=createWorkerAccount.test.js.map
         
     | 
| 
         @@ -0,0 +1 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            {"version":3,"file":"createWorkerAccount.test.js","sourceRoot":"","sources":["../../src/test/createWorkerAccount.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9D,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AAEhE,QAAQ,CAAC,yCAAyC,EAAE,GAAG,EAAE;IACrD,EAAE,CAAC,4DAA4D,EAAE,KAAK,IAAI,EAAE;QACxE,8DAA8D;QAC9D,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,EAAE,EAAE,EAAE,EAAE,CAAC,CAAC;QAElF,cAAc,CAAC,GAAG,EAAE;YAChB,MAAM,CAAC,KAAK,EAAE,CAAA;QAClB,CAAC,CAAC,CAAC;QAEH,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAC;QAEjC,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YAClD,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;QACvD,CAAC;QAED,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,MAAM,mBAAmB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,kBAAkB,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAEvH,MAAM,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;QAChC,MAAM,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;QACjE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,MAAM,mBAAmB,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,wBAAwB,EAAE,CAAC,CAAC;QAE/G,MAAM,CAAC,SAAS,CAAC,CAAC,WAAW,EAAE,CAAC;QAChC,MAAM,CAAC,WAAW,CAAC,CAAC,WAAW,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;AACP,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.13",
         
     | 
| 
       7 
7 
     | 
    
         
             
              "dependencies": {
         
     | 
| 
       8 
8 
     | 
    
         
             
                "@effect/cli": "^0.41.2",
         
     | 
| 
       9 
9 
     | 
    
         
             
                "@effect/platform-node": "^0.57.2",
         
     | 
| 
         @@ -11,11 +11,11 @@ 
     | 
|
| 
       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.12",
         
     | 
| 
      
 15 
     | 
    
         
            +
                "cojson-storage-sqlite": "0.8.12",
         
     | 
| 
      
 16 
     | 
    
         
            +
                "cojson-transport-ws": "0.8.13",
         
     | 
| 
       17 
17 
     | 
    
         
             
                "effect": "^3.6.5",
         
     | 
| 
       18 
     | 
    
         
            -
                "jazz-tools": "0.8. 
     | 
| 
      
 18 
     | 
    
         
            +
                "jazz-tools": "0.8.13",
         
     | 
| 
       19 
19 
     | 
    
         
             
                "ws": "^8.14.2"
         
     | 
| 
       20 
20 
     | 
    
         
             
              },
         
     | 
| 
       21 
21 
     | 
    
         
             
              "devDependencies": {
         
     | 
| 
         @@ -0,0 +1,114 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import { createWebSocketPeer } from "cojson-transport-ws";
         
     | 
| 
      
 2 
     | 
    
         
            +
            import { WebSocket } from "ws";
         
     | 
| 
      
 3 
     | 
    
         
            +
            import {
         
     | 
| 
      
 4 
     | 
    
         
            +
                Account,
         
     | 
| 
      
 5 
     | 
    
         
            +
                Peer,
         
     | 
| 
      
 6 
     | 
    
         
            +
                WasmCrypto,
         
     | 
| 
      
 7 
     | 
    
         
            +
                createJazzContext,
         
     | 
| 
      
 8 
     | 
    
         
            +
                isControlledAccount,
         
     | 
| 
      
 9 
     | 
    
         
            +
            } from "jazz-tools";
         
     | 
| 
      
 10 
     | 
    
         
            +
            import { fixedCredentialsAuth, randomSessionProvider } from "jazz-tools";
         
     | 
| 
      
 11 
     | 
    
         
            +
            import { CoValueCore } from "cojson";
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
            export const createWorkerAccount = async ({
         
     | 
| 
      
 14 
     | 
    
         
            +
                name,
         
     | 
| 
      
 15 
     | 
    
         
            +
                peer: peerAddr,
         
     | 
| 
      
 16 
     | 
    
         
            +
            }: {
         
     | 
| 
      
 17 
     | 
    
         
            +
                name: string;
         
     | 
| 
      
 18 
     | 
    
         
            +
                peer: string;
         
     | 
| 
      
 19 
     | 
    
         
            +
            }) => {
         
     | 
| 
      
 20 
     | 
    
         
            +
                const crypto = await WasmCrypto.create();
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                const peer = createWebSocketPeer({
         
     | 
| 
      
 23 
     | 
    
         
            +
                    id: "upstream",
         
     | 
| 
      
 24 
     | 
    
         
            +
                    websocket: new WebSocket(peerAddr),
         
     | 
| 
      
 25 
     | 
    
         
            +
                    role: "server",
         
     | 
| 
      
 26 
     | 
    
         
            +
                });
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                const account = await Account.create({
         
     | 
| 
      
 29 
     | 
    
         
            +
                    creationProps: { name },
         
     | 
| 
      
 30 
     | 
    
         
            +
                    peersToLoadFrom: [peer],
         
     | 
| 
      
 31 
     | 
    
         
            +
                    crypto,
         
     | 
| 
      
 32 
     | 
    
         
            +
                });
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                if (!isControlledAccount(account)) {
         
     | 
| 
      
 35 
     | 
    
         
            +
                    throw new Error("account is not a controlled account");
         
     | 
| 
      
 36 
     | 
    
         
            +
                }
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                const accountCoValue = account._raw.core;
         
     | 
| 
      
 39 
     | 
    
         
            +
                const accountProfileCoValue = account.profile!._raw.core;
         
     | 
| 
      
 40 
     | 
    
         
            +
                const syncManager = account._raw.core.node.syncManager;
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
                await Promise.all([
         
     | 
| 
      
 43 
     | 
    
         
            +
                    syncManager.syncCoValue(accountCoValue),
         
     | 
| 
      
 44 
     | 
    
         
            +
                    syncManager.syncCoValue(accountProfileCoValue),
         
     | 
| 
      
 45 
     | 
    
         
            +
                ]);
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                await Promise.all([
         
     | 
| 
      
 48 
     | 
    
         
            +
                    waitForSync(account, peer, accountCoValue),
         
     | 
| 
      
 49 
     | 
    
         
            +
                    waitForSync(account, peer, accountProfileCoValue),
         
     | 
| 
      
 50 
     | 
    
         
            +
                ]);
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                // Spawn a second peer to double check that the account is fully synced
         
     | 
| 
      
 53 
     | 
    
         
            +
                const peer2 = createWebSocketPeer({
         
     | 
| 
      
 54 
     | 
    
         
            +
                    id: "upstream2",
         
     | 
| 
      
 55 
     | 
    
         
            +
                    websocket: new WebSocket(peerAddr),
         
     | 
| 
      
 56 
     | 
    
         
            +
                    role: "server",
         
     | 
| 
      
 57 
     | 
    
         
            +
                });
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                await createJazzContext({
         
     | 
| 
      
 60 
     | 
    
         
            +
                    auth: fixedCredentialsAuth({
         
     | 
| 
      
 61 
     | 
    
         
            +
                        accountID: account.id,
         
     | 
| 
      
 62 
     | 
    
         
            +
                        secret: account._raw.agentSecret,
         
     | 
| 
      
 63 
     | 
    
         
            +
                    }),
         
     | 
| 
      
 64 
     | 
    
         
            +
                    sessionProvider: randomSessionProvider,
         
     | 
| 
      
 65 
     | 
    
         
            +
                    peersToLoadFrom: [peer2],
         
     | 
| 
      
 66 
     | 
    
         
            +
                    crypto,
         
     | 
| 
      
 67 
     | 
    
         
            +
                });
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                return {
         
     | 
| 
      
 70 
     | 
    
         
            +
                    accountId: account.id,
         
     | 
| 
      
 71 
     | 
    
         
            +
                    agentSecret: account._raw.agentSecret,
         
     | 
| 
      
 72 
     | 
    
         
            +
                };
         
     | 
| 
      
 73 
     | 
    
         
            +
            };
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
            function waitForSync(account: Account, peer: Peer, coValue: CoValueCore) {
         
     | 
| 
      
 76 
     | 
    
         
            +
                const syncManager = account._raw.core.node.syncManager;
         
     | 
| 
      
 77 
     | 
    
         
            +
                const peerState = syncManager.peers[peer.id];
         
     | 
| 
      
 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 
     | 
    
         
            +
            }
         
     | 
| 
      
 98 
     | 
    
         
            +
             
     | 
| 
      
 99 
     | 
    
         
            +
            function isEqualSession(a: Record<string, number>, b: Record<string, number>) {
         
     | 
| 
      
 100 
     | 
    
         
            +
                const keysA = Object.keys(a);
         
     | 
| 
      
 101 
     | 
    
         
            +
                const keysB = Object.keys(b);
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
                if (keysA.length !== keysB.length) {
         
     | 
| 
      
 104 
     | 
    
         
            +
                    return false;
         
     | 
| 
      
 105 
     | 
    
         
            +
                }
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
                for (const sessionId of keysA) {
         
     | 
| 
      
 108 
     | 
    
         
            +
                    if (a[sessionId] !== b[sessionId]) {
         
     | 
| 
      
 109 
     | 
    
         
            +
                        return false;
         
     | 
| 
      
 110 
     | 
    
         
            +
                    }
         
     | 
| 
      
 111 
     | 
    
         
            +
                }
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                return true;
         
     | 
| 
      
 114 
     | 
    
         
            +
            }
         
     | 
    
        package/src/index.ts
    CHANGED
    
    | 
         @@ -1,18 +1,86 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            #!/usr/bin/env node
         
     | 
| 
       2 
2 
     | 
    
         
             
            /* istanbul ignore file -- @preserve */
         
     | 
| 
       3 
     | 
    
         
            -
            import { Command } from "@effect/cli";
         
     | 
| 
      
 3 
     | 
    
         
            +
            import { Command, Options } from "@effect/cli";
         
     | 
| 
       4 
4 
     | 
    
         
             
            import { NodeContext, NodeRuntime } from "@effect/platform-node";
         
     | 
| 
       5 
     | 
    
         
            -
            import { Effect } from "effect";
         
     | 
| 
       6 
     | 
    
         
            -
            import {  
     | 
| 
       7 
     | 
    
         
            -
            import {  
     | 
| 
      
 5 
     | 
    
         
            +
            import { Console, Effect } from "effect";
         
     | 
| 
      
 6 
     | 
    
         
            +
            import { createWorkerAccount } from "./createWorkerAccount.js";
         
     | 
| 
      
 7 
     | 
    
         
            +
            import { startSyncServer } from "./startSyncServer.js";
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
            const jazzTools = Command.make("jazz-tools");
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
     | 
    
         
            -
            const  
     | 
| 
      
 11 
     | 
    
         
            +
            const nameOption = Options.text("name").pipe(Options.withAlias("n"));
         
     | 
| 
      
 12 
     | 
    
         
            +
            const peerOption = Options.text("peer")
         
     | 
| 
      
 13 
     | 
    
         
            +
                .pipe(Options.withAlias("p"))
         
     | 
| 
      
 14 
     | 
    
         
            +
                .pipe(Options.withDefault("wss://cloud.jazz.tools"));
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            const createAccountCommand = Command.make(
         
     | 
| 
      
 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 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                        yield* Console.log(`# Credentials for Jazz account "${name}":
         
     | 
| 
      
 26 
     | 
    
         
            +
            JAZZ_WORKER_ACCOUNT=${accountId}
         
     | 
| 
      
 27 
     | 
    
         
            +
            JAZZ_WORKER_SECRET=${agentSecret}
         
     | 
| 
      
 28 
     | 
    
         
            +
            `);
         
     | 
| 
      
 29 
     | 
    
         
            +
                    });
         
     | 
| 
      
 30 
     | 
    
         
            +
                },
         
     | 
| 
      
 31 
     | 
    
         
            +
            );
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            const accountCommand = Command.make("account").pipe(
         
     | 
| 
      
 34 
     | 
    
         
            +
                Command.withSubcommands([createAccountCommand]),
         
     | 
| 
      
 35 
     | 
    
         
            +
            );
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
            const portOption = Options.text("port")
         
     | 
| 
      
 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 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
            const inMemoryOption = Options.boolean("in-memory").pipe(
         
     | 
| 
      
 47 
     | 
    
         
            +
                Options.withDescription("Use an in-memory storage instead of file-based"),
         
     | 
| 
      
 48 
     | 
    
         
            +
            );
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
            const dbOption = Options.file("db")
         
     | 
| 
      
 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 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
            export const startSyncServerCommand = Command.make(
         
     | 
| 
      
 59 
     | 
    
         
            +
                "sync",
         
     | 
| 
      
 60 
     | 
    
         
            +
                { port: portOption, inMemory: inMemoryOption, db: dbOption },
         
     | 
| 
      
 61 
     | 
    
         
            +
                ({ port, inMemory, db }) => {
         
     | 
| 
      
 62 
     | 
    
         
            +
                    return Effect.gen(function* () {
         
     | 
| 
      
 63 
     | 
    
         
            +
                        yield* Effect.promise(() =>
         
     | 
| 
      
 64 
     | 
    
         
            +
                            startSyncServer({ port, inMemory, db }),
         
     | 
| 
      
 65 
     | 
    
         
            +
                        );
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                        Console.log(
         
     | 
| 
      
 68 
     | 
    
         
            +
                            `COJSON sync server listening on ws://127.0.0.1:${port}`,
         
     | 
| 
      
 69 
     | 
    
         
            +
                        );
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
                        // Keep the server up
         
     | 
| 
      
 72 
     | 
    
         
            +
                        yield* Effect.never;
         
     | 
| 
      
 73 
     | 
    
         
            +
                    });
         
     | 
| 
      
 74 
     | 
    
         
            +
                },
         
     | 
| 
      
 75 
     | 
    
         
            +
            );
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
            const command = jazzTools.pipe(
         
     | 
| 
      
 78 
     | 
    
         
            +
                Command.withSubcommands([accountCommand, startSyncServerCommand]),
         
     | 
| 
      
 79 
     | 
    
         
            +
            );
         
     | 
| 
       12 
80 
     | 
    
         | 
| 
       13 
81 
     | 
    
         
             
            const cli = Command.run(command, {
         
     | 
| 
       14 
82 
     | 
    
         
             
                name: "Jazz CLI Tools",
         
     | 
| 
       15 
     | 
    
         
            -
                version: "v0. 
     | 
| 
      
 83 
     | 
    
         
            +
                version: "v0.8.11",
         
     | 
| 
       16 
84 
     | 
    
         
             
            });
         
     | 
| 
       17 
85 
     | 
    
         | 
| 
       18 
86 
     | 
    
         
             
            Effect.suspend(() => cli(process.argv)).pipe(
         
     | 
| 
         @@ -0,0 +1,95 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import { ControlledAgent, LocalNode, WasmCrypto } from "cojson";
         
     | 
| 
      
 2 
     | 
    
         
            +
            import { WebSocketServer } from "ws";
         
     | 
| 
      
 3 
     | 
    
         
            +
            import { createServer } from "http";
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            import { createWebSocketPeer } from "cojson-transport-ws";
         
     | 
| 
      
 6 
     | 
    
         
            +
            import { SQLiteStorage } from "cojson-storage-sqlite";
         
     | 
| 
      
 7 
     | 
    
         
            +
            import { dirname } from "node:path";
         
     | 
| 
      
 8 
     | 
    
         
            +
            import { mkdir } from "node:fs/promises";
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            export const startSyncServer = async ({
         
     | 
| 
      
 11 
     | 
    
         
            +
                port,
         
     | 
| 
      
 12 
     | 
    
         
            +
                inMemory,
         
     | 
| 
      
 13 
     | 
    
         
            +
                db,
         
     | 
| 
      
 14 
     | 
    
         
            +
            }: {
         
     | 
| 
      
 15 
     | 
    
         
            +
                port: string | undefined;
         
     | 
| 
      
 16 
     | 
    
         
            +
                inMemory: boolean;
         
     | 
| 
      
 17 
     | 
    
         
            +
                db: string;
         
     | 
| 
      
 18 
     | 
    
         
            +
            }) => {
         
     | 
| 
      
 19 
     | 
    
         
            +
                const crypto = await WasmCrypto.create();
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                const server = createServer((req, res) => {
         
     | 
| 
      
 22 
     | 
    
         
            +
                    if (req.url === "/health") {
         
     | 
| 
      
 23 
     | 
    
         
            +
                        res.writeHead(200);
         
     | 
| 
      
 24 
     | 
    
         
            +
                        res.end("ok");
         
     | 
| 
      
 25 
     | 
    
         
            +
                    }
         
     | 
| 
      
 26 
     | 
    
         
            +
                });
         
     | 
| 
      
 27 
     | 
    
         
            +
                const wss = new WebSocketServer({ noServer: true });
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                const agentSecret = crypto.newRandomAgentSecret();
         
     | 
| 
      
 30 
     | 
    
         
            +
                const agentID = crypto.getAgentID(agentSecret);
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
      
 32 
     | 
    
         
            +
                const localNode = new LocalNode(
         
     | 
| 
      
 33 
     | 
    
         
            +
                    new ControlledAgent(agentSecret, crypto),
         
     | 
| 
      
 34 
     | 
    
         
            +
                    crypto.newRandomSessionID(agentID),
         
     | 
| 
      
 35 
     | 
    
         
            +
                    crypto,
         
     | 
| 
      
 36 
     | 
    
         
            +
                );
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                if (!inMemory) {
         
     | 
| 
      
 39 
     | 
    
         
            +
                    await mkdir(dirname(db), { recursive: true })
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                    const storage = await SQLiteStorage.asPeer({ filename: db });
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    localNode.syncManager.addPeer(storage);
         
     | 
| 
      
 44 
     | 
    
         
            +
                }
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                wss.on("connection", function connection(ws, req) {
         
     | 
| 
      
 47 
     | 
    
         
            +
                    // ping/pong for the connection liveness
         
     | 
| 
      
 48 
     | 
    
         
            +
                    const pinging = setInterval(() => {
         
     | 
| 
      
 49 
     | 
    
         
            +
                        ws.send(
         
     | 
| 
      
 50 
     | 
    
         
            +
                            JSON.stringify({
         
     | 
| 
      
 51 
     | 
    
         
            +
                                type: "ping",
         
     | 
| 
      
 52 
     | 
    
         
            +
                                time: Date.now(),
         
     | 
| 
      
 53 
     | 
    
         
            +
                                dc: "unknown",
         
     | 
| 
      
 54 
     | 
    
         
            +
                            }),
         
     | 
| 
      
 55 
     | 
    
         
            +
                        );
         
     | 
| 
      
 56 
     | 
    
         
            +
                    }, 1500);
         
     | 
| 
      
 57 
     | 
    
         
            +
             
     | 
| 
      
 58 
     | 
    
         
            +
                    ws.on("close", () => {
         
     | 
| 
      
 59 
     | 
    
         
            +
                        clearInterval(pinging);
         
     | 
| 
      
 60 
     | 
    
         
            +
                    });
         
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
      
 62 
     | 
    
         
            +
                    const clientAddress =
         
     | 
| 
      
 63 
     | 
    
         
            +
                        (req.headers["x-forwarded-for"] as string | undefined)
         
     | 
| 
      
 64 
     | 
    
         
            +
                            ?.split(",")[0]
         
     | 
| 
      
 65 
     | 
    
         
            +
                            ?.trim() || req.socket.remoteAddress;
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
                    const clientId = clientAddress + "@" + new Date().toISOString();
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                    localNode.syncManager.addPeer(
         
     | 
| 
      
 70 
     | 
    
         
            +
                        createWebSocketPeer({
         
     | 
| 
      
 71 
     | 
    
         
            +
                            id: clientId,
         
     | 
| 
      
 72 
     | 
    
         
            +
                            role: "client",
         
     | 
| 
      
 73 
     | 
    
         
            +
                            websocket: ws,
         
     | 
| 
      
 74 
     | 
    
         
            +
                            expectPings: false,
         
     | 
| 
      
 75 
     | 
    
         
            +
                            batchingByDefault: false,
         
     | 
| 
      
 76 
     | 
    
         
            +
                        }),
         
     | 
| 
      
 77 
     | 
    
         
            +
                    );
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                    ws.on("error", (e) =>
         
     | 
| 
      
 80 
     | 
    
         
            +
                        console.error(`Error on connection ${clientId}:`, e),
         
     | 
| 
      
 81 
     | 
    
         
            +
                    );
         
     | 
| 
      
 82 
     | 
    
         
            +
                });
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                server.on("upgrade", function upgrade(req, socket, head) {
         
     | 
| 
      
 85 
     | 
    
         
            +
                    if (req.url !== "/health") {
         
     | 
| 
      
 86 
     | 
    
         
            +
                        wss.handleUpgrade(req, socket, head, function done(ws) {
         
     | 
| 
      
 87 
     | 
    
         
            +
                            wss.emit("connection", ws, req);
         
     | 
| 
      
 88 
     | 
    
         
            +
                        });
         
     | 
| 
      
 89 
     | 
    
         
            +
                    }
         
     | 
| 
      
 90 
     | 
    
         
            +
                });
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                server.listen(port ? parseInt(port) : undefined);
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                return server;
         
     | 
| 
      
 95 
     | 
    
         
            +
            };
         
     | 
| 
         @@ -0,0 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            import { describe, onTestFinished, it, expect } from "vitest";
         
     | 
| 
      
 2 
     | 
    
         
            +
            import { startSyncServer } from "../startSyncServer.js";
         
     | 
| 
      
 3 
     | 
    
         
            +
            import { createWorkerAccount } from "../createWorkerAccount.js";
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            describe("createWorkerAccount - integration tests", () => {
         
     | 
| 
      
 6 
     | 
    
         
            +
                it("should create a worker account using the local sync server", async () => {
         
     | 
| 
      
 7 
     | 
    
         
            +
                    // Pass port: undefined to let the server choose a random port
         
     | 
| 
      
 8 
     | 
    
         
            +
                    const server = await startSyncServer({ port: undefined, inMemory: true, db: '' });
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                    onTestFinished(() => {
         
     | 
| 
      
 11 
     | 
    
         
            +
                        server.close()
         
     | 
| 
      
 12 
     | 
    
         
            +
                    });
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                    const address = server.address();
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                    if (typeof address !== 'object' || address === null) {
         
     | 
| 
      
 17 
     | 
    
         
            +
                        throw new Error('Server address is not an object');
         
     | 
| 
      
 18 
     | 
    
         
            +
                    }
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                    const { accountId, agentSecret } = await createWorkerAccount({ name: "test", peer: `ws://localhost:${address.port}` });
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                    expect(accountId).toBeDefined();
         
     | 
| 
      
 23 
     | 
    
         
            +
                    expect(agentSecret).toBeDefined();
         
     | 
| 
      
 24 
     | 
    
         
            +
                });
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                it("should create a worker account using the Jazz cloud", async () => {
         
     | 
| 
      
 27 
     | 
    
         
            +
                    const { accountId, agentSecret } = await createWorkerAccount({ name: "test", peer: `wss://cloud.jazz.tools` });
         
     | 
| 
      
 28 
     | 
    
         
            +
             
     | 
| 
      
 29 
     | 
    
         
            +
                    expect(accountId).toBeDefined();
         
     | 
| 
      
 30 
     | 
    
         
            +
                    expect(agentSecret).toBeDefined();
         
     | 
| 
      
 31 
     | 
    
         
            +
                });
         
     | 
| 
      
 32 
     | 
    
         
            +
            });
         
     | 
    
        package/dist/accountCreate.js
    DELETED
    
    | 
         @@ -1,88 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            import { Command, Options } from "@effect/cli";
         
     | 
| 
       2 
     | 
    
         
            -
            import { Console, Effect } from "effect";
         
     | 
| 
       3 
     | 
    
         
            -
            import { createWebSocketPeer } from "cojson-transport-ws";
         
     | 
| 
       4 
     | 
    
         
            -
            import { WebSocket } from "ws";
         
     | 
| 
       5 
     | 
    
         
            -
            import { Account, WasmCrypto, createJazzContext, isControlledAccount, } from "jazz-tools";
         
     | 
| 
       6 
     | 
    
         
            -
            import { fixedCredentialsAuth, randomSessionProvider } from "jazz-tools";
         
     | 
| 
       7 
     | 
    
         
            -
            const name = Options.text("name").pipe(Options.withAlias("n"));
         
     | 
| 
       8 
     | 
    
         
            -
            const peer = Options.text("peer")
         
     | 
| 
       9 
     | 
    
         
            -
                .pipe(Options.withAlias("p"))
         
     | 
| 
       10 
     | 
    
         
            -
                .pipe(Options.withDefault("wss://cloud.jazz.tools"));
         
     | 
| 
       11 
     | 
    
         
            -
            const accountCreate = Command.make("create", { name, peer }, ({ name, peer: peerAddr }) => {
         
     | 
| 
       12 
     | 
    
         
            -
                return Effect.gen(function* () {
         
     | 
| 
       13 
     | 
    
         
            -
                    const crypto = yield* Effect.promise(() => WasmCrypto.create());
         
     | 
| 
       14 
     | 
    
         
            -
                    const peer = createWebSocketPeer({
         
     | 
| 
       15 
     | 
    
         
            -
                        id: "upstream",
         
     | 
| 
       16 
     | 
    
         
            -
                        websocket: new WebSocket(peerAddr),
         
     | 
| 
       17 
     | 
    
         
            -
                        role: "server",
         
     | 
| 
       18 
     | 
    
         
            -
                    });
         
     | 
| 
       19 
     | 
    
         
            -
                    const account = yield* Effect.promise(async () => Account.create({
         
     | 
| 
       20 
     | 
    
         
            -
                        creationProps: { name },
         
     | 
| 
       21 
     | 
    
         
            -
                        peersToLoadFrom: [peer],
         
     | 
| 
       22 
     | 
    
         
            -
                        crypto,
         
     | 
| 
       23 
     | 
    
         
            -
                    }));
         
     | 
| 
       24 
     | 
    
         
            -
                    if (!isControlledAccount(account)) {
         
     | 
| 
       25 
     | 
    
         
            -
                        throw new Error("account is not a controlled account");
         
     | 
| 
       26 
     | 
    
         
            -
                    }
         
     | 
| 
       27 
     | 
    
         
            -
                    const accountCoValue = account._raw.core;
         
     | 
| 
       28 
     | 
    
         
            -
                    const accountProfileCoValue = account.profile._raw.core;
         
     | 
| 
       29 
     | 
    
         
            -
                    const syncManager = account._raw.core.node.syncManager;
         
     | 
| 
       30 
     | 
    
         
            -
                    yield* Effect.promise(() => syncManager.syncCoValue(accountCoValue));
         
     | 
| 
       31 
     | 
    
         
            -
                    yield* Effect.promise(() => syncManager.syncCoValue(accountProfileCoValue));
         
     | 
| 
       32 
     | 
    
         
            -
                    yield* Effect.promise(() => Promise.all([
         
     | 
| 
       33 
     | 
    
         
            -
                        waitForSync(account, peer, accountCoValue),
         
     | 
| 
       34 
     | 
    
         
            -
                        waitForSync(account, peer, accountProfileCoValue),
         
     | 
| 
       35 
     | 
    
         
            -
                    ]));
         
     | 
| 
       36 
     | 
    
         
            -
                    // Spawn a second peer to double check that the account is fully synced
         
     | 
| 
       37 
     | 
    
         
            -
                    const peer2 = createWebSocketPeer({
         
     | 
| 
       38 
     | 
    
         
            -
                        id: "upstream2",
         
     | 
| 
       39 
     | 
    
         
            -
                        websocket: new WebSocket(peerAddr),
         
     | 
| 
       40 
     | 
    
         
            -
                        role: "server",
         
     | 
| 
       41 
     | 
    
         
            -
                    });
         
     | 
| 
       42 
     | 
    
         
            -
                    yield* Effect.promise(async () => createJazzContext({
         
     | 
| 
       43 
     | 
    
         
            -
                        auth: fixedCredentialsAuth({
         
     | 
| 
       44 
     | 
    
         
            -
                            accountID: account.id,
         
     | 
| 
       45 
     | 
    
         
            -
                            secret: account._raw.agentSecret,
         
     | 
| 
       46 
     | 
    
         
            -
                        }),
         
     | 
| 
       47 
     | 
    
         
            -
                        sessionProvider: randomSessionProvider,
         
     | 
| 
       48 
     | 
    
         
            -
                        peersToLoadFrom: [peer2],
         
     | 
| 
       49 
     | 
    
         
            -
                        crypto,
         
     | 
| 
       50 
     | 
    
         
            -
                    }));
         
     | 
| 
       51 
     | 
    
         
            -
                    yield* Console.log(`# Credentials for Jazz account "${name}":
         
     | 
| 
       52 
     | 
    
         
            -
            JAZZ_WORKER_ACCOUNT=${account.id}
         
     | 
| 
       53 
     | 
    
         
            -
            JAZZ_WORKER_SECRET=${account._raw.agentSecret}
         
     | 
| 
       54 
     | 
    
         
            -
            `);
         
     | 
| 
       55 
     | 
    
         
            -
                });
         
     | 
| 
       56 
     | 
    
         
            -
            });
         
     | 
| 
       57 
     | 
    
         
            -
            const accountBase = Command.make("account");
         
     | 
| 
       58 
     | 
    
         
            -
            export const account = accountBase.pipe(Command.withSubcommands([accountCreate]));
         
     | 
| 
       59 
     | 
    
         
            -
            function waitForSync(account, peer, coValue) {
         
     | 
| 
       60 
     | 
    
         
            -
                const syncManager = account._raw.core.node.syncManager;
         
     | 
| 
       61 
     | 
    
         
            -
                const peerState = syncManager.peers[peer.id];
         
     | 
| 
       62 
     | 
    
         
            -
                return new Promise((resolve) => {
         
     | 
| 
       63 
     | 
    
         
            -
                    const unsubscribe = peerState?.optimisticKnownStates.subscribe((id, peerKnownState) => {
         
     | 
| 
       64 
     | 
    
         
            -
                        if (id !== coValue.id)
         
     | 
| 
       65 
     | 
    
         
            -
                            return;
         
     | 
| 
       66 
     | 
    
         
            -
                        const knownState = coValue.knownState();
         
     | 
| 
       67 
     | 
    
         
            -
                        const synced = isEqualSession(knownState.sessions, peerKnownState.sessions);
         
     | 
| 
       68 
     | 
    
         
            -
                        if (synced) {
         
     | 
| 
       69 
     | 
    
         
            -
                            resolve(true);
         
     | 
| 
       70 
     | 
    
         
            -
                            unsubscribe?.();
         
     | 
| 
       71 
     | 
    
         
            -
                        }
         
     | 
| 
       72 
     | 
    
         
            -
                    });
         
     | 
| 
       73 
     | 
    
         
            -
                });
         
     | 
| 
       74 
     | 
    
         
            -
            }
         
     | 
| 
       75 
     | 
    
         
            -
            function isEqualSession(a, b) {
         
     | 
| 
       76 
     | 
    
         
            -
                const keysA = Object.keys(a);
         
     | 
| 
       77 
     | 
    
         
            -
                const keysB = Object.keys(b);
         
     | 
| 
       78 
     | 
    
         
            -
                if (keysA.length !== keysB.length) {
         
     | 
| 
       79 
     | 
    
         
            -
                    return false;
         
     | 
| 
       80 
     | 
    
         
            -
                }
         
     | 
| 
       81 
     | 
    
         
            -
                for (const sessionId of keysA) {
         
     | 
| 
       82 
     | 
    
         
            -
                    if (a[sessionId] !== b[sessionId]) {
         
     | 
| 
       83 
     | 
    
         
            -
                        return false;
         
     | 
| 
       84 
     | 
    
         
            -
                    }
         
     | 
| 
       85 
     | 
    
         
            -
                }
         
     | 
| 
       86 
     | 
    
         
            -
                return true;
         
     | 
| 
       87 
     | 
    
         
            -
            }
         
     | 
| 
       88 
     | 
    
         
            -
            //# sourceMappingURL=accountCreate.js.map
         
     | 
| 
         @@ -1 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            {"version":3,"file":"accountCreate.js","sourceRoot":"","sources":["../src/accountCreate.ts"],"names":[],"mappings":"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"}
         
     | 
    
        package/dist/startSync.js
    DELETED
    
    | 
         @@ -1,76 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            /* istanbul ignore file -- @preserve */
         
     | 
| 
       2 
     | 
    
         
            -
            import { Command, Options } from "@effect/cli";
         
     | 
| 
       3 
     | 
    
         
            -
            import { ControlledAgent, LocalNode, WasmCrypto } from "cojson";
         
     | 
| 
       4 
     | 
    
         
            -
            import { WebSocketServer } from "ws";
         
     | 
| 
       5 
     | 
    
         
            -
            import { createServer } from "http";
         
     | 
| 
       6 
     | 
    
         
            -
            import { createWebSocketPeer } from "cojson-transport-ws";
         
     | 
| 
       7 
     | 
    
         
            -
            import { Effect } from "effect";
         
     | 
| 
       8 
     | 
    
         
            -
            import { SQLiteStorage } from "cojson-storage-sqlite";
         
     | 
| 
       9 
     | 
    
         
            -
            import { dirname } from "node:path";
         
     | 
| 
       10 
     | 
    
         
            -
            import { mkdir } from "node:fs/promises";
         
     | 
| 
       11 
     | 
    
         
            -
            const port = Options.text("port")
         
     | 
| 
       12 
     | 
    
         
            -
                .pipe(Options.withAlias("p"))
         
     | 
| 
       13 
     | 
    
         
            -
                .pipe(Options.withDescription("Select a different port for the WebSocket server. Default is 4200"))
         
     | 
| 
       14 
     | 
    
         
            -
                .pipe(Options.withDefault("4200"));
         
     | 
| 
       15 
     | 
    
         
            -
            const inMemory = Options.boolean("in-memory").pipe(Options.withDescription("Use an in-memory storage instead of file-based"));
         
     | 
| 
       16 
     | 
    
         
            -
            const db = Options.file("db")
         
     | 
| 
       17 
     | 
    
         
            -
                .pipe(Options.withDescription("The path to the file where to store the data. Default is 'sync-db/storage.db'"))
         
     | 
| 
       18 
     | 
    
         
            -
                .pipe(Options.withDefault("sync-db/storage.db"));
         
     | 
| 
       19 
     | 
    
         
            -
            export const startSync = Command.make("sync", { port, inMemory, db }, ({ port, inMemory, db }) => {
         
     | 
| 
       20 
     | 
    
         
            -
                return Effect.gen(function* () {
         
     | 
| 
       21 
     | 
    
         
            -
                    const crypto = yield* Effect.promise(() => WasmCrypto.create());
         
     | 
| 
       22 
     | 
    
         
            -
                    const server = createServer((req, res) => {
         
     | 
| 
       23 
     | 
    
         
            -
                        if (req.url === "/health") {
         
     | 
| 
       24 
     | 
    
         
            -
                            res.writeHead(200);
         
     | 
| 
       25 
     | 
    
         
            -
                            res.end("ok");
         
     | 
| 
       26 
     | 
    
         
            -
                        }
         
     | 
| 
       27 
     | 
    
         
            -
                    });
         
     | 
| 
       28 
     | 
    
         
            -
                    const wss = new WebSocketServer({ noServer: true });
         
     | 
| 
       29 
     | 
    
         
            -
                    console.log("COJSON sync server listening on port " + port);
         
     | 
| 
       30 
     | 
    
         
            -
                    const agentSecret = crypto.newRandomAgentSecret();
         
     | 
| 
       31 
     | 
    
         
            -
                    const agentID = crypto.getAgentID(agentSecret);
         
     | 
| 
       32 
     | 
    
         
            -
                    const localNode = new LocalNode(new ControlledAgent(agentSecret, crypto), crypto.newRandomSessionID(agentID), crypto);
         
     | 
| 
       33 
     | 
    
         
            -
                    if (!inMemory) {
         
     | 
| 
       34 
     | 
    
         
            -
                        yield* Effect.promise(() => mkdir(dirname(db), { recursive: true }));
         
     | 
| 
       35 
     | 
    
         
            -
                        const storage = yield* Effect.promise(() => SQLiteStorage.asPeer({ filename: db }));
         
     | 
| 
       36 
     | 
    
         
            -
                        localNode.syncManager.addPeer(storage);
         
     | 
| 
       37 
     | 
    
         
            -
                    }
         
     | 
| 
       38 
     | 
    
         
            -
                    wss.on("connection", function connection(ws, req) {
         
     | 
| 
       39 
     | 
    
         
            -
                        // ping/pong for the connection liveness
         
     | 
| 
       40 
     | 
    
         
            -
                        const pinging = setInterval(() => {
         
     | 
| 
       41 
     | 
    
         
            -
                            ws.send(JSON.stringify({
         
     | 
| 
       42 
     | 
    
         
            -
                                type: "ping",
         
     | 
| 
       43 
     | 
    
         
            -
                                time: Date.now(),
         
     | 
| 
       44 
     | 
    
         
            -
                                dc: "unknown",
         
     | 
| 
       45 
     | 
    
         
            -
                            }));
         
     | 
| 
       46 
     | 
    
         
            -
                        }, 1500);
         
     | 
| 
       47 
     | 
    
         
            -
                        ws.on("close", () => {
         
     | 
| 
       48 
     | 
    
         
            -
                            clearInterval(pinging);
         
     | 
| 
       49 
     | 
    
         
            -
                        });
         
     | 
| 
       50 
     | 
    
         
            -
                        const clientAddress = req.headers["x-forwarded-for"]
         
     | 
| 
       51 
     | 
    
         
            -
                            ?.split(",")[0]
         
     | 
| 
       52 
     | 
    
         
            -
                            ?.trim() || req.socket.remoteAddress;
         
     | 
| 
       53 
     | 
    
         
            -
                        const clientId = clientAddress + "@" + new Date().toISOString();
         
     | 
| 
       54 
     | 
    
         
            -
                        localNode.syncManager.addPeer(createWebSocketPeer({
         
     | 
| 
       55 
     | 
    
         
            -
                            id: clientId,
         
     | 
| 
       56 
     | 
    
         
            -
                            role: "client",
         
     | 
| 
       57 
     | 
    
         
            -
                            // eslint-disable-next-line @typescript-eslint/no-explicit-any
         
     | 
| 
       58 
     | 
    
         
            -
                            websocket: ws, // TODO: fix types
         
     | 
| 
       59 
     | 
    
         
            -
                            expectPings: false,
         
     | 
| 
       60 
     | 
    
         
            -
                            batchingByDefault: false
         
     | 
| 
       61 
     | 
    
         
            -
                        }));
         
     | 
| 
       62 
     | 
    
         
            -
                        ws.on("error", (e) => console.error(`Error on connection ${clientId}:`, e));
         
     | 
| 
       63 
     | 
    
         
            -
                    });
         
     | 
| 
       64 
     | 
    
         
            -
                    server.on("upgrade", function upgrade(req, socket, head) {
         
     | 
| 
       65 
     | 
    
         
            -
                        if (req.url !== "/health") {
         
     | 
| 
       66 
     | 
    
         
            -
                            wss.handleUpgrade(req, socket, head, function done(ws) {
         
     | 
| 
       67 
     | 
    
         
            -
                                wss.emit("connection", ws, req);
         
     | 
| 
       68 
     | 
    
         
            -
                            });
         
     | 
| 
       69 
     | 
    
         
            -
                        }
         
     | 
| 
       70 
     | 
    
         
            -
                    });
         
     | 
| 
       71 
     | 
    
         
            -
                    server.listen(parseInt(port));
         
     | 
| 
       72 
     | 
    
         
            -
                    // Keep the server up
         
     | 
| 
       73 
     | 
    
         
            -
                    yield* Effect.never;
         
     | 
| 
       74 
     | 
    
         
            -
                });
         
     | 
| 
       75 
     | 
    
         
            -
            });
         
     | 
| 
       76 
     | 
    
         
            -
            //# sourceMappingURL=startSync.js.map
         
     | 
    
        package/dist/startSync.js.map
    DELETED
    
    | 
         @@ -1 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            {"version":3,"file":"startSync.js","sourceRoot":"","sources":["../src/startSync.ts"],"names":[],"mappings":"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"}
         
     | 
    
        package/src/accountCreate.ts
    DELETED
    
    | 
         @@ -1,125 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            import { Command, Options } from "@effect/cli";
         
     | 
| 
       2 
     | 
    
         
            -
            import { Console, Effect } from "effect";
         
     | 
| 
       3 
     | 
    
         
            -
            import { createWebSocketPeer } from "cojson-transport-ws";
         
     | 
| 
       4 
     | 
    
         
            -
            import { WebSocket } from "ws";
         
     | 
| 
       5 
     | 
    
         
            -
            import {
         
     | 
| 
       6 
     | 
    
         
            -
                Account,
         
     | 
| 
       7 
     | 
    
         
            -
                Peer,
         
     | 
| 
       8 
     | 
    
         
            -
                WasmCrypto,
         
     | 
| 
       9 
     | 
    
         
            -
                createJazzContext,
         
     | 
| 
       10 
     | 
    
         
            -
                isControlledAccount,
         
     | 
| 
       11 
     | 
    
         
            -
            } from "jazz-tools";
         
     | 
| 
       12 
     | 
    
         
            -
            import { fixedCredentialsAuth, randomSessionProvider } from "jazz-tools";
         
     | 
| 
       13 
     | 
    
         
            -
            import { CoValueCore } from "cojson";
         
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
            const name = Options.text("name").pipe(Options.withAlias("n"));
         
     | 
| 
       16 
     | 
    
         
            -
            const peer = Options.text("peer")
         
     | 
| 
       17 
     | 
    
         
            -
                .pipe(Options.withAlias("p"))
         
     | 
| 
       18 
     | 
    
         
            -
                .pipe(Options.withDefault("wss://cloud.jazz.tools"));
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
            const accountCreate = Command.make(
         
     | 
| 
       21 
     | 
    
         
            -
                "create",
         
     | 
| 
       22 
     | 
    
         
            -
                { name, peer },
         
     | 
| 
       23 
     | 
    
         
            -
                ({ name, peer: peerAddr }) => {
         
     | 
| 
       24 
     | 
    
         
            -
                    return Effect.gen(function* () {
         
     | 
| 
       25 
     | 
    
         
            -
                        const crypto = yield* Effect.promise(() => WasmCrypto.create());
         
     | 
| 
       26 
     | 
    
         
            -
             
     | 
| 
       27 
     | 
    
         
            -
                        const peer = createWebSocketPeer({
         
     | 
| 
       28 
     | 
    
         
            -
                            id: "upstream",
         
     | 
| 
       29 
     | 
    
         
            -
                            websocket: new WebSocket(peerAddr),
         
     | 
| 
       30 
     | 
    
         
            -
                            role: "server",
         
     | 
| 
       31 
     | 
    
         
            -
                        });
         
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
                        const account: Account = yield* Effect.promise(async () =>
         
     | 
| 
       34 
     | 
    
         
            -
                            Account.create({
         
     | 
| 
       35 
     | 
    
         
            -
                                creationProps: { name },
         
     | 
| 
       36 
     | 
    
         
            -
                                peersToLoadFrom: [peer],
         
     | 
| 
       37 
     | 
    
         
            -
                                crypto,
         
     | 
| 
       38 
     | 
    
         
            -
                            }),
         
     | 
| 
       39 
     | 
    
         
            -
                        );
         
     | 
| 
       40 
     | 
    
         
            -
                        if (!isControlledAccount(account)) {
         
     | 
| 
       41 
     | 
    
         
            -
                            throw new Error("account is not a controlled account");
         
     | 
| 
       42 
     | 
    
         
            -
                        }
         
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
       44 
     | 
    
         
            -
                        const accountCoValue = account._raw.core;
         
     | 
| 
       45 
     | 
    
         
            -
                        const accountProfileCoValue = account.profile!._raw.core;
         
     | 
| 
       46 
     | 
    
         
            -
                        const syncManager = account._raw.core.node.syncManager;
         
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
                        yield* Effect.promise(() =>
         
     | 
| 
       49 
     | 
    
         
            -
                            syncManager.syncCoValue(accountCoValue),
         
     | 
| 
       50 
     | 
    
         
            -
                        );
         
     | 
| 
       51 
     | 
    
         
            -
                        yield* Effect.promise(() =>
         
     | 
| 
       52 
     | 
    
         
            -
                            syncManager.syncCoValue(accountProfileCoValue),
         
     | 
| 
       53 
     | 
    
         
            -
                        );
         
     | 
| 
       54 
     | 
    
         
            -
             
     | 
| 
       55 
     | 
    
         
            -
                        yield* Effect.promise(() => Promise.all([
         
     | 
| 
       56 
     | 
    
         
            -
                            waitForSync(account, peer, accountCoValue),
         
     | 
| 
       57 
     | 
    
         
            -
                            waitForSync(account, peer, accountProfileCoValue),
         
     | 
| 
       58 
     | 
    
         
            -
                        ]));
         
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
                        // Spawn a second peer to double check that the account is fully synced
         
     | 
| 
       61 
     | 
    
         
            -
                        const peer2 = createWebSocketPeer({
         
     | 
| 
       62 
     | 
    
         
            -
                            id: "upstream2",
         
     | 
| 
       63 
     | 
    
         
            -
                            websocket: new WebSocket(peerAddr),
         
     | 
| 
       64 
     | 
    
         
            -
                            role: "server",
         
     | 
| 
       65 
     | 
    
         
            -
                        });
         
     | 
| 
       66 
     | 
    
         
            -
             
     | 
| 
       67 
     | 
    
         
            -
                        yield* Effect.promise(async () =>
         
     | 
| 
       68 
     | 
    
         
            -
                            createJazzContext({
         
     | 
| 
       69 
     | 
    
         
            -
                                auth: fixedCredentialsAuth({
         
     | 
| 
       70 
     | 
    
         
            -
                                    accountID: account.id,
         
     | 
| 
       71 
     | 
    
         
            -
                                    secret: account._raw.agentSecret,
         
     | 
| 
       72 
     | 
    
         
            -
                                }),
         
     | 
| 
       73 
     | 
    
         
            -
                                sessionProvider: randomSessionProvider,
         
     | 
| 
       74 
     | 
    
         
            -
                                peersToLoadFrom: [peer2],
         
     | 
| 
       75 
     | 
    
         
            -
                                crypto,
         
     | 
| 
       76 
     | 
    
         
            -
                            }),
         
     | 
| 
       77 
     | 
    
         
            -
                        );
         
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
                        yield* Console.log(`# Credentials for Jazz account "${name}":
         
     | 
| 
       80 
     | 
    
         
            -
            JAZZ_WORKER_ACCOUNT=${account.id}
         
     | 
| 
       81 
     | 
    
         
            -
            JAZZ_WORKER_SECRET=${account._raw.agentSecret}
         
     | 
| 
       82 
     | 
    
         
            -
            `);
         
     | 
| 
       83 
     | 
    
         
            -
                    });
         
     | 
| 
       84 
     | 
    
         
            -
                },
         
     | 
| 
       85 
     | 
    
         
            -
            );
         
     | 
| 
       86 
     | 
    
         
            -
             
     | 
| 
       87 
     | 
    
         
            -
            const accountBase = Command.make("account");
         
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
            export const account = accountBase.pipe(Command.withSubcommands([accountCreate]));
         
     | 
| 
       90 
     | 
    
         
            -
             
     | 
| 
       91 
     | 
    
         
            -
            function waitForSync(account: Account, peer: Peer, coValue: CoValueCore) {
         
     | 
| 
       92 
     | 
    
         
            -
                const syncManager = account._raw.core.node.syncManager;
         
     | 
| 
       93 
     | 
    
         
            -
                const peerState = syncManager.peers[peer.id];
         
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
                return new Promise((resolve) => {
         
     | 
| 
       96 
     | 
    
         
            -
                    const unsubscribe = peerState?.optimisticKnownStates.subscribe((id, peerKnownState) => {
         
     | 
| 
       97 
     | 
    
         
            -
                        if (id !== coValue.id) return;
         
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
                        const knownState = coValue.knownState();
         
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
       101 
     | 
    
         
            -
                        const synced = isEqualSession(knownState.sessions, peerKnownState.sessions);
         
     | 
| 
       102 
     | 
    
         
            -
                        if (synced) {
         
     | 
| 
       103 
     | 
    
         
            -
                            resolve(true);
         
     | 
| 
       104 
     | 
    
         
            -
                            unsubscribe?.();
         
     | 
| 
       105 
     | 
    
         
            -
                        }
         
     | 
| 
       106 
     | 
    
         
            -
                    });
         
     | 
| 
       107 
     | 
    
         
            -
                });
         
     | 
| 
       108 
     | 
    
         
            -
            }
         
     | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
       110 
     | 
    
         
            -
            function isEqualSession(a: Record<string, number>, b: Record<string, number>) {
         
     | 
| 
       111 
     | 
    
         
            -
                const keysA = Object.keys(a);
         
     | 
| 
       112 
     | 
    
         
            -
                const keysB = Object.keys(b);
         
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
                if (keysA.length !== keysB.length) {
         
     | 
| 
       115 
     | 
    
         
            -
                    return false;
         
     | 
| 
       116 
     | 
    
         
            -
                }
         
     | 
| 
       117 
     | 
    
         
            -
             
     | 
| 
       118 
     | 
    
         
            -
                for (const sessionId of keysA) {
         
     | 
| 
       119 
     | 
    
         
            -
                    if (a[sessionId] !== b[sessionId]) {
         
     | 
| 
       120 
     | 
    
         
            -
                        return false;
         
     | 
| 
       121 
     | 
    
         
            -
                    }
         
     | 
| 
       122 
     | 
    
         
            -
                }
         
     | 
| 
       123 
     | 
    
         
            -
             
     | 
| 
       124 
     | 
    
         
            -
                return true;
         
     | 
| 
       125 
     | 
    
         
            -
            }
         
     | 
    
        package/src/startSync.ts
    DELETED
    
    | 
         @@ -1,125 +0,0 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            /* istanbul ignore file -- @preserve */
         
     | 
| 
       2 
     | 
    
         
            -
            import { Command, Options } from "@effect/cli";
         
     | 
| 
       3 
     | 
    
         
            -
            import { ControlledAgent, LocalNode, WasmCrypto } from "cojson";
         
     | 
| 
       4 
     | 
    
         
            -
            import { WebSocketServer } from "ws";
         
     | 
| 
       5 
     | 
    
         
            -
            import { createServer } from "http";
         
     | 
| 
       6 
     | 
    
         
            -
             
     | 
| 
       7 
     | 
    
         
            -
            import { createWebSocketPeer } from "cojson-transport-ws";
         
     | 
| 
       8 
     | 
    
         
            -
            import { Effect } from "effect";
         
     | 
| 
       9 
     | 
    
         
            -
            import { SQLiteStorage } from "cojson-storage-sqlite";
         
     | 
| 
       10 
     | 
    
         
            -
            import { dirname } from "node:path";
         
     | 
| 
       11 
     | 
    
         
            -
            import { mkdir } from "node:fs/promises";
         
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
            const port = Options.text("port")
         
     | 
| 
       14 
     | 
    
         
            -
                .pipe(Options.withAlias("p"))
         
     | 
| 
       15 
     | 
    
         
            -
                .pipe(
         
     | 
| 
       16 
     | 
    
         
            -
                    Options.withDescription(
         
     | 
| 
       17 
     | 
    
         
            -
                        "Select a different port for the WebSocket server. Default is 4200",
         
     | 
| 
       18 
     | 
    
         
            -
                    ),
         
     | 
| 
       19 
     | 
    
         
            -
                )
         
     | 
| 
       20 
     | 
    
         
            -
                .pipe(Options.withDefault("4200"));
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
            const inMemory = Options.boolean("in-memory").pipe(
         
     | 
| 
       23 
     | 
    
         
            -
                Options.withDescription("Use an in-memory storage instead of file-based"),
         
     | 
| 
       24 
     | 
    
         
            -
            );
         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
       26 
     | 
    
         
            -
            const db = Options.file("db")
         
     | 
| 
       27 
     | 
    
         
            -
                .pipe(
         
     | 
| 
       28 
     | 
    
         
            -
                    Options.withDescription(
         
     | 
| 
       29 
     | 
    
         
            -
                        "The path to the file where to store the data. Default is 'sync-db/storage.db'",
         
     | 
| 
       30 
     | 
    
         
            -
                    ),
         
     | 
| 
       31 
     | 
    
         
            -
                )
         
     | 
| 
       32 
     | 
    
         
            -
                .pipe(Options.withDefault("sync-db/storage.db"));
         
     | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
       34 
     | 
    
         
            -
            export const startSync = Command.make(
         
     | 
| 
       35 
     | 
    
         
            -
                "sync",
         
     | 
| 
       36 
     | 
    
         
            -
                { port, inMemory, db },
         
     | 
| 
       37 
     | 
    
         
            -
                ({ port, inMemory, db }) => {
         
     | 
| 
       38 
     | 
    
         
            -
                    return Effect.gen(function* () {
         
     | 
| 
       39 
     | 
    
         
            -
                        const crypto = yield* Effect.promise(() => WasmCrypto.create());
         
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
                        const server = createServer((req, res) => {
         
     | 
| 
       42 
     | 
    
         
            -
                            if (req.url === "/health") {
         
     | 
| 
       43 
     | 
    
         
            -
                                res.writeHead(200);
         
     | 
| 
       44 
     | 
    
         
            -
                                res.end("ok");
         
     | 
| 
       45 
     | 
    
         
            -
                            }
         
     | 
| 
       46 
     | 
    
         
            -
                        });
         
     | 
| 
       47 
     | 
    
         
            -
                        const wss = new WebSocketServer({ noServer: true });
         
     | 
| 
       48 
     | 
    
         
            -
             
     | 
| 
       49 
     | 
    
         
            -
                        console.log("COJSON sync server listening on port " + port);
         
     | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
                        const agentSecret = crypto.newRandomAgentSecret();
         
     | 
| 
       52 
     | 
    
         
            -
                        const agentID = crypto.getAgentID(agentSecret);
         
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
                        const localNode = new LocalNode(
         
     | 
| 
       55 
     | 
    
         
            -
                            new ControlledAgent(agentSecret, crypto),
         
     | 
| 
       56 
     | 
    
         
            -
                            crypto.newRandomSessionID(agentID),
         
     | 
| 
       57 
     | 
    
         
            -
                            crypto,
         
     | 
| 
       58 
     | 
    
         
            -
                        );
         
     | 
| 
       59 
     | 
    
         
            -
             
     | 
| 
       60 
     | 
    
         
            -
                        if (!inMemory) {
         
     | 
| 
       61 
     | 
    
         
            -
                            yield* Effect.promise(() =>
         
     | 
| 
       62 
     | 
    
         
            -
                                mkdir(dirname(db), { recursive: true }),
         
     | 
| 
       63 
     | 
    
         
            -
                            );
         
     | 
| 
       64 
     | 
    
         
            -
             
     | 
| 
       65 
     | 
    
         
            -
                            const storage = yield* Effect.promise(() =>
         
     | 
| 
       66 
     | 
    
         
            -
                                SQLiteStorage.asPeer({ filename: db }),
         
     | 
| 
       67 
     | 
    
         
            -
                            );
         
     | 
| 
       68 
     | 
    
         
            -
             
     | 
| 
       69 
     | 
    
         
            -
                            localNode.syncManager.addPeer(storage);
         
     | 
| 
       70 
     | 
    
         
            -
                        }
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
                        wss.on("connection", function connection(ws, req) {
         
     | 
| 
       73 
     | 
    
         
            -
                            // ping/pong for the connection liveness
         
     | 
| 
       74 
     | 
    
         
            -
                            const pinging = setInterval(() => {
         
     | 
| 
       75 
     | 
    
         
            -
                                ws.send(
         
     | 
| 
       76 
     | 
    
         
            -
                                    JSON.stringify({
         
     | 
| 
       77 
     | 
    
         
            -
                                        type: "ping",
         
     | 
| 
       78 
     | 
    
         
            -
                                        time: Date.now(),
         
     | 
| 
       79 
     | 
    
         
            -
                                        dc: "unknown",
         
     | 
| 
       80 
     | 
    
         
            -
                                    }),
         
     | 
| 
       81 
     | 
    
         
            -
                                );
         
     | 
| 
       82 
     | 
    
         
            -
                            }, 1500);
         
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
                            ws.on("close", () => {
         
     | 
| 
       85 
     | 
    
         
            -
                                clearInterval(pinging);
         
     | 
| 
       86 
     | 
    
         
            -
                            });
         
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
                            const clientAddress =
         
     | 
| 
       89 
     | 
    
         
            -
                                (req.headers["x-forwarded-for"] as string | undefined)
         
     | 
| 
       90 
     | 
    
         
            -
                                    ?.split(",")[0]
         
     | 
| 
       91 
     | 
    
         
            -
                                    ?.trim() || req.socket.remoteAddress;
         
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
       93 
     | 
    
         
            -
                            const clientId = clientAddress + "@" + new Date().toISOString();
         
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
                            localNode.syncManager.addPeer(
         
     | 
| 
       96 
     | 
    
         
            -
                                createWebSocketPeer({
         
     | 
| 
       97 
     | 
    
         
            -
                                    id: clientId,
         
     | 
| 
       98 
     | 
    
         
            -
                                    role: "client",
         
     | 
| 
       99 
     | 
    
         
            -
                                    // eslint-disable-next-line @typescript-eslint/no-explicit-any
         
     | 
| 
       100 
     | 
    
         
            -
                                    websocket: ws as any, // TODO: fix types
         
     | 
| 
       101 
     | 
    
         
            -
                                    expectPings: false,
         
     | 
| 
       102 
     | 
    
         
            -
                                    batchingByDefault: false
         
     | 
| 
       103 
     | 
    
         
            -
                                }),
         
     | 
| 
       104 
     | 
    
         
            -
                            );
         
     | 
| 
       105 
     | 
    
         
            -
             
     | 
| 
       106 
     | 
    
         
            -
                            ws.on("error", (e) =>
         
     | 
| 
       107 
     | 
    
         
            -
                                console.error(`Error on connection ${clientId}:`, e),
         
     | 
| 
       108 
     | 
    
         
            -
                            );
         
     | 
| 
       109 
     | 
    
         
            -
                        });
         
     | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
                        server.on("upgrade", function upgrade(req, socket, head) {
         
     | 
| 
       112 
     | 
    
         
            -
                            if (req.url !== "/health") {
         
     | 
| 
       113 
     | 
    
         
            -
                                wss.handleUpgrade(req, socket, head, function done(ws) {
         
     | 
| 
       114 
     | 
    
         
            -
                                    wss.emit("connection", ws, req);
         
     | 
| 
       115 
     | 
    
         
            -
                                });
         
     | 
| 
       116 
     | 
    
         
            -
                            }
         
     | 
| 
       117 
     | 
    
         
            -
                        });
         
     | 
| 
       118 
     | 
    
         
            -
             
     | 
| 
       119 
     | 
    
         
            -
                        server.listen(parseInt(port));
         
     | 
| 
       120 
     | 
    
         
            -
             
     | 
| 
       121 
     | 
    
         
            -
                        // Keep the server up
         
     | 
| 
       122 
     | 
    
         
            -
                        yield* Effect.never;
         
     | 
| 
       123 
     | 
    
         
            -
                    });
         
     | 
| 
       124 
     | 
    
         
            -
                },
         
     | 
| 
       125 
     | 
    
         
            -
            );
         
     |