pushwork 2.0.0-preview → 2.0.0-preview.3

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 (169) hide show
  1. package/dist/branches.d.ts +1 -0
  2. package/dist/branches.d.ts.map +1 -1
  3. package/dist/cli/commands.d.ts +71 -0
  4. package/dist/cli/commands.d.ts.map +1 -0
  5. package/dist/cli/commands.js +794 -0
  6. package/dist/cli/commands.js.map +1 -0
  7. package/dist/cli/index.d.ts +2 -0
  8. package/dist/cli/index.d.ts.map +1 -0
  9. package/dist/cli/index.js +19 -0
  10. package/dist/cli/index.js.map +1 -0
  11. package/dist/cli.js +67 -112
  12. package/dist/cli.js.map +1 -1
  13. package/dist/commands.d.ts +58 -0
  14. package/dist/commands.d.ts.map +1 -0
  15. package/dist/commands.js +975 -0
  16. package/dist/commands.js.map +1 -0
  17. package/dist/config/index.d.ts +71 -0
  18. package/dist/config/index.d.ts.map +1 -0
  19. package/dist/config/index.js +314 -0
  20. package/dist/config/index.js.map +1 -0
  21. package/dist/config.d.ts +1 -2
  22. package/dist/config.d.ts.map +1 -1
  23. package/dist/config.js +1 -2
  24. package/dist/config.js.map +1 -1
  25. package/dist/core/change-detection.d.ts +80 -0
  26. package/dist/core/change-detection.d.ts.map +1 -0
  27. package/dist/core/change-detection.js +560 -0
  28. package/dist/core/change-detection.js.map +1 -0
  29. package/dist/core/config.d.ts +81 -0
  30. package/dist/core/config.d.ts.map +1 -0
  31. package/dist/core/config.js +304 -0
  32. package/dist/core/config.js.map +1 -0
  33. package/dist/core/index.d.ts +6 -0
  34. package/dist/core/index.d.ts.map +1 -0
  35. package/dist/core/index.js +22 -0
  36. package/dist/core/index.js.map +1 -0
  37. package/dist/core/move-detection.d.ts +34 -0
  38. package/dist/core/move-detection.d.ts.map +1 -0
  39. package/dist/core/move-detection.js +128 -0
  40. package/dist/core/move-detection.js.map +1 -0
  41. package/dist/core/snapshot.d.ts +105 -0
  42. package/dist/core/snapshot.d.ts.map +1 -0
  43. package/dist/core/snapshot.js +254 -0
  44. package/dist/core/snapshot.js.map +1 -0
  45. package/dist/core/sync-engine.d.ts +177 -0
  46. package/dist/core/sync-engine.d.ts.map +1 -0
  47. package/dist/core/sync-engine.js +1471 -0
  48. package/dist/core/sync-engine.js.map +1 -0
  49. package/dist/index.d.ts +2 -4
  50. package/dist/index.d.ts.map +1 -1
  51. package/dist/index.js +4 -14
  52. package/dist/index.js.map +1 -1
  53. package/dist/pushwork.d.ts +28 -61
  54. package/dist/pushwork.d.ts.map +1 -1
  55. package/dist/pushwork.js +127 -445
  56. package/dist/pushwork.js.map +1 -1
  57. package/dist/shapes/types.d.ts +1 -0
  58. package/dist/shapes/types.d.ts.map +1 -1
  59. package/dist/shapes/types.js.map +1 -1
  60. package/dist/shapes/vfs.d.ts.map +1 -1
  61. package/dist/shapes/vfs.js +6 -2
  62. package/dist/shapes/vfs.js.map +1 -1
  63. package/dist/snarf.d.ts +21 -0
  64. package/dist/snarf.d.ts.map +1 -0
  65. package/dist/snarf.js +117 -0
  66. package/dist/snarf.js.map +1 -0
  67. package/dist/stash.d.ts +0 -2
  68. package/dist/stash.d.ts.map +1 -1
  69. package/dist/stash.js +0 -1
  70. package/dist/stash.js.map +1 -1
  71. package/dist/types/config.d.ts +102 -0
  72. package/dist/types/config.d.ts.map +1 -0
  73. package/dist/types/config.js +10 -0
  74. package/dist/types/config.js.map +1 -0
  75. package/dist/types/documents.d.ts +88 -0
  76. package/dist/types/documents.d.ts.map +1 -0
  77. package/dist/types/documents.js +23 -0
  78. package/dist/types/documents.js.map +1 -0
  79. package/dist/types/index.d.ts +4 -0
  80. package/dist/types/index.d.ts.map +1 -0
  81. package/dist/types/index.js +20 -0
  82. package/dist/types/index.js.map +1 -0
  83. package/dist/types/snapshot.d.ts +64 -0
  84. package/dist/types/snapshot.d.ts.map +1 -0
  85. package/dist/types/snapshot.js +3 -0
  86. package/dist/types/snapshot.js.map +1 -0
  87. package/dist/utils/content-similarity.d.ts +53 -0
  88. package/dist/utils/content-similarity.d.ts.map +1 -0
  89. package/dist/utils/content-similarity.js +155 -0
  90. package/dist/utils/content-similarity.js.map +1 -0
  91. package/dist/utils/content.d.ts +10 -0
  92. package/dist/utils/content.d.ts.map +1 -0
  93. package/dist/utils/content.js +35 -0
  94. package/dist/utils/content.js.map +1 -0
  95. package/dist/utils/directory.d.ts +24 -0
  96. package/dist/utils/directory.d.ts.map +1 -0
  97. package/dist/utils/directory.js +56 -0
  98. package/dist/utils/directory.js.map +1 -0
  99. package/dist/utils/fs.d.ts +74 -0
  100. package/dist/utils/fs.d.ts.map +1 -0
  101. package/dist/utils/fs.js +298 -0
  102. package/dist/utils/fs.js.map +1 -0
  103. package/dist/utils/index.d.ts +5 -0
  104. package/dist/utils/index.d.ts.map +1 -0
  105. package/dist/utils/index.js +21 -0
  106. package/dist/utils/index.js.map +1 -0
  107. package/dist/utils/mime-types.d.ts +13 -0
  108. package/dist/utils/mime-types.d.ts.map +1 -0
  109. package/dist/utils/mime-types.js +247 -0
  110. package/dist/utils/mime-types.js.map +1 -0
  111. package/dist/utils/network-sync.d.ts +30 -0
  112. package/dist/utils/network-sync.d.ts.map +1 -0
  113. package/dist/utils/network-sync.js +391 -0
  114. package/dist/utils/network-sync.js.map +1 -0
  115. package/dist/utils/node-polyfills.d.ts +9 -0
  116. package/dist/utils/node-polyfills.d.ts.map +1 -0
  117. package/dist/utils/node-polyfills.js +9 -0
  118. package/dist/utils/node-polyfills.js.map +1 -0
  119. package/dist/utils/output.d.ts +129 -0
  120. package/dist/utils/output.d.ts.map +1 -0
  121. package/dist/utils/output.js +375 -0
  122. package/dist/utils/output.js.map +1 -0
  123. package/dist/utils/repo-factory.d.ts +15 -0
  124. package/dist/utils/repo-factory.d.ts.map +1 -0
  125. package/dist/utils/repo-factory.js +156 -0
  126. package/dist/utils/repo-factory.js.map +1 -0
  127. package/dist/utils/string-similarity.d.ts +14 -0
  128. package/dist/utils/string-similarity.d.ts.map +1 -0
  129. package/dist/utils/string-similarity.js +43 -0
  130. package/dist/utils/string-similarity.js.map +1 -0
  131. package/dist/utils/text-diff.d.ts +37 -0
  132. package/dist/utils/text-diff.d.ts.map +1 -0
  133. package/dist/utils/text-diff.js +131 -0
  134. package/dist/utils/text-diff.js.map +1 -0
  135. package/dist/utils/trace.d.ts +19 -0
  136. package/dist/utils/trace.d.ts.map +1 -0
  137. package/dist/utils/trace.js +68 -0
  138. package/dist/utils/trace.js.map +1 -0
  139. package/dist/version.d.ts +11 -0
  140. package/dist/version.d.ts.map +1 -0
  141. package/dist/version.js +93 -0
  142. package/dist/version.js.map +1 -0
  143. package/package.json +5 -1
  144. package/.prettierrc +0 -9
  145. package/flake.lock +0 -128
  146. package/flake.nix +0 -66
  147. package/pnpm-workspace.yaml +0 -5
  148. package/src/branches.ts +0 -93
  149. package/src/cli.ts +0 -292
  150. package/src/config.ts +0 -64
  151. package/src/fs-tree.ts +0 -70
  152. package/src/ignore.ts +0 -33
  153. package/src/index.ts +0 -38
  154. package/src/log.ts +0 -8
  155. package/src/pushwork.ts +0 -1055
  156. package/src/repo.ts +0 -76
  157. package/src/shapes/custom.ts +0 -29
  158. package/src/shapes/file.ts +0 -115
  159. package/src/shapes/index.ts +0 -19
  160. package/src/shapes/patchwork-folder.ts +0 -156
  161. package/src/shapes/types.ts +0 -79
  162. package/src/shapes/vfs.ts +0 -93
  163. package/src/stash.ts +0 -106
  164. package/test/integration/branches.test.ts +0 -389
  165. package/test/integration/pushwork.test.ts +0 -547
  166. package/test/setup.ts +0 -29
  167. package/test/unit/doc-shape.test.ts +0 -612
  168. package/tsconfig.json +0 -22
  169. package/vitest.config.ts +0 -14
package/src/branches.ts DELETED
@@ -1,93 +0,0 @@
1
- import * as fs from "fs/promises";
2
- import * as path from "path";
3
- import {
4
- type AutomergeUrl,
5
- type DocHandle,
6
- type Repo,
7
- } from "@automerge/automerge-repo";
8
- import { log } from "./log.js";
9
-
10
- const dlog = log("branches");
11
-
12
- export const META = "@patchwork";
13
- export const DEFAULT_BRANCH = "default";
14
-
15
- export type BranchesDoc = {
16
- "@patchwork": { type: "branches" };
17
- branches: { [name: string]: AutomergeUrl };
18
- };
19
-
20
- export const isBranchesDoc = (doc: unknown): doc is BranchesDoc => {
21
- if (!doc || typeof doc !== "object") return false;
22
- const meta = (doc as Record<string, unknown>)[META];
23
- return (
24
- !!meta &&
25
- typeof meta === "object" &&
26
- (meta as Record<string, unknown>).type === "branches"
27
- );
28
- };
29
-
30
- export function detectDocType(
31
- doc: unknown,
32
- ): "branches" | "folder" | "directory" | "unknown" {
33
- if (!doc || typeof doc !== "object") return "unknown";
34
- const meta = (doc as Record<string, unknown>)[META];
35
- if (!meta || typeof meta !== "object") return "unknown";
36
- const t = (meta as Record<string, unknown>).type;
37
- if (t === "branches" || t === "folder" || t === "directory") return t;
38
- return "unknown";
39
- }
40
-
41
- export async function resolveEffectiveRoot(
42
- repo: Repo,
43
- rootHandle: DocHandle<unknown>,
44
- branchName: string | null,
45
- ): Promise<DocHandle<unknown>> {
46
- const doc = rootHandle.doc();
47
- if (!isBranchesDoc(doc)) return rootHandle;
48
- if (!branchName) {
49
- throw new Error(
50
- "pushwork repo uses branches but no branch name is set",
51
- );
52
- }
53
- const url = doc.branches?.[branchName];
54
- if (!url) {
55
- throw new Error(
56
- `branch "${branchName}" not found in branches doc ${rootHandle.url}`,
57
- );
58
- }
59
- dlog("resolveEffectiveRoot branch=%s → %s", branchName, url);
60
- return repo.find<unknown>(url);
61
- }
62
-
63
- export function listBranchNames(branchesDoc: BranchesDoc): string[] {
64
- return Object.keys(branchesDoc.branches ?? {}).sort();
65
- }
66
-
67
- const BRANCH_FILE = path.join(".pushwork", "branch");
68
-
69
- export async function readBranchFile(root: string): Promise<string | null> {
70
- try {
71
- const text = await fs.readFile(path.join(root, BRANCH_FILE), "utf8");
72
- return text.trim() || null;
73
- } catch (err) {
74
- if ((err as NodeJS.ErrnoException).code === "ENOENT") return null;
75
- throw err;
76
- }
77
- }
78
-
79
- export async function writeBranchFile(
80
- root: string,
81
- branchName: string,
82
- ): Promise<void> {
83
- await fs.mkdir(path.join(root, ".pushwork"), { recursive: true });
84
- await fs.writeFile(path.join(root, BRANCH_FILE), branchName + "\n");
85
- }
86
-
87
- export async function deleteBranchFile(root: string): Promise<void> {
88
- try {
89
- await fs.unlink(path.join(root, BRANCH_FILE));
90
- } catch (err) {
91
- if ((err as NodeJS.ErrnoException).code !== "ENOENT") throw err;
92
- }
93
- }
package/src/cli.ts DELETED
@@ -1,292 +0,0 @@
1
- #!/usr/bin/env node
2
- import "./log.js"; // sets up DEBUG=true → DEBUG=* before anything else
3
- import { Command } from "@commander-js/extra-typings";
4
- import * as path from "path";
5
- import {
6
- clone,
7
- createBranch,
8
- currentBranch,
9
- cutWorkdir,
10
- diff,
11
- init,
12
- listBranches,
13
- mergeBranch,
14
- pasteStash,
15
- previewMerge,
16
- save,
17
- showStashes,
18
- status,
19
- switchBranch,
20
- sync,
21
- url,
22
- } from "./pushwork.js";
23
- import { log } from "./log.js";
24
-
25
- const dlog = log("cli");
26
-
27
- const collect = (value: string, prev: string[] | undefined) =>
28
- (prev ?? []).concat(value);
29
-
30
- const program = new Command()
31
- .name("pushwork")
32
- .description("Bidirectional directory synchronization using Automerge CRDTs");
33
-
34
- program
35
- .command("init")
36
- .description("Initialize pushwork in a directory")
37
- .argument("[dir]", "Directory to initialize", ".")
38
- .option("--sub", "Use subduction backend")
39
- .option(
40
- "--shape <shape>",
41
- "Document shape: vfs, patchwork-folder, or path to a custom shape module",
42
- "vfs",
43
- )
44
- .option(
45
- "--artifact-dir <dir>",
46
- "Directory whose contents are stored as ImmutableString and pinned with heads in the root doc. Repeatable.",
47
- collect,
48
- undefined as string[] | undefined,
49
- )
50
- .option("--no-branches", "Skip wrapping the root doc in a branches doc")
51
- .action(async (dir, opts) => {
52
- dlog("init dir=%s opts=%o", dir, opts);
53
- const u = await init({
54
- dir: path.resolve(dir),
55
- backend: opts.sub ? "subduction" : "legacy",
56
- shape: opts.shape,
57
- artifactDirectories: opts.artifactDir,
58
- branches: opts.branches,
59
- });
60
- process.stderr.write(`initialized ${u}\n`);
61
- });
62
-
63
- program
64
- .command("clone")
65
- .description("Clone an automerge URL into a directory")
66
- .argument("<url>", "automerge: URL")
67
- .argument("[dir]", "Target directory", ".")
68
- .option("--sub", "Use subduction backend")
69
- .option(
70
- "--shape <shape>",
71
- "Document shape: vfs, patchwork-folder, or path to a custom shape module",
72
- "vfs",
73
- )
74
- .option(
75
- "--artifact-dir <dir>",
76
- "Directory whose contents are stored as ImmutableString and pinned with heads in the root doc. Repeatable.",
77
- collect,
78
- undefined as string[] | undefined,
79
- )
80
- .option("--branch <name>", "Branch to track when cloning a branches doc")
81
- .action(async (u, dir, opts) => {
82
- dlog("clone url=%s dir=%s opts=%o", u, dir, opts);
83
- await clone({
84
- url: u,
85
- dir: path.resolve(dir),
86
- backend: opts.sub ? "subduction" : "legacy",
87
- shape: opts.shape,
88
- artifactDirectories: opts.artifactDir,
89
- branch: opts.branch,
90
- });
91
- process.stderr.write(`cloned into ${path.resolve(dir)}\n`);
92
- });
93
-
94
- program
95
- .command("url")
96
- .description("Print the automerge URL of this pushwork repo")
97
- .action(async () => {
98
- dlog("url cwd=%s", process.cwd());
99
- const u = await url(process.cwd());
100
- process.stdout.write(u + "\n");
101
- });
102
-
103
- program
104
- .command("sync")
105
- .description("Sync local changes with peers")
106
- .action(async () => {
107
- dlog("sync cwd=%s", process.cwd());
108
- await sync(process.cwd());
109
- process.stderr.write("synced\n");
110
- });
111
-
112
- program
113
- .command("save")
114
- .alias("commit")
115
- .description("Commit local changes to the current branch without contacting the sync server")
116
- .action(async () => {
117
- dlog("save cwd=%s", process.cwd());
118
- await save(process.cwd());
119
- process.stderr.write("saved\n");
120
- });
121
-
122
- program
123
- .command("status")
124
- .description("Show current branch and changes against it")
125
- .action(async () => {
126
- const { branch, diff: d } = await status(process.cwd());
127
- const lines: string[] = [];
128
- if (branch) lines.push(`On branch ${branch}`);
129
- else lines.push("(no branches)");
130
- const total = d.added.length + d.modified.length + d.deleted.length;
131
- if (total === 0) {
132
- lines.push("nothing to save, working tree clean");
133
- } else {
134
- lines.push("Changes:");
135
- for (const p of d.modified) lines.push(` modified: ${p}`);
136
- for (const p of d.added) lines.push(` added: ${p}`);
137
- for (const p of d.deleted) lines.push(` deleted: ${p}`);
138
- }
139
- process.stdout.write(lines.join("\n") + "\n");
140
- });
141
-
142
- program
143
- .command("diff")
144
- .description("Show textual diffs of local changes against the current branch")
145
- .argument("[path]", "Limit to a specific path")
146
- .action(async (limitPath) => {
147
- const entries = await diff(process.cwd(), limitPath);
148
- if (entries.length === 0) {
149
- process.stdout.write("(no changes)\n");
150
- return;
151
- }
152
- const { createPatch } = await import("diff");
153
- const td = new TextDecoder("utf-8", { fatal: false });
154
- for (const e of entries) {
155
- const before = e.before ? td.decode(e.before) : "";
156
- const after = e.after ? td.decode(e.after) : "";
157
- const header =
158
- e.kind === "added" ? `+++ ${e.path}` :
159
- e.kind === "deleted" ? `--- ${e.path}` :
160
- `*** ${e.path}`;
161
- process.stdout.write(header + "\n");
162
- process.stdout.write(createPatch(e.path, before, after, "", "") + "\n");
163
- }
164
- });
165
-
166
- program
167
- .command("branch")
168
- .description("With no arg: print the current branch. With <name>: create a new branch from the current one (offline).")
169
- .argument("[name]", "Name of the new branch")
170
- .action(async (name) => {
171
- if (!name) {
172
- const cur = await currentBranch(process.cwd());
173
- process.stdout.write((cur ?? "(none)") + "\n");
174
- return;
175
- }
176
- const newUrl = await createBranch(process.cwd(), name);
177
- process.stderr.write(`created branch ${name} → ${newUrl}\n`);
178
- });
179
-
180
- program
181
- .command("switch")
182
- .description("Switch to a branch (offline). With no name: list branches.")
183
- .argument("[name]", "Name of the branch to switch to")
184
- .action(async (name) => {
185
- if (!name) {
186
- const { current, names } = await listBranches(process.cwd());
187
- for (const n of names) {
188
- process.stdout.write(`${n === current ? "* " : " "}${n}\n`);
189
- }
190
- return;
191
- }
192
- await switchBranch(process.cwd(), name);
193
- process.stderr.write(`switched to ${name}\n`);
194
- });
195
-
196
- program
197
- .command("merge")
198
- .description("Apply changes from <source> branch onto the current branch (offline)")
199
- .argument("<source>", "Branch to merge into the current one")
200
- .option("--dry", "Preview the merge without applying")
201
- .action(async (source, opts) => {
202
- if (opts.dry) {
203
- const preview = await previewMerge(process.cwd(), source);
204
- const lines: string[] = [];
205
- lines.push(`Merging ${preview.source} into ${preview.target} (preview)`);
206
- if (preview.entries.length === 0) {
207
- lines.push("(no changes)");
208
- process.stdout.write(lines.join("\n") + "\n");
209
- return;
210
- }
211
- const { createPatch } = await import("diff");
212
- const td = new TextDecoder("utf-8", { fatal: false });
213
- for (const e of preview.entries) {
214
- const before = e.before ? td.decode(e.before) : "";
215
- const after = td.decode(e.after);
216
- const tag = e.kind === "added" ? "added" : "merged";
217
- lines.push(` ${tag}: ${e.path}`);
218
- lines.push(createPatch(e.path, before, after, "", ""));
219
- }
220
- process.stdout.write(lines.join("\n") + "\n");
221
- return;
222
- }
223
- const report = await mergeBranch(process.cwd(), source);
224
- const lines: string[] = [];
225
- lines.push(`Merging ${report.source} into ${report.target}`);
226
- if (report.merged.length === 0 && report.added.length === 0) {
227
- lines.push("(no changes)");
228
- } else {
229
- for (const p of report.merged) lines.push(` merged: ${p}`);
230
- for (const p of report.added) lines.push(` added: ${p}`);
231
- }
232
- process.stdout.write(lines.join("\n") + "\n");
233
- });
234
-
235
- program
236
- .command("cut")
237
- .description("Stash working-tree changes against the current branch and reset the tree to clean (offline)")
238
- .argument("[name]", "Optional name for the stash entry")
239
- .action(async (name) => {
240
- const result = await cutWorkdir(process.cwd(), { name });
241
- process.stderr.write(`cut #${result.id}: ${result.entries} entr${result.entries === 1 ? "y" : "ies"}\n`);
242
- });
243
-
244
- program
245
- .command("paste")
246
- .description("Re-apply a stashed set of changes; default is the most recent (offline)")
247
- .argument("[id-or-name]", "Stash id or name")
248
- .action(async (selector) => {
249
- const result = await pasteStash(process.cwd(), selector);
250
- process.stderr.write(
251
- `pasted #${result.id}${result.name ? ` (${result.name})` : ""}: ${result.entries} entr${result.entries === 1 ? "y" : "ies"}\n`,
252
- );
253
- });
254
-
255
- program
256
- .command("cuts")
257
- .description("List stashed change sets (newest first)")
258
- .action(async () => {
259
- const stashes = await showStashes(process.cwd());
260
- if (stashes.length === 0) {
261
- process.stdout.write("(no stashes)\n");
262
- return;
263
- }
264
- for (const s of stashes) {
265
- const ts = new Date(s.createdAt).toISOString();
266
- const label = s.name ? `"${s.name}"` : "";
267
- const branch = s.branch ? ` on ${s.branch}` : "";
268
- process.stdout.write(
269
- `#${s.id}${label ? " " + label : ""}${branch} ${s.entries.length} entr${s.entries.length === 1 ? "y" : "ies"} ${ts}\n`,
270
- );
271
- }
272
- });
273
-
274
- program
275
- .command("branches")
276
- .description("List branches")
277
- .action(async () => {
278
- const { current, names } = await listBranches(process.cwd());
279
- for (const n of names) {
280
- process.stdout.write(`${n === current ? "* " : " "}${n}\n`);
281
- }
282
- });
283
-
284
- program
285
- .parseAsync(process.argv)
286
- .then(() => process.exit(0))
287
- .catch((err) => {
288
- process.stderr.write(
289
- `pushwork: ${err instanceof Error ? err.message : String(err)}\n`,
290
- );
291
- process.exit(1);
292
- });
package/src/config.ts DELETED
@@ -1,64 +0,0 @@
1
- import * as fs from "fs/promises";
2
- import * as path from "path";
3
- import type { AutomergeUrl } from "@automerge/automerge-repo";
4
-
5
- export type Backend = "legacy" | "subduction";
6
-
7
- export const CONFIG_VERSION = 3;
8
-
9
- export interface PushworkConfig {
10
- version: typeof CONFIG_VERSION;
11
- rootUrl: AutomergeUrl;
12
- backend: Backend;
13
- shape: string;
14
- artifactDirectories: string[];
15
- branches: boolean;
16
- }
17
-
18
- const DIR = ".pushwork";
19
- const CONFIG = "config.json";
20
- const STORAGE = "storage";
21
-
22
- export const pushworkDir = (root: string) => path.join(root, DIR);
23
- export const storageDir = (root: string) => path.join(root, DIR, STORAGE);
24
-
25
- export async function readConfig(root: string): Promise<PushworkConfig> {
26
- const text = await fs.readFile(path.join(root, DIR, CONFIG), "utf8");
27
- const parsed = JSON.parse(text) as Partial<PushworkConfig>;
28
- if (parsed.version !== CONFIG_VERSION) {
29
- throw new Error(
30
- `pushwork config version mismatch: expected ${CONFIG_VERSION}, got ${parsed.version ?? "(missing)"}`,
31
- );
32
- }
33
- if (!parsed.rootUrl) throw new Error("pushwork config missing rootUrl");
34
- if (!parsed.backend) throw new Error("pushwork config missing backend");
35
- if (!parsed.shape) throw new Error("pushwork config missing shape");
36
- return {
37
- version: CONFIG_VERSION,
38
- rootUrl: parsed.rootUrl,
39
- backend: parsed.backend,
40
- shape: parsed.shape,
41
- artifactDirectories: parsed.artifactDirectories ?? [],
42
- branches: parsed.branches ?? true,
43
- };
44
- }
45
-
46
- export async function writeConfig(
47
- root: string,
48
- config: PushworkConfig,
49
- ): Promise<void> {
50
- await fs.mkdir(path.join(root, DIR), { recursive: true });
51
- await fs.writeFile(
52
- path.join(root, DIR, CONFIG),
53
- JSON.stringify(config, null, 2) + "\n",
54
- );
55
- }
56
-
57
- export async function configExists(root: string): Promise<boolean> {
58
- try {
59
- await fs.access(path.join(root, DIR, CONFIG));
60
- return true;
61
- } catch {
62
- return false;
63
- }
64
- }
package/src/fs-tree.ts DELETED
@@ -1,70 +0,0 @@
1
- import * as fs from "fs/promises";
2
- import * as path from "path";
3
- import type { Ignore } from "ignore";
4
- import { isIgnored } from "./ignore.js";
5
- import { log } from "./log.js";
6
-
7
- const dlog = log("fs-tree");
8
-
9
- export type FileTree = Map<string, Uint8Array>;
10
-
11
- const toPosix = (p: string) => p.split(path.sep).join("/");
12
-
13
- export async function walkDir(root: string, ig: Ignore): Promise<FileTree> {
14
- dlog("walkDir root=%s", root);
15
- const tree: FileTree = new Map();
16
- await walk(root, root, ig, tree);
17
- dlog("walkDir done: %d files", tree.size);
18
- return tree;
19
- }
20
-
21
- async function walk(
22
- root: string,
23
- current: string,
24
- ig: Ignore,
25
- tree: FileTree,
26
- ): Promise<void> {
27
- let names: string[];
28
- try {
29
- names = await fs.readdir(current);
30
- } catch {
31
- return;
32
- }
33
- for (const name of names) {
34
- const full = path.join(current, name);
35
- const rel = toPosix(path.relative(root, full));
36
- if (isIgnored(ig, rel)) {
37
- dlog("skip ignored: %s", rel);
38
- continue;
39
- }
40
- let stat;
41
- try {
42
- stat = await fs.lstat(full);
43
- } catch {
44
- continue;
45
- }
46
- if (stat.isSymbolicLink()) continue;
47
- if (stat.isDirectory()) {
48
- await walk(root, full, ig, tree);
49
- } else if (stat.isFile()) {
50
- const bytes = await fs.readFile(full);
51
- tree.set(rel, new Uint8Array(bytes));
52
- }
53
- }
54
- }
55
-
56
- export function byteEq(a: Uint8Array | undefined, b: Uint8Array): boolean {
57
- if (!a) return false;
58
- if (a.length !== b.length) return false;
59
- for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return false;
60
- return true;
61
- }
62
-
63
- export async function writeFileAtomic(
64
- target: string,
65
- bytes: Uint8Array,
66
- ): Promise<void> {
67
- await fs.mkdir(path.dirname(target), { recursive: true });
68
- await fs.writeFile(target, bytes);
69
- }
70
-
package/src/ignore.ts DELETED
@@ -1,33 +0,0 @@
1
- import * as fs from "fs/promises";
2
- import * as path from "path";
3
- import ignore, { type Ignore } from "ignore";
4
- import { log } from "./log.js";
5
-
6
- const dlog = log("ignore");
7
-
8
- export const ALWAYS_IGNORE = [".pushwork", ".git", "node_modules"];
9
- export const IGNORE_FILE = ".pushworkignore";
10
-
11
- export async function loadIgnore(root: string): Promise<Ignore> {
12
- const ig = ignore().add(ALWAYS_IGNORE);
13
- dlog("always-ignored: %o", ALWAYS_IGNORE);
14
- try {
15
- const text = await fs.readFile(path.join(root, IGNORE_FILE), "utf8");
16
- const patterns = text
17
- .split(/\r?\n/)
18
- .map((l) => l.trim())
19
- .filter((l) => l && !l.startsWith("#"));
20
- ig.add(patterns);
21
- dlog("loaded %d patterns from %s", patterns.length, IGNORE_FILE);
22
- } catch (err) {
23
- const e = err as NodeJS.ErrnoException;
24
- if (e.code !== "ENOENT") throw err;
25
- dlog("no %s in %s", IGNORE_FILE, root);
26
- }
27
- return ig;
28
- }
29
-
30
- export function isIgnored(ig: Ignore, relativePath: string): boolean {
31
- if (relativePath === "" || relativePath === ".") return false;
32
- return ig.ignores(relativePath);
33
- }
package/src/index.ts DELETED
@@ -1,38 +0,0 @@
1
- export {
2
- init,
3
- clone,
4
- sync,
5
- save,
6
- status,
7
- diff,
8
- url,
9
- currentBranch,
10
- createBranch,
11
- switchBranch,
12
- listBranches,
13
- mergeBranch,
14
- previewMerge,
15
- cutWorkdir,
16
- pasteStash,
17
- showStashes,
18
- } from "./pushwork.js";
19
- export type { MergeReport, MergePreview, MergePreviewEntry } from "./pushwork.js";
20
- export type { Stash, StashEntry } from "./stash.js";
21
- export type { Backend, PushworkConfig } from "./config.js";
22
- export { CONFIG_VERSION } from "./config.js";
23
- export type { Shape, VfsNode, UnixFileEntry } from "./shapes/index.js";
24
- export {
25
- vfsShape,
26
- patchworkFolderShape,
27
- isInArtifactDir,
28
- normalizeArtifactDir,
29
- pinUrl,
30
- stripHeads,
31
- } from "./shapes/index.js";
32
- export {
33
- DEFAULT_BRANCH,
34
- detectDocType,
35
- isBranchesDoc,
36
- resolveEffectiveRoot,
37
- type BranchesDoc,
38
- } from "./branches.js";
package/src/log.ts DELETED
@@ -1,8 +0,0 @@
1
- import debug from "debug";
2
-
3
- if (process.env.DEBUG === "true") {
4
- process.env.DEBUG = "*";
5
- debug.enable("*");
6
- }
7
-
8
- export const log = (ns: string) => debug(`pushwork:${ns}`);