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.
Files changed (84) hide show
  1. package/ARCHITECTURE-ACCORDING-TO-CLAUDE.md +17 -11
  2. package/CLAUDE.md +46 -1
  3. package/README.md +18 -4
  4. package/dist/cli.js +45 -4
  5. package/dist/cli.js.map +1 -1
  6. package/dist/commands.d.ts +1 -0
  7. package/dist/commands.d.ts.map +1 -1
  8. package/dist/commands.js +151 -38
  9. package/dist/commands.js.map +1 -1
  10. package/dist/core/change-detection.js +2 -2
  11. package/dist/core/change-detection.js.map +1 -1
  12. package/dist/core/config.d.ts.map +1 -1
  13. package/dist/core/config.js +3 -0
  14. package/dist/core/config.js.map +1 -1
  15. package/dist/core/move-detection.d.ts.map +1 -1
  16. package/dist/core/move-detection.js +4 -1
  17. package/dist/core/move-detection.js.map +1 -1
  18. package/dist/core/sync-engine.d.ts +7 -3
  19. package/dist/core/sync-engine.d.ts.map +1 -1
  20. package/dist/core/sync-engine.js +40 -14
  21. package/dist/core/sync-engine.js.map +1 -1
  22. package/dist/types/config.d.ts +4 -0
  23. package/dist/types/config.d.ts.map +1 -1
  24. package/dist/types/config.js +2 -1
  25. package/dist/types/config.js.map +1 -1
  26. package/dist/utils/content.js +1 -1
  27. package/dist/utils/content.js.map +1 -1
  28. package/dist/utils/network-sync.d.ts +1 -2
  29. package/dist/utils/network-sync.d.ts.map +1 -1
  30. package/dist/utils/network-sync.js +76 -7
  31. package/dist/utils/network-sync.js.map +1 -1
  32. package/dist/utils/output.js +7 -7
  33. package/dist/utils/output.js.map +1 -1
  34. package/dist/utils/repo-factory.d.ts +11 -3
  35. package/dist/utils/repo-factory.d.ts.map +1 -1
  36. package/dist/utils/repo-factory.js +112 -8
  37. package/dist/utils/repo-factory.js.map +1 -1
  38. package/flake.lock +128 -0
  39. package/flake.nix +66 -0
  40. package/package.json +98 -96
  41. package/scripts/roundtrip-test.sh +35 -0
  42. package/src/cli.ts +53 -6
  43. package/src/commands.ts +150 -26
  44. package/src/core/change-detection.ts +2 -2
  45. package/src/core/config.ts +4 -0
  46. package/src/core/move-detection.ts +3 -1
  47. package/src/core/sync-engine.ts +40 -15
  48. package/src/types/config.ts +4 -0
  49. package/src/utils/content.ts +1 -1
  50. package/src/utils/network-sync.ts +92 -8
  51. package/src/utils/output.ts +7 -7
  52. package/src/utils/repo-factory.ts +124 -10
  53. package/test/integration/clone-test.sh +0 -0
  54. package/test/integration/conflict-resolution-test.sh +0 -0
  55. package/test/integration/deletion-behavior-test.sh +0 -0
  56. package/test/integration/deletion-sync-test-simple.sh +0 -0
  57. package/test/integration/deletion-sync-test.sh +0 -0
  58. package/test/integration/full-integration-test.sh +0 -0
  59. package/test/integration/manual-sync-test.sh +0 -0
  60. package/test/integration/sub-flag.test.ts +187 -0
  61. package/test/run-tests.sh +0 -0
  62. package/test/unit/network-sync-sub.test.ts +144 -0
  63. package/test/unit/repo-factory.test.ts +111 -0
  64. package/test/unit/subduction-config.test.ts +69 -0
  65. package/dist/cli/commands.d.ts +0 -71
  66. package/dist/cli/commands.d.ts.map +0 -1
  67. package/dist/cli/commands.js +0 -794
  68. package/dist/cli/commands.js.map +0 -1
  69. package/dist/cli/index.d.ts +0 -2
  70. package/dist/cli/index.d.ts.map +0 -1
  71. package/dist/cli/index.js +0 -19
  72. package/dist/cli/index.js.map +0 -1
  73. package/dist/config/index.d.ts +0 -71
  74. package/dist/config/index.d.ts.map +0 -1
  75. package/dist/config/index.js +0 -314
  76. package/dist/config/index.js.map +0 -1
  77. package/dist/utils/content-similarity.d.ts +0 -53
  78. package/dist/utils/content-similarity.d.ts.map +0 -1
  79. package/dist/utils/content-similarity.js +0 -155
  80. package/dist/utils/content-similarity.js.map +0 -1
  81. package/dist/utils/node-polyfills.d.ts +0 -9
  82. package/dist/utils/node-polyfills.d.ts.map +0 -1
  83. package/dist/utils/node-polyfills.js +0 -9
  84. 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
+ });
@@ -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"}