pushwork 1.1.8 → 1.2.2
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/ARCHITECTURE-ACCORDING-TO-CLAUDE.md +17 -11
- package/CLAUDE.md +46 -1
- package/README.md +18 -4
- package/dist/cli.js +45 -4
- package/dist/cli.js.map +1 -1
- package/dist/commands.d.ts +1 -0
- package/dist/commands.d.ts.map +1 -1
- package/dist/commands.js +151 -38
- package/dist/commands.js.map +1 -1
- package/dist/core/change-detection.js +2 -2
- package/dist/core/change-detection.js.map +1 -1
- package/dist/core/config.d.ts.map +1 -1
- package/dist/core/config.js +3 -0
- package/dist/core/config.js.map +1 -1
- package/dist/core/move-detection.d.ts.map +1 -1
- package/dist/core/move-detection.js +4 -1
- package/dist/core/move-detection.js.map +1 -1
- package/dist/core/sync-engine.d.ts +7 -3
- package/dist/core/sync-engine.d.ts.map +1 -1
- package/dist/core/sync-engine.js +40 -14
- package/dist/core/sync-engine.js.map +1 -1
- package/dist/types/config.d.ts +4 -0
- package/dist/types/config.d.ts.map +1 -1
- package/dist/types/config.js +2 -1
- package/dist/types/config.js.map +1 -1
- package/dist/utils/content.js +1 -1
- package/dist/utils/content.js.map +1 -1
- package/dist/utils/network-sync.d.ts +1 -2
- package/dist/utils/network-sync.d.ts.map +1 -1
- package/dist/utils/network-sync.js +76 -7
- package/dist/utils/network-sync.js.map +1 -1
- package/dist/utils/output.js +7 -7
- package/dist/utils/output.js.map +1 -1
- package/dist/utils/repo-factory.d.ts +11 -3
- package/dist/utils/repo-factory.d.ts.map +1 -1
- package/dist/utils/repo-factory.js +112 -8
- package/dist/utils/repo-factory.js.map +1 -1
- package/flake.lock +128 -0
- package/flake.nix +66 -0
- package/package.json +98 -96
- package/scripts/roundtrip-test.sh +35 -0
- package/src/cli.ts +53 -6
- package/src/commands.ts +150 -26
- package/src/core/change-detection.ts +2 -2
- package/src/core/config.ts +4 -0
- package/src/core/move-detection.ts +3 -1
- package/src/core/sync-engine.ts +40 -15
- package/src/types/config.ts +4 -0
- package/src/utils/content.ts +1 -1
- package/src/utils/network-sync.ts +92 -8
- package/src/utils/output.ts +7 -7
- package/src/utils/repo-factory.ts +124 -10
- package/test/integration/clone-test.sh +0 -0
- package/test/integration/conflict-resolution-test.sh +0 -0
- package/test/integration/deletion-behavior-test.sh +0 -0
- package/test/integration/deletion-sync-test-simple.sh +0 -0
- package/test/integration/deletion-sync-test.sh +0 -0
- package/test/integration/full-integration-test.sh +0 -0
- package/test/integration/manual-sync-test.sh +0 -0
- package/test/integration/sub-flag.test.ts +187 -0
- package/test/run-tests.sh +0 -0
- package/test/unit/network-sync-sub.test.ts +144 -0
- package/test/unit/repo-factory.test.ts +111 -0
- package/test/unit/subduction-config.test.ts +69 -0
- package/dist/cli/commands.d.ts +0 -71
- package/dist/cli/commands.d.ts.map +0 -1
- package/dist/cli/commands.js +0 -794
- package/dist/cli/commands.js.map +0 -1
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.d.ts.map +0 -1
- package/dist/cli/index.js +0 -19
- package/dist/cli/index.js.map +0 -1
- package/dist/config/index.d.ts +0 -71
- package/dist/config/index.d.ts.map +0 -1
- package/dist/config/index.js +0 -314
- package/dist/config/index.js.map +0 -1
- package/dist/utils/content-similarity.d.ts +0 -53
- package/dist/utils/content-similarity.d.ts.map +0 -1
- package/dist/utils/content-similarity.js +0 -155
- package/dist/utils/content-similarity.js.map +0 -1
- package/dist/utils/node-polyfills.d.ts +0 -9
- package/dist/utils/node-polyfills.d.ts.map +0 -1
- package/dist/utils/node-polyfills.js +0 -9
- package/dist/utils/node-polyfills.js.map +0 -1
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for repo-factory.ts Subduction configuration.
|
|
3
|
+
*
|
|
4
|
+
* The actual Repo construction requires Wasm initialization via real ESM
|
|
5
|
+
* dynamic imports. We test by invoking the CLI as a subprocess (which runs
|
|
6
|
+
* in a real Node.js context) and inspecting the results.
|
|
7
|
+
*
|
|
8
|
+
* Non-sub (WebSocket) init is tested elsewhere (init-sync.test.ts).
|
|
9
|
+
* These tests focus on the --sub path.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import * as path from "path";
|
|
13
|
+
import * as fs from "fs/promises";
|
|
14
|
+
import * as tmp from "tmp";
|
|
15
|
+
import { execSync } from "child_process";
|
|
16
|
+
|
|
17
|
+
describe("createRepo with --sub", () => {
|
|
18
|
+
let tmpDir: string;
|
|
19
|
+
let cleanup: () => void;
|
|
20
|
+
const cliPath = path.join(__dirname, "../../dist/cli.js");
|
|
21
|
+
|
|
22
|
+
beforeAll(() => {
|
|
23
|
+
execSync("pnpm build", { cwd: path.join(__dirname, "../.."), stdio: "pipe" });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
beforeEach(async () => {
|
|
27
|
+
const tmpObj = tmp.dirSync({ unsafeCleanup: true });
|
|
28
|
+
tmpDir = tmpObj.name;
|
|
29
|
+
cleanup = tmpObj.removeCallback;
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
afterEach(() => {
|
|
33
|
+
cleanup();
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("should create a working repo with --sub flag", async () => {
|
|
37
|
+
await fs.writeFile(path.join(tmpDir, "test.txt"), "hello");
|
|
38
|
+
|
|
39
|
+
execSync(`node "${cliPath}" init --sub "${tmpDir}"`, {
|
|
40
|
+
stdio: "pipe",
|
|
41
|
+
timeout: 30000,
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const snapshotPath = path.join(tmpDir, ".pushwork", "snapshot.json");
|
|
45
|
+
const stat = await fs.stat(snapshotPath);
|
|
46
|
+
expect(stat.isFile()).toBe(true);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
it("should produce a valid automerge URL", async () => {
|
|
50
|
+
await fs.writeFile(path.join(tmpDir, "test.txt"), "hello");
|
|
51
|
+
|
|
52
|
+
execSync(`node "${cliPath}" init --sub "${tmpDir}"`, {
|
|
53
|
+
stdio: "pipe",
|
|
54
|
+
timeout: 30000,
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
const url = execSync(`node "${cliPath}" url "${tmpDir}"`, {
|
|
58
|
+
encoding: "utf8",
|
|
59
|
+
timeout: 10000,
|
|
60
|
+
}).trim();
|
|
61
|
+
|
|
62
|
+
expect(url).toMatch(/^automerge:/);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
it("should track files in the snapshot", async () => {
|
|
66
|
+
await fs.writeFile(path.join(tmpDir, "a.txt"), "aaa");
|
|
67
|
+
await fs.mkdir(path.join(tmpDir, "sub"), { recursive: true });
|
|
68
|
+
await fs.writeFile(path.join(tmpDir, "sub", "b.txt"), "bbb");
|
|
69
|
+
|
|
70
|
+
execSync(`node "${cliPath}" init --sub "${tmpDir}"`, {
|
|
71
|
+
stdio: "pipe",
|
|
72
|
+
timeout: 30000,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const ls = execSync(`node "${cliPath}" ls "${tmpDir}"`, {
|
|
76
|
+
encoding: "utf8",
|
|
77
|
+
timeout: 10000,
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
expect(ls).toContain("a.txt");
|
|
81
|
+
expect(ls).toContain("b.txt");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("should be able to sync after init", async () => {
|
|
85
|
+
await fs.writeFile(path.join(tmpDir, "initial.txt"), "first");
|
|
86
|
+
|
|
87
|
+
execSync(`node "${cliPath}" init --sub "${tmpDir}"`, {
|
|
88
|
+
stdio: "pipe",
|
|
89
|
+
timeout: 30000,
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Add a new file
|
|
93
|
+
await fs.writeFile(path.join(tmpDir, "added.txt"), "second");
|
|
94
|
+
|
|
95
|
+
// Sync should not throw. The `sync` command has no --sub flag — it
|
|
96
|
+
// reads the backend choice from .pushwork/config.json (persisted by
|
|
97
|
+
// the init --sub above).
|
|
98
|
+
execSync(`node "${cliPath}" sync "${tmpDir}"`, {
|
|
99
|
+
stdio: "pipe",
|
|
100
|
+
timeout: 30000,
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
const ls = execSync(`node "${cliPath}" ls "${tmpDir}"`, {
|
|
104
|
+
encoding: "utf8",
|
|
105
|
+
timeout: 10000,
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
expect(ls).toContain("initial.txt");
|
|
109
|
+
expect(ls).toContain("added.txt");
|
|
110
|
+
});
|
|
111
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import * as path from "path";
|
|
2
|
+
import * as fs from "fs/promises";
|
|
3
|
+
import * as tmp from "tmp";
|
|
4
|
+
import { ConfigManager } from "../../src/core/config";
|
|
5
|
+
import { DEFAULT_SUBDUCTION_SERVER, DEFAULT_SYNC_SERVER } from "../../src/types/config";
|
|
6
|
+
|
|
7
|
+
describe("Subduction configuration", () => {
|
|
8
|
+
let tmpDir: string;
|
|
9
|
+
let cleanup: () => void;
|
|
10
|
+
|
|
11
|
+
beforeEach(async () => {
|
|
12
|
+
const tmpObj = tmp.dirSync({ unsafeCleanup: true });
|
|
13
|
+
tmpDir = tmpObj.name;
|
|
14
|
+
cleanup = tmpObj.removeCallback;
|
|
15
|
+
|
|
16
|
+
// Set up .pushwork directory structure
|
|
17
|
+
await fs.mkdir(path.join(tmpDir, ".pushwork", "automerge"), { recursive: true });
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
afterEach(() => {
|
|
21
|
+
cleanup();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("DEFAULT_SUBDUCTION_SERVER", () => {
|
|
25
|
+
it("should be the subduction sync endpoint", () => {
|
|
26
|
+
expect(DEFAULT_SUBDUCTION_SERVER).toBe("wss://subduction.sync.inkandswitch.com");
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("should differ from the default WebSocket sync server", () => {
|
|
30
|
+
expect(DEFAULT_SUBDUCTION_SERVER).not.toBe(DEFAULT_SYNC_SERVER);
|
|
31
|
+
});
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
describe("ConfigManager defaults", () => {
|
|
35
|
+
it("should use the WebSocket server as default sync_server", async () => {
|
|
36
|
+
const configManager = new ConfigManager(tmpDir);
|
|
37
|
+
const config = configManager.getDefaultDirectoryConfig();
|
|
38
|
+
expect(config.sync_server).toBe(DEFAULT_SYNC_SERVER);
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
it("should not default to the subduction server", async () => {
|
|
42
|
+
const configManager = new ConfigManager(tmpDir);
|
|
43
|
+
const config = configManager.getDefaultDirectoryConfig();
|
|
44
|
+
expect(config.sync_server).not.toBe(DEFAULT_SUBDUCTION_SERVER);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe("sub flag option types", () => {
|
|
49
|
+
// These tests verify that the option interfaces accept `sub` on the
|
|
50
|
+
// commands that actually have a --sub flag (init and clone). The flag
|
|
51
|
+
// is NOT on SyncOptions or WatchOptions because sync/watch read the
|
|
52
|
+
// backend choice from persisted config (see `setupCommandContext`).
|
|
53
|
+
// If the type definitions are wrong, these will fail at compile time.
|
|
54
|
+
it("should accept sub on InitOptions", () => {
|
|
55
|
+
const opts: import("../../src/types/config").InitOptions = { sub: true };
|
|
56
|
+
expect(opts.sub).toBe(true);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it("should accept sub on CloneOptions", () => {
|
|
60
|
+
const opts: import("../../src/types/config").CloneOptions = { sub: true };
|
|
61
|
+
expect(opts.sub).toBe(true);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it("should default sub to undefined (not required) on InitOptions", () => {
|
|
65
|
+
const opts: import("../../src/types/config").InitOptions = {};
|
|
66
|
+
expect(opts.sub).toBeUndefined();
|
|
67
|
+
});
|
|
68
|
+
});
|
|
69
|
+
});
|
package/dist/cli/commands.d.ts
DELETED
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import { Repo } from "@automerge/automerge-repo";
|
|
2
|
-
import { CloneOptions, SyncOptions, DiffOptions, LogOptions, CheckoutOptions, DirectoryConfig } from "../types";
|
|
3
|
-
import { SyncEngine } from "../core";
|
|
4
|
-
/**
|
|
5
|
-
* Shared context that commands can use
|
|
6
|
-
*/
|
|
7
|
-
export interface CommandContext {
|
|
8
|
-
repo: Repo;
|
|
9
|
-
syncEngine: SyncEngine;
|
|
10
|
-
config: DirectoryConfig;
|
|
11
|
-
workingDir: string;
|
|
12
|
-
}
|
|
13
|
-
/**
|
|
14
|
-
* Shared pre-action that ensures repository and sync engine are properly initialized
|
|
15
|
-
* This function always works, with or without network connectivity
|
|
16
|
-
*/
|
|
17
|
-
export declare function setupCommandContext(workingDir?: string, customSyncServer?: string, customStorageId?: string, enableNetwork?: boolean): Promise<CommandContext>;
|
|
18
|
-
/**
|
|
19
|
-
* Safely shutdown a repository with proper error handling
|
|
20
|
-
*/
|
|
21
|
-
export declare function safeRepoShutdown(repo: Repo, context?: string): Promise<void>;
|
|
22
|
-
/**
|
|
23
|
-
* Common progress message helpers
|
|
24
|
-
*/
|
|
25
|
-
export declare const ProgressMessages: {
|
|
26
|
-
directoryFound: () => void;
|
|
27
|
-
configLoaded: () => void;
|
|
28
|
-
repoConnected: () => void;
|
|
29
|
-
syncServer: (server: string) => void;
|
|
30
|
-
storageId: (id: string) => void;
|
|
31
|
-
rootUrl: (url: string) => void;
|
|
32
|
-
changesWritten: () => void;
|
|
33
|
-
syncCompleted: (duration: number) => void;
|
|
34
|
-
directoryStructureCreated: () => void;
|
|
35
|
-
configSaved: () => void;
|
|
36
|
-
repoCreated: () => void;
|
|
37
|
-
};
|
|
38
|
-
/**
|
|
39
|
-
* Initialize sync in a directory
|
|
40
|
-
*/
|
|
41
|
-
export declare function init(targetPath: string, syncServer?: string, syncServerStorageId?: string): Promise<void>;
|
|
42
|
-
/**
|
|
43
|
-
* Run bidirectional sync
|
|
44
|
-
*/
|
|
45
|
-
export declare function sync(options: SyncOptions): Promise<void>;
|
|
46
|
-
/**
|
|
47
|
-
* Show differences between local and remote
|
|
48
|
-
*/
|
|
49
|
-
export declare function diff(targetPath: string | undefined, options: DiffOptions): Promise<void>;
|
|
50
|
-
/**
|
|
51
|
-
* Show sync status
|
|
52
|
-
*/
|
|
53
|
-
export declare function status(): Promise<void>;
|
|
54
|
-
/**
|
|
55
|
-
* Show sync history
|
|
56
|
-
*/
|
|
57
|
-
export declare function log(targetPath: string | undefined, options: LogOptions): Promise<void>;
|
|
58
|
-
/**
|
|
59
|
-
* Checkout/restore from previous sync
|
|
60
|
-
*/
|
|
61
|
-
export declare function checkout(syncId: string, targetPath: string | undefined, options: CheckoutOptions): Promise<void>;
|
|
62
|
-
/**
|
|
63
|
-
* Clone an existing synced directory from an AutomergeUrl
|
|
64
|
-
*/
|
|
65
|
-
export declare function clone(rootUrl: string, targetPath: string, options: CloneOptions): Promise<void>;
|
|
66
|
-
/**
|
|
67
|
-
* Get the root URL for the current pushwork repository
|
|
68
|
-
*/
|
|
69
|
-
export declare function url(targetPath?: string): Promise<void>;
|
|
70
|
-
export declare function commit(targetPath: string, dryRun?: boolean): Promise<void>;
|
|
71
|
-
//# sourceMappingURL=commands.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"commands.d.ts","sourceRoot":"","sources":["../../src/cli/commands.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,IAAI,EAA2B,MAAM,2BAA2B,CAAC;AAI1E,OAAO,EAEL,YAAY,EACZ,WAAW,EACX,WAAW,EACX,UAAU,EACV,eAAe,EACf,eAAe,EAEhB,MAAM,UAAU,CAAC;AAClB,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAMrC;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,IAAI,CAAC;IACX,UAAU,EAAE,UAAU,CAAC;IACvB,MAAM,EAAE,eAAe,CAAC;IACxB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;GAGG;AACH,wBAAsB,mBAAmB,CACvC,UAAU,GAAE,MAAsB,EAClC,gBAAgB,CAAC,EAAE,MAAM,EACzB,eAAe,CAAC,EAAE,MAAM,EACxB,aAAa,GAAE,OAAc,GAC5B,OAAO,CAAC,cAAc,CAAC,CAqCzB;AAED;;GAEG;AACH,wBAAsB,gBAAgB,CACpC,IAAI,EAAE,IAAI,EACV,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB;;;;yBAON,MAAM;oBAEX,MAAM;mBACP,MAAM;;8BAKK,MAAM;;;;CAOjC,CAAC;AA+DF;;GAEG;AACH,wBAAsB,IAAI,CACxB,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,EACnB,mBAAmB,CAAC,EAAE,MAAM,GAC3B,OAAO,CAAC,IAAI,CAAC,CA6Hf;AAED;;GAEG;AACH,wBAAsB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAgM9D;AAED;;GAEG;AACH,wBAAsB,IAAI,CACxB,UAAU,oBAAM,EAChB,OAAO,EAAE,WAAW,GACnB,OAAO,CAAC,IAAI,CAAC,CA6Df;AAED;;GAEG;AACH,wBAAsB,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CA6H5C;AAED;;GAEG;AACH,wBAAsB,GAAG,CACvB,UAAU,oBAAM,EAChB,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,IAAI,CAAC,CA2Cf;AAED;;GAEG;AACH,wBAAsB,QAAQ,CAC5B,MAAM,EAAE,MAAM,EACd,UAAU,oBAAM,EAChB,OAAO,EAAE,eAAe,GACvB,OAAO,CAAC,IAAI,CAAC,CAkBf;AAED;;GAEG;AACH,wBAAsB,KAAK,CACzB,OAAO,EAAE,MAAM,EACf,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,YAAY,GACpB,OAAO,CAAC,IAAI,CAAC,CA6Hf;AAED;;GAEG;AACH,wBAAsB,GAAG,CAAC,UAAU,SAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAuCzD;AAED,wBAAsB,MAAM,CAC1B,UAAU,EAAE,MAAM,EAClB,MAAM,GAAE,OAAe,GACtB,OAAO,CAAC,IAAI,CAAC,CAgEf"}
|