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
package/package.json
CHANGED
|
@@ -1,97 +1,99 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
|
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(
|
|
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 (
|
|
51
|
-
|
|
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
|
-
.
|
|
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
|
|