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
package/package.json CHANGED
@@ -1,97 +1,99 @@
1
1
  {
2
- "name": "pushwork",
3
- "version": "1.1.8",
4
- "description": "Bidirectional directory synchronization using Automerge CRDTs",
5
- "main": "dist/index.js",
6
- "exports": {
7
- ".": "./dist/index.js"
8
- },
9
- "bin": {
10
- "pushwork": "./dist/cli.js"
11
- },
12
- "engines": {
13
- "node": ">=24.0.0",
14
- "pnpm": ">=8.0.0"
15
- },
16
- "keywords": [
17
- "automerge",
18
- "sync",
19
- "crdt",
20
- "collaboration",
21
- "filesystem"
22
- ],
23
- "author": "Peter van Hardenberg",
24
- "license": "MIT",
25
- "dependencies": {
26
- "@automerge/automerge": "^3.2.4",
27
- "@automerge/automerge-repo": "^2.5.3",
28
- "@automerge/automerge-repo-network-websocket": "^2.5.3",
29
- "@automerge/automerge-repo-storage-indexeddb": "^2.5.3",
30
- "@automerge/automerge-repo-storage-nodefs": "^2.5.3",
31
- "@commander-js/extra-typings": "^14.0.0",
32
- "chalk": "^5.3.0",
33
- "commander": "^14.0.2",
34
- "diff": "^8.0.2",
35
- "glob": "^10.3.0",
36
- "ignore": "^5.3.0",
37
- "mime-types": "^2.1.35",
38
- "ora": "^7.0.1"
39
- },
40
- "devDependencies": {
41
- "@babel/core": "^7.28.6",
42
- "@babel/preset-env": "^7.28.6",
43
- "@types/diff": "^5.0.3",
44
- "@types/jest": "^29.5.0",
45
- "@types/mime-types": "^2.1.1",
46
- "@types/node": "^20.0.0",
47
- "@types/tmp": "^0.2.4",
48
- "babel-jest": "^30.2.0",
49
- "fast-check": "^4.3.0",
50
- "jest": "^29.7.0",
51
- "tmp": "^0.2.1",
52
- "ts-jest": "^29.1.0",
53
- "tsx": "^4.19.2",
54
- "typescript": "^5.2.0"
55
- },
56
- "jest": {
57
- "preset": "ts-jest",
58
- "testEnvironment": "node",
59
- "roots": [
60
- "<rootDir>/src",
61
- "<rootDir>/test"
62
- ],
63
- "testMatch": [
64
- "**/__tests__/**/*.ts",
65
- "**/*.(test|spec).ts"
66
- ],
67
- "collectCoverageFrom": [
68
- "src/**/*.ts",
69
- "!src/**/*.d.ts"
70
- ],
71
- "setupFilesAfterEnv": [
72
- "<rootDir>/test/jest.setup.ts"
73
- ],
74
- "maxWorkers": "75%",
75
- "maxConcurrency": 10,
76
- "transformIgnorePatterns": [
77
- "node_modules/(?!.*@automerge)"
78
- ],
79
- "transform": {
80
- "^.+\\.tsx?$": "ts-jest",
81
- "^.+\\.jsx?$": "babel-jest"
82
- }
83
- },
84
- "scripts": {
85
- "build": "tsc",
86
- "dev": "tsc --watch",
87
- "test": "jest",
88
- "test:bail": "jest --bail",
89
- "test:watch": "jest --watch",
90
- "test:coverage": "jest --coverage",
91
- "lint": "eslint src --ext .ts",
92
- "lint:fix": "eslint src --ext .ts --fix",
93
- "clean": "rm -rf dist",
94
- "start": "node dist/cli.js",
95
- "typecheck": "tsc --noEmit"
96
- }
97
- }
2
+ "name": "pushwork",
3
+ "version": "1.2.2",
4
+ "description": "Bidirectional directory synchronization using Automerge CRDTs",
5
+ "main": "dist/index.js",
6
+ "exports": {
7
+ ".": "./dist/index.js"
8
+ },
9
+ "bin": {
10
+ "pushwork": "./dist/cli.js"
11
+ },
12
+ "scripts": {
13
+ "build": "tsc",
14
+ "dev": "tsc --watch",
15
+ "test": "jest",
16
+ "test:bail": "jest --bail",
17
+ "test:watch": "jest --watch",
18
+ "test:coverage": "jest --coverage",
19
+ "lint": "eslint src --ext .ts",
20
+ "lint:fix": "eslint src --ext .ts --fix",
21
+ "clean": "rm -rf dist",
22
+ "prepack": "pnpm run build",
23
+ "start": "node dist/cli.js",
24
+ "typecheck": "tsc --noEmit"
25
+ },
26
+ "packageManager": "pnpm@10.32.1+sha512.a706938f0e89ac1456b6563eab4edf1d1faf3368d1191fc5c59790e96dc918e4456ab2e67d613de1043d2e8c81f87303e6b40d4ffeca9df15ef1ad567348f2be",
27
+ "engines": {
28
+ "node": ">=24.0.0",
29
+ "pnpm": ">=8.0.0"
30
+ },
31
+ "keywords": [
32
+ "automerge",
33
+ "sync",
34
+ "crdt",
35
+ "collaboration",
36
+ "filesystem"
37
+ ],
38
+ "author": "Peter van Hardenberg",
39
+ "license": "MIT",
40
+ "dependencies": {
41
+ "@automerge/automerge": "^3.2.5",
42
+ "@automerge/automerge-repo": "2.6.0-subduction.14",
43
+ "@automerge/automerge-repo-network-websocket": "2.6.0-subduction.14",
44
+ "@automerge/automerge-repo-storage-nodefs": "2.6.0-subduction.14",
45
+ "@automerge/automerge-subduction": "0.7.0",
46
+ "@commander-js/extra-typings": "^14.0.0",
47
+ "chalk": "^5.3.0",
48
+ "commander": "^14.0.2",
49
+ "diff": "^8.0.2",
50
+ "glob": "^10.3.0",
51
+ "ignore": "^5.3.0",
52
+ "mime-types": "^2.1.35",
53
+ "ora": "^7.0.1"
54
+ },
55
+ "devDependencies": {
56
+ "@babel/core": "^7.28.6",
57
+ "@babel/preset-env": "^7.28.6",
58
+ "@types/diff": "^5.0.3",
59
+ "@types/jest": "^29.5.0",
60
+ "@types/mime-types": "^2.1.1",
61
+ "@types/node": "^20.0.0",
62
+ "@types/tmp": "^0.2.4",
63
+ "babel-jest": "^30.2.0",
64
+ "fast-check": "^4.3.0",
65
+ "jest": "^29.7.0",
66
+ "tmp": "^0.2.1",
67
+ "ts-jest": "^29.1.0",
68
+ "tsx": "^4.19.2",
69
+ "typescript": "^5.2.0"
70
+ },
71
+ "jest": {
72
+ "preset": "ts-jest",
73
+ "testEnvironment": "node",
74
+ "roots": [
75
+ "<rootDir>/src",
76
+ "<rootDir>/test"
77
+ ],
78
+ "testMatch": [
79
+ "**/__tests__/**/*.ts",
80
+ "**/*.(test|spec).ts"
81
+ ],
82
+ "collectCoverageFrom": [
83
+ "src/**/*.ts",
84
+ "!src/**/*.d.ts"
85
+ ],
86
+ "setupFilesAfterEnv": [
87
+ "<rootDir>/test/jest.setup.ts"
88
+ ],
89
+ "maxWorkers": "75%",
90
+ "maxConcurrency": 10,
91
+ "transformIgnorePatterns": [
92
+ "node_modules/(?!.*@automerge)"
93
+ ],
94
+ "transform": {
95
+ "^.+\\.tsx?$": "ts-jest",
96
+ "^.+\\.jsx?$": "babel-jest"
97
+ }
98
+ }
99
+ }
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env bash
2
+ # Roundtrip-test pushwork by init-ing a directory, cloning it elsewhere,
3
+ # and diffing the two trees (ignoring files that aren't synced).
4
+ #
5
+ # Usage: roundtrip-test.sh <source-dir>
6
+
7
+ set -euo pipefail
8
+
9
+ if [ $# -lt 1 ]; then
10
+ echo "usage: $0 <source-dir>" >&2
11
+ exit 1
12
+ fi
13
+
14
+ SRC=$(cd "$1" && pwd)
15
+ CLONE_DIR=$(mktemp -d -t pushwork-roundtrip-XXXXXX)/clone
16
+
17
+ echo ">> init $SRC"
18
+ pushwork init --sub "$SRC"
19
+
20
+ URL=$(pushwork url "$SRC")
21
+ echo ">> url $URL"
22
+
23
+ echo ">> clone $CLONE_DIR"
24
+ pushwork clone "$URL" "$CLONE_DIR" --sub
25
+
26
+ echo ">> diff $SRC <-> $CLONE_DIR"
27
+ if diff -r \
28
+ --exclude=.pushwork \
29
+ --exclude=node_modules \
30
+ "$SRC" "$CLONE_DIR"; then
31
+ echo ">> OK: directories match"
32
+ else
33
+ echo ">> FAIL: directories differ" >&2
34
+ exit 1
35
+ fi
package/src/cli.ts CHANGED
@@ -20,11 +20,49 @@ import {
20
20
  watch,
21
21
  } from "./commands";
22
22
 
23
- const version = require("../package.json").version;
23
+ const pkg = require("../package.json");
24
+ const version = pkg.version;
25
+
26
+ // Resolve dependency versions from installed package.json files. These
27
+ // are the actual runtime versions, which is what users care about when
28
+ // reporting bugs (they may differ from package.json ranges after a
29
+ // `pnpm install` that satisfied a range with a newer patch).
30
+ //
31
+ // Some packages (like @automerge/automerge-repo) have an `exports` field
32
+ // that blocks `require("pkg/package.json")`, so we resolve the package
33
+ // entry point with `require.resolve` and walk up to find package.json.
34
+ function depVersion(pkgName: string): string {
35
+ try {
36
+ const fs = require("fs");
37
+ const path = require("path");
38
+ let dir = path.dirname(require.resolve(pkgName));
39
+ while (dir !== path.dirname(dir)) {
40
+ const candidate = path.join(dir, "package.json");
41
+ if (fs.existsSync(candidate)) {
42
+ const data = JSON.parse(fs.readFileSync(candidate, "utf8"));
43
+ if (data.name === pkgName) {
44
+ return data.version;
45
+ }
46
+ }
47
+ dir = path.dirname(dir);
48
+ }
49
+ return "unknown";
50
+ } catch {
51
+ return "unknown";
52
+ }
53
+ }
54
+
55
+ const versionString = [
56
+ `pushwork ${version}`,
57
+ ` @automerge/automerge ${depVersion("@automerge/automerge")}`,
58
+ ` @automerge/automerge-repo ${depVersion("@automerge/automerge-repo")}`,
59
+ ` @automerge/automerge-subduction ${depVersion("@automerge/automerge-subduction")}`,
60
+ ].join("\n");
61
+
24
62
  const program = new Command()
25
63
  .name("pushwork")
26
64
  .description("Bidirectional directory synchronization using Automerge CRDTs")
27
- .version(version, "-V, --version", "output the version number");
65
+ .version(versionString, "-V, --version", "output the version number");
28
66
 
29
67
  // Init command
30
68
  program
@@ -39,16 +77,21 @@ program
39
77
  "--sync-server <url> <storage-id...>",
40
78
  "Custom sync server URL and storage ID"
41
79
  )
80
+ .option("--sub", "Use Subduction sync backend", false)
42
81
  .action(async (path, opts) => {
43
82
  const [syncServer, syncServerStorageId] = validateSyncServer(
44
83
  opts.syncServer
45
84
  );
46
- await init(path, { syncServer, syncServerStorageId });
85
+ await init(path, { syncServer, syncServerStorageId, sub: opts.sub });
47
86
  });
48
87
 
49
88
  // Track command (set root directory URL without full initialization)
50
- const trackAction = async (url: string, path: string, opts: { force: boolean }) => {
51
- await root(url, path, { force: opts.force });
89
+ const trackAction = async (
90
+ url: string,
91
+ path: string,
92
+ opts: { force: boolean; sub: boolean }
93
+ ) => {
94
+ await root(url, path, { force: opts.force, sub: opts.sub });
52
95
  };
53
96
 
54
97
  program
@@ -64,6 +107,7 @@ program
64
107
  "."
65
108
  )
66
109
  .option("-f, --force", "Overwrite existing pushwork setup", false)
110
+ .option("--sub", "Use Subduction sync backend", false)
67
111
  .action(async (url, path, opts) => {
68
112
  await trackAction(url, path, opts);
69
113
  });
@@ -74,7 +118,8 @@ program
74
118
  .argument("<url>")
75
119
  .argument("[path]", "", ".")
76
120
  .option("-f, --force", "", false)
77
- .action(async (url: string, path: string, opts: { force: boolean }) => {
121
+ .option("--sub", "", false)
122
+ .action(async (url: string, path: string, opts: { force: boolean; sub: boolean }) => {
78
123
  await trackAction(url, path, opts);
79
124
  });
80
125
 
@@ -92,6 +137,7 @@ program
92
137
  "--sync-server <url> <storage-id...>",
93
138
  "Custom sync server URL and storage ID"
94
139
  )
140
+ .option("--sub", "Use Subduction sync backend", false)
95
141
  .option("-v, --verbose", "Verbose output", false)
96
142
  .action(async (url, path, opts) => {
97
143
  const [syncServer, syncServerStorageId] = validateSyncServer(
@@ -102,6 +148,7 @@ program
102
148
  verbose: opts.verbose,
103
149
  syncServer,
104
150
  syncServerStorageId,
151
+ sub: opts.sub,
105
152
  });
106
153
  });
107
154