@ricsam/r5d-worker 0.0.137 → 0.0.139

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 (236) hide show
  1. package/README.md +4 -94
  2. package/dist/cjs/package.json +1 -1
  3. package/dist/mjs/main.mjs +46 -7543
  4. package/dist/mjs/package.json +1 -1
  5. package/dist/mjs/personal/action-scheduler.mjs +31 -0
  6. package/dist/mjs/personal/client.mjs +215 -0
  7. package/dist/mjs/personal/runtime.mjs +359 -0
  8. package/dist/mjs/personal/tcp.mjs +217 -0
  9. package/dist/mjs/runtime/adapter.mjs +42 -0
  10. package/dist/mjs/runtime/client.mjs +95 -0
  11. package/dist/mjs/runtime/daemon.mjs +131 -0
  12. package/dist/mjs/runtime/executor.mjs +436 -0
  13. package/dist/mjs/runtime/index.mjs +15 -0
  14. package/dist/mjs/runtime/protocol.mjs +139 -0
  15. package/dist/mjs/runtime/pty-host.mjs +156 -0
  16. package/dist/mjs/runtime/pty.mjs +127 -0
  17. package/dist/mjs/runtime/releases/artifacts.mjs +201 -0
  18. package/dist/mjs/runtime/releases/grants.mjs +93 -0
  19. package/dist/mjs/runtime/releases/main.mjs +101 -0
  20. package/dist/mjs/runtime/releases/manager.mjs +541 -0
  21. package/dist/mjs/runtime/releases/process.mjs +88 -0
  22. package/dist/mjs/runtime/releases/retirement.mjs +18 -0
  23. package/dist/mjs/runtime/releases/rpc-client.mjs +184 -0
  24. package/dist/mjs/runtime/releases/rpc-main.mjs +213 -0
  25. package/dist/mjs/runtime/releases/rpc-protocol.mjs +103 -0
  26. package/dist/mjs/runtime/storage.mjs +156 -0
  27. package/dist/mjs/runtime/workspace/artifacts.mjs +121 -0
  28. package/dist/mjs/runtime/workspace/authority.mjs +1355 -0
  29. package/dist/mjs/runtime/workspace/contracts.mjs +60 -0
  30. package/dist/mjs/runtime/workspace/file-write.mjs +23 -0
  31. package/dist/mjs/runtime/workspace/files.mjs +308 -0
  32. package/dist/mjs/runtime/workspace/http-body.mjs +45 -0
  33. package/dist/mjs/runtime/workspace/storage-client.mjs +59 -0
  34. package/dist/mjs/runtime/workspace/storage-wire.mjs +53 -0
  35. package/dist/types/main.d.ts +0 -1167
  36. package/dist/types/personal/action-scheduler.d.ts +23 -0
  37. package/dist/types/personal/client.d.ts +14 -0
  38. package/dist/types/personal/runtime.d.ts +121 -0
  39. package/dist/types/personal/tcp.d.ts +14 -0
  40. package/dist/types/runtime/client.d.ts +3 -1
  41. package/dist/types/runtime/executor.d.ts +6 -1
  42. package/dist/types/runtime/protocol.d.ts +176 -4
  43. package/dist/types/runtime/pty.d.ts +1 -0
  44. package/dist/types/runtime/releases/artifacts.d.ts +51 -0
  45. package/dist/types/runtime/releases/grants.d.ts +16 -0
  46. package/dist/types/runtime/releases/manager.d.ts +145 -0
  47. package/dist/types/runtime/releases/process.d.ts +14 -0
  48. package/dist/types/runtime/releases/retirement.d.ts +15 -0
  49. package/dist/types/runtime/releases/rpc-client.d.ts +52 -0
  50. package/dist/types/runtime/releases/rpc-main.d.ts +10 -0
  51. package/dist/types/runtime/releases/rpc-protocol.d.ts +1139 -0
  52. package/dist/types/runtime/workspace/artifacts.d.ts +30 -0
  53. package/dist/types/runtime/workspace/authority.d.ts +338 -0
  54. package/dist/types/runtime/workspace/contracts.d.ts +86 -0
  55. package/dist/types/runtime/workspace/file-write.d.ts +14 -0
  56. package/dist/types/runtime/workspace/files.d.ts +21 -0
  57. package/dist/types/runtime/workspace/http-body.d.ts +9 -0
  58. package/dist/types/runtime/workspace/storage-client.d.ts +23 -0
  59. package/dist/types/runtime/workspace/storage-wire.d.ts +138 -0
  60. package/package.json +8 -6
  61. package/dist/cjs/atomic-rename.cjs +0 -303
  62. package/dist/cjs/browser-upload.cjs +0 -73
  63. package/dist/cjs/cli-update.cjs +0 -108
  64. package/dist/cjs/command-launcher.cjs +0 -423
  65. package/dist/cjs/control-command-policy.cjs +0 -177
  66. package/dist/cjs/git-blob-hash.cjs +0 -41
  67. package/dist/cjs/git-identity.cjs +0 -39
  68. package/dist/cjs/git-process-environment.cjs +0 -133
  69. package/dist/cjs/inline-output.cjs +0 -62
  70. package/dist/cjs/main.cjs +0 -7517
  71. package/dist/cjs/managed-paths.cjs +0 -215
  72. package/dist/cjs/plan-lint.cjs +0 -165
  73. package/dist/cjs/plan-parser.cjs +0 -127
  74. package/dist/cjs/plan-paths.cjs +0 -53
  75. package/dist/cjs/port-forward-client.cjs +0 -206
  76. package/dist/cjs/process-tree.cjs +0 -117
  77. package/dist/cjs/project-checkout-garbage.cjs +0 -219
  78. package/dist/cjs/project-mirror-fetch-policy.cjs +0 -82
  79. package/dist/cjs/project-mirror-refs-token.cjs +0 -85
  80. package/dist/cjs/project-workspace-state.cjs +0 -972
  81. package/dist/cjs/project-worktrees.cjs +0 -1713
  82. package/dist/cjs/pty-output-coalescer.cjs +0 -87
  83. package/dist/cjs/recovery-journal-protocol.cjs +0 -56
  84. package/dist/cjs/recovery-journal-runtime.cjs +0 -133
  85. package/dist/cjs/recovery-journal-thread.cjs +0 -9
  86. package/dist/cjs/recovery-journal.cjs +0 -742
  87. package/dist/cjs/recovery-output-window.cjs +0 -77
  88. package/dist/cjs/recovery-protocol.cjs +0 -31
  89. package/dist/cjs/recovery-store.cjs +0 -252
  90. package/dist/cjs/registry-auth.cjs +0 -310
  91. package/dist/cjs/repository-transition-policy.cjs +0 -49
  92. package/dist/cjs/runtime-version.cjs +0 -88
  93. package/dist/cjs/session-file-mutations.cjs +0 -61
  94. package/dist/cjs/supervisor.cjs +0 -147
  95. package/dist/cjs/three-way-merge.cjs +0 -346
  96. package/dist/cjs/working-tree-mirror.cjs +0 -1872
  97. package/dist/cjs/workspace-automatic-sync-policy.cjs +0 -79
  98. package/dist/cjs/workspace-branch-incarnation-policy.cjs +0 -37
  99. package/dist/cjs/workspace-command-sync-policy.cjs +0 -112
  100. package/dist/cjs/workspace-command-targets.cjs +0 -63
  101. package/dist/cjs/workspace-filesystem-executor-thread.cjs +0 -36
  102. package/dist/cjs/workspace-filesystem-executor.cjs +0 -327
  103. package/dist/cjs/workspace-filesystem-job-types.cjs +0 -144
  104. package/dist/cjs/workspace-filesystem-jobs.cjs +0 -59
  105. package/dist/cjs/workspace-git-sync.cjs +0 -3595
  106. package/dist/cjs/workspace-hydration-ledger.cjs +0 -83
  107. package/dist/cjs/workspace-hydration-merge.cjs +0 -448
  108. package/dist/cjs/workspace-hydration-recovery-state.cjs +0 -53
  109. package/dist/cjs/workspace-incident-state.cjs +0 -37
  110. package/dist/cjs/workspace-merge-projection.cjs +0 -628
  111. package/dist/cjs/workspace-mount-boundary.cjs +0 -71
  112. package/dist/cjs/workspace-mount-hold-fence.cjs +0 -120
  113. package/dist/cjs/workspace-mutation-gate.cjs +0 -127
  114. package/dist/cjs/workspace-path-move.cjs +0 -195
  115. package/dist/cjs/workspace-preserve-only-policy.cjs +0 -36
  116. package/dist/cjs/workspace-project-config-policy.cjs +0 -53
  117. package/dist/cjs/workspace-projection-ledger.cjs +0 -81
  118. package/dist/cjs/workspace-publication-evidence.cjs +0 -46
  119. package/dist/cjs/workspace-sync-coalescer.cjs +0 -58
  120. package/dist/mjs/atomic-rename.mjs +0 -261
  121. package/dist/mjs/browser-upload.mjs +0 -42
  122. package/dist/mjs/command-launcher.mjs +0 -393
  123. package/dist/mjs/control-command-policy.mjs +0 -146
  124. package/dist/mjs/git-blob-hash.mjs +0 -16
  125. package/dist/mjs/git-identity.mjs +0 -15
  126. package/dist/mjs/git-process-environment.mjs +0 -105
  127. package/dist/mjs/inline-output.mjs +0 -37
  128. package/dist/mjs/managed-paths.mjs +0 -174
  129. package/dist/mjs/plan-lint.mjs +0 -129
  130. package/dist/mjs/plan-parser.mjs +0 -94
  131. package/dist/mjs/plan-paths.mjs +0 -25
  132. package/dist/mjs/port-forward-client.mjs +0 -172
  133. package/dist/mjs/project-checkout-garbage.mjs +0 -178
  134. package/dist/mjs/project-mirror-fetch-policy.mjs +0 -56
  135. package/dist/mjs/project-mirror-refs-token.mjs +0 -53
  136. package/dist/mjs/project-workspace-state.mjs +0 -937
  137. package/dist/mjs/project-worktrees.mjs +0 -1657
  138. package/dist/mjs/pty-output-coalescer.mjs +0 -61
  139. package/dist/mjs/recovery-journal-protocol.mjs +0 -30
  140. package/dist/mjs/recovery-journal-runtime.mjs +0 -112
  141. package/dist/mjs/recovery-journal-thread.mjs +0 -8
  142. package/dist/mjs/recovery-journal.mjs +0 -694
  143. package/dist/mjs/recovery-output-window.mjs +0 -53
  144. package/dist/mjs/recovery-protocol.mjs +0 -6
  145. package/dist/mjs/recovery-store.mjs +0 -217
  146. package/dist/mjs/registry-auth.mjs +0 -269
  147. package/dist/mjs/repository-transition-policy.mjs +0 -22
  148. package/dist/mjs/runtime-version.mjs +0 -54
  149. package/dist/mjs/session-file-mutations.mjs +0 -37
  150. package/dist/mjs/supervisor.mjs +0 -114
  151. package/dist/mjs/three-way-merge.mjs +0 -318
  152. package/dist/mjs/working-tree-mirror.mjs +0 -1817
  153. package/dist/mjs/workspace-automatic-sync-policy.mjs +0 -48
  154. package/dist/mjs/workspace-branch-incarnation-policy.mjs +0 -13
  155. package/dist/mjs/workspace-command-sync-policy.mjs +0 -84
  156. package/dist/mjs/workspace-command-targets.mjs +0 -37
  157. package/dist/mjs/workspace-filesystem-executor-thread.mjs +0 -38
  158. package/dist/mjs/workspace-filesystem-executor.mjs +0 -287
  159. package/dist/mjs/workspace-filesystem-job-types.mjs +0 -116
  160. package/dist/mjs/workspace-filesystem-jobs.mjs +0 -55
  161. package/dist/mjs/workspace-git-sync.mjs +0 -3553
  162. package/dist/mjs/workspace-hydration-ledger.mjs +0 -58
  163. package/dist/mjs/workspace-hydration-merge.mjs +0 -414
  164. package/dist/mjs/workspace-hydration-recovery-state.mjs +0 -29
  165. package/dist/mjs/workspace-incident-state.mjs +0 -12
  166. package/dist/mjs/workspace-merge-projection.mjs +0 -594
  167. package/dist/mjs/workspace-mount-boundary.mjs +0 -37
  168. package/dist/mjs/workspace-mount-hold-fence.mjs +0 -95
  169. package/dist/mjs/workspace-mutation-gate.mjs +0 -103
  170. package/dist/mjs/workspace-path-move.mjs +0 -160
  171. package/dist/mjs/workspace-preserve-only-policy.mjs +0 -11
  172. package/dist/mjs/workspace-project-config-policy.mjs +0 -27
  173. package/dist/mjs/workspace-projection-ledger.mjs +0 -57
  174. package/dist/mjs/workspace-publication-evidence.mjs +0 -21
  175. package/dist/mjs/workspace-sync-coalescer.mjs +0 -34
  176. package/dist/types/atomic-rename.d.ts +0 -78
  177. package/dist/types/browser-upload.d.ts +0 -7
  178. package/dist/types/command-launcher.d.ts +0 -104
  179. package/dist/types/control-command-policy.d.ts +0 -53
  180. package/dist/types/git-blob-hash.d.ts +0 -10
  181. package/dist/types/git-identity.d.ts +0 -9
  182. package/dist/types/git-process-environment.d.ts +0 -9
  183. package/dist/types/inline-output.d.ts +0 -3
  184. package/dist/types/managed-paths.d.ts +0 -19
  185. package/dist/types/plan-lint.d.ts +0 -16
  186. package/dist/types/plan-parser.d.ts +0 -38
  187. package/dist/types/plan-paths.d.ts +0 -36
  188. package/dist/types/port-forward-client.d.ts +0 -8
  189. package/dist/types/project-checkout-garbage.d.ts +0 -86
  190. package/dist/types/project-mirror-fetch-policy.d.ts +0 -84
  191. package/dist/types/project-mirror-refs-token.d.ts +0 -53
  192. package/dist/types/project-workspace-state.d.ts +0 -188
  193. package/dist/types/project-worktrees.d.ts +0 -397
  194. package/dist/types/pty-output-coalescer.d.ts +0 -20
  195. package/dist/types/recovery-journal-protocol.d.ts +0 -35
  196. package/dist/types/recovery-journal-runtime.d.ts +0 -12
  197. package/dist/types/recovery-journal-stall-fixture.d.ts +0 -1
  198. package/dist/types/recovery-journal-thread.d.ts +0 -1
  199. package/dist/types/recovery-journal.d.ts +0 -248
  200. package/dist/types/recovery-output-window.d.ts +0 -19
  201. package/dist/types/recovery-protocol.d.ts +0 -59
  202. package/dist/types/recovery-store.d.ts +0 -52
  203. package/dist/types/registry-auth.d.ts +0 -47
  204. package/dist/types/repository-transition-policy.d.ts +0 -26
  205. package/dist/types/runtime-version.d.ts +0 -1
  206. package/dist/types/session-file-mutations.d.ts +0 -22
  207. package/dist/types/supervisor-daemonized-fixture.d.ts +0 -1
  208. package/dist/types/supervisor-launcher-cleanup-fixture.d.ts +0 -1
  209. package/dist/types/supervisor-signal-fixture.d.ts +0 -1
  210. package/dist/types/supervisor.d.ts +0 -23
  211. package/dist/types/three-way-merge.d.ts +0 -77
  212. package/dist/types/working-tree-mirror.d.ts +0 -424
  213. package/dist/types/workspace-automatic-sync-policy.d.ts +0 -57
  214. package/dist/types/workspace-branch-incarnation-policy.d.ts +0 -14
  215. package/dist/types/workspace-command-sync-policy.d.ts +0 -57
  216. package/dist/types/workspace-command-targets.d.ts +0 -37
  217. package/dist/types/workspace-filesystem-executor-thread.d.ts +0 -1
  218. package/dist/types/workspace-filesystem-executor.d.ts +0 -123
  219. package/dist/types/workspace-filesystem-job-types.d.ts +0 -302
  220. package/dist/types/workspace-filesystem-jobs.d.ts +0 -7
  221. package/dist/types/workspace-git-sync.d.ts +0 -514
  222. package/dist/types/workspace-hydration-ledger.d.ts +0 -76
  223. package/dist/types/workspace-hydration-merge.d.ts +0 -103
  224. package/dist/types/workspace-hydration-recovery-state.d.ts +0 -10
  225. package/dist/types/workspace-incident-state.d.ts +0 -19
  226. package/dist/types/workspace-merge-projection.d.ts +0 -69
  227. package/dist/types/workspace-mount-boundary.d.ts +0 -10
  228. package/dist/types/workspace-mount-hold-fence.d.ts +0 -42
  229. package/dist/types/workspace-mutation-gate.d.ts +0 -42
  230. package/dist/types/workspace-path-move.d.ts +0 -21
  231. package/dist/types/workspace-preserve-only-policy.d.ts +0 -15
  232. package/dist/types/workspace-project-config-policy.d.ts +0 -48
  233. package/dist/types/workspace-projection-ledger.d.ts +0 -59
  234. package/dist/types/workspace-publication-evidence.d.ts +0 -17
  235. package/dist/types/workspace-sync-coalescer.d.ts +0 -28
  236. /package/dist/types/{credential-authority-lock-fixture.d.ts → runtime/releases/main.d.ts} +0 -0
@@ -1,594 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- import { workerGitProcessEnvironment } from "./git-process-environment.mjs";
4
- import {
5
- inspectWorkingTree,
6
- projectedFileEntry
7
- } from "./working-tree-mirror.mjs";
8
- import { assertManagedDirectoryPath } from "./workspace-mount-boundary.mjs";
9
- const MINIMUM_MERGE_TREE_GIT_VERSION = { major: 2, minor: 40 };
10
- const NON_RECURSIVE_GIT_CONFIG = [
11
- "-c",
12
- "submodule.recurse=false",
13
- "-c",
14
- "fetch.recurseSubmodules=false",
15
- "-c",
16
- "push.recurseSubmodules=false"
17
- ];
18
- let cachedWorkspaceMergeProjectionSupport = null;
19
- function gitCommandArgs(args) {
20
- return ["git", ...NON_RECURSIVE_GIT_CONFIG, ...args];
21
- }
22
- function gitResult(cwd, args, options = {}) {
23
- const result = Bun.spawnSync(gitCommandArgs(args), {
24
- cwd,
25
- ...options.stdin !== void 0 ? { stdin: options.stdin } : {},
26
- stdout: "pipe",
27
- stderr: "pipe",
28
- env: options.environment ?? workerGitProcessEnvironment()
29
- });
30
- return { exitCode: result.exitCode, stdout: Buffer.from(result.stdout), stderr: Buffer.from(result.stderr) };
31
- }
32
- function git(cwd, args, action, options = {}) {
33
- const result = gitResult(cwd, args, options);
34
- if (result.exitCode !== 0) {
35
- const detail = result.stderr.toString().trim() || result.stdout.toString().trim() || `git exited ${result.exitCode}`;
36
- throw new Error(`${action}: ${detail}`);
37
- }
38
- return result.stdout;
39
- }
40
- function gitText(cwd, args, action, options = {}) {
41
- return git(cwd, args, action, options).toString().trim();
42
- }
43
- function gitVersionIsSupported(version) {
44
- const match = /(?:^|\s)(\d+)\.(\d+)(?:\.\d+)?(?:\s|$)/u.exec(version);
45
- if (!match) return false;
46
- const major = Number(match[1]);
47
- const minor = Number(match[2]);
48
- return major > MINIMUM_MERGE_TREE_GIT_VERSION.major || major === MINIMUM_MERGE_TREE_GIT_VERSION.major && minor >= MINIMUM_MERGE_TREE_GIT_VERSION.minor;
49
- }
50
- function workspaceMergeProjectionSupport() {
51
- if (cachedWorkspaceMergeProjectionSupport) return cachedWorkspaceMergeProjectionSupport;
52
- if (process.env.R5D_WORKSPACE_MERGE_PROJECTION === "0") {
53
- cachedWorkspaceMergeProjectionSupport = {
54
- supported: false,
55
- error: "Workspace merge projection is disabled by R5D_WORKSPACE_MERGE_PROJECTION=0; stale mounts will remain pinned until it is enabled"
56
- };
57
- return cachedWorkspaceMergeProjectionSupport;
58
- }
59
- const version = gitResult(void 0, ["--version"]);
60
- const versionText = version.stdout.toString().trim();
61
- if (version.exitCode !== 0 || !gitVersionIsSupported(versionText)) {
62
- const observed = versionText || version.stderr.toString().trim() || `git exited ${version.exitCode}`;
63
- cachedWorkspaceMergeProjectionSupport = {
64
- supported: false,
65
- error: `Workspace merge projection requires Git 2.40 or newer (found ${observed}); upgrade Git before synchronizing stale mounts`
66
- };
67
- return cachedWorkspaceMergeProjectionSupport;
68
- }
69
- cachedWorkspaceMergeProjectionSupport = { supported: true };
70
- return cachedWorkspaceMergeProjectionSupport;
71
- }
72
- function normalizeWorkspaceRelativePath(value) {
73
- const normalized = value.replace(/\\/g, "/").replace(/^\/+|\/+$/g, "");
74
- if (!normalized || normalized === "." || normalized.includes("\0") || normalized.split("/").some((segment) => segment === ".." || segment === ".git")) {
75
- throw new Error(`Invalid workspace merge-projection path: ${value}`);
76
- }
77
- return normalized;
78
- }
79
- function requireObjectId(value, label) {
80
- if (!/^(?:[0-9a-f]{40}|[0-9a-f]{64})$/u.test(value)) throw new Error(`${label} is not a Git object id: ${value}`);
81
- return value;
82
- }
83
- function requireCommit(workspacePath, revision, label) {
84
- const head = gitText(workspacePath, ["rev-parse", "--verify", `${revision}^{commit}`], `resolve ${label}`);
85
- return requireObjectId(head, label);
86
- }
87
- function workspacePrivateStateDirectory(workspacePath) {
88
- const gitDirectory = path.join(workspacePath, ".git");
89
- const gitStatus = fs.lstatSync(gitDirectory);
90
- if (!gitStatus.isDirectory() || gitStatus.isSymbolicLink()) {
91
- throw new Error(`Workspace Git directory is not a regular directory: ${gitDirectory}`);
92
- }
93
- const stateDirectory = path.join(gitDirectory, "r5d");
94
- let stateStatus = null;
95
- try {
96
- stateStatus = fs.lstatSync(stateDirectory);
97
- } catch (error) {
98
- if (error.code !== "ENOENT") throw error;
99
- }
100
- if (!stateStatus) fs.mkdirSync(stateDirectory, { mode: 448 });
101
- else if (!stateStatus.isDirectory() || stateStatus.isSymbolicLink()) {
102
- throw new Error(`Workspace Git private state is not a regular directory: ${stateDirectory}`);
103
- }
104
- return stateDirectory;
105
- }
106
- function temporaryIndexPath(workspacePath) {
107
- return path.join(workspacePrivateStateDirectory(workspacePath), `tmp-index-${crypto.randomUUID()}`);
108
- }
109
- function temporaryIndexEnvironment(indexPath) {
110
- return { ...workerGitProcessEnvironment(), GIT_INDEX_FILE: indexPath };
111
- }
112
- function removeTemporaryIndex(indexPath) {
113
- fs.rmSync(indexPath, { force: true });
114
- fs.rmSync(`${indexPath}.lock`, { force: true });
115
- }
116
- function sourceEntryPath(sourceRoot, relativePath) {
117
- const candidate = path.resolve(sourceRoot, ...relativePath.split("/"));
118
- const relative = path.relative(sourceRoot, candidate);
119
- if (relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
120
- throw new Error(`Workspace merge-projection source escapes its root: ${relativePath}`);
121
- }
122
- return candidate;
123
- }
124
- function assertSameOpenFile(before, after, sourcePath) {
125
- if (!after.isFile() || before.dev !== after.dev || before.ino !== after.ino || before.size !== after.size || before.mode !== after.mode || before.mtimeMs !== after.mtimeMs || before.ctimeMs !== after.ctimeMs) {
126
- throw new Error(`Working-tree source changed while its merge projection was being synthesized: ${sourcePath}`);
127
- }
128
- }
129
- function hashRegularFile(workspacePath, sourcePath, entry) {
130
- const descriptor = fs.openSync(sourcePath, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
131
- try {
132
- const before = fs.fstatSync(descriptor);
133
- if (!before.isFile() || before.size !== entry.size || (before.mode & 511) !== (entry.mode & 511)) {
134
- throw new Error(`Working-tree source changed while its merge projection was being synthesized: ${sourcePath}`);
135
- }
136
- const objectId = requireObjectId(
137
- gitText(workspacePath, ["hash-object", "-w", "--stdin"], `hash workspace projection file ${sourcePath}`, {
138
- stdin: descriptor
139
- }),
140
- `Workspace projection blob for ${sourcePath}`
141
- );
142
- const after = fs.fstatSync(descriptor);
143
- assertSameOpenFile(before, after, sourcePath);
144
- const pathStatus = fs.lstatSync(sourcePath);
145
- if (pathStatus.dev !== after.dev || pathStatus.ino !== after.ino || !pathStatus.isFile() || pathStatus.isSymbolicLink()) {
146
- throw new Error(`Working-tree source changed while its merge projection was being synthesized: ${sourcePath}`);
147
- }
148
- return { objectId, projected: projectedFileEntry(after) };
149
- } finally {
150
- fs.closeSync(descriptor);
151
- }
152
- }
153
- function hashSymlink(workspacePath, sourcePath, entry) {
154
- const status = fs.lstatSync(sourcePath);
155
- if (!status.isSymbolicLink() || fs.readlinkSync(sourcePath) !== entry.target) {
156
- throw new Error(`Working-tree source changed while its merge projection was being synthesized: ${sourcePath}`);
157
- }
158
- const objectId = requireObjectId(
159
- gitText(workspacePath, ["hash-object", "-w", "--stdin"], `hash workspace projection symlink ${sourcePath}`, {
160
- stdin: Buffer.from(entry.target)
161
- }),
162
- `Workspace projection symlink blob for ${sourcePath}`
163
- );
164
- if (!fs.lstatSync(sourcePath).isSymbolicLink() || fs.readlinkSync(sourcePath) !== entry.target) {
165
- throw new Error(`Working-tree source changed while its merge projection was being synthesized: ${sourcePath}`);
166
- }
167
- return objectId;
168
- }
169
- function indexMode(entry) {
170
- if (entry.kind === "symlink") return "120000";
171
- return (entry.mode & 73) !== 0 ? "100755" : "100644";
172
- }
173
- function indexInfoRecord(mode, objectId, relativePath) {
174
- return Buffer.concat([Buffer.from(`${mode} ${objectId} `), Buffer.from(relativePath), Buffer.from([0])]);
175
- }
176
- function synthesizeMountTree(input) {
177
- const sourceRoot = path.resolve(input.sourcePath);
178
- const entries = inspectWorkingTree(sourceRoot, input.sourceMode);
179
- const indexPath = temporaryIndexPath(input.workspacePath);
180
- const environment = temporaryIndexEnvironment(indexPath);
181
- const projectedFiles = {};
182
- const blobs = /* @__PURE__ */ new Map();
183
- try {
184
- git(input.workspacePath, ["read-tree", "--empty"], "initialize workspace projection index", { environment });
185
- const records = [];
186
- for (const [relativePath, entry] of [...entries.entries()].sort(([left], [right]) => left.localeCompare(right))) {
187
- if (entry.kind === "directory") continue;
188
- if (entry.kind === "gitlink") {
189
- records.push(indexInfoRecord("160000", entry.objectId, relativePath));
190
- projectedFiles[relativePath] = { kind: "gitlink", objectId: entry.objectId };
191
- continue;
192
- }
193
- const sourcePath = sourceEntryPath(sourceRoot, relativePath);
194
- let objectId;
195
- if (entry.kind === "file") {
196
- const hashed = hashRegularFile(input.workspacePath, sourcePath, entry);
197
- objectId = hashed.objectId;
198
- projectedFiles[relativePath] = hashed.projected;
199
- } else {
200
- objectId = hashSymlink(input.workspacePath, sourcePath, entry);
201
- projectedFiles[relativePath] = { kind: "symlink", target: entry.target };
202
- }
203
- blobs.set(relativePath, objectId);
204
- records.push(indexInfoRecord(indexMode(entry), objectId, relativePath));
205
- }
206
- if (records.length > 0) {
207
- git(input.workspacePath, ["update-index", "-z", "--index-info"], "populate workspace projection index", {
208
- environment,
209
- stdin: Buffer.concat(records)
210
- });
211
- }
212
- const tree = requireObjectId(
213
- gitText(input.workspacePath, ["write-tree"], "write workspace projection mount tree", { environment }),
214
- "Workspace projection mount tree"
215
- );
216
- return { tree, projectedFiles, blobs };
217
- } finally {
218
- removeTemporaryIndex(indexPath);
219
- }
220
- }
221
- function ledgerStatMatches(recorded, projected) {
222
- return recorded !== null && projected?.kind === "file" && projected.ino === recorded.ino && projected.size === recorded.size && projected.mode === recorded.mode && projected.mtimeMs === recorded.mtimeMs && projected.ctimeMs === recorded.ctimeMs;
223
- }
224
- function classifyHydrationLedger(input) {
225
- const entries = Object.entries(input.hydrationLedger ?? {});
226
- if (entries.length === 0) return { stale: [], merge: [], cleared: [] };
227
- const probed = gitText(input.workspacePath, ["cat-file", "--batch-check"], "resolve workspace projection basis subtree", {
228
- stdin: Buffer.from(`${input.basisHead}:${input.workspaceRelativePath}
229
- `)
230
- });
231
- const basisSubtree = /^([0-9a-f]{40,64}) tree \d+$/u.exec(probed);
232
- if (!basisSubtree && !/ missing$/u.test(probed))
233
- throw new Error(`Resolve workspace projection basis subtree: unexpected answer ${probed}`);
234
- const changed = /* @__PURE__ */ new Set();
235
- if (!basisSubtree) {
236
- for (const relativePath of input.blobs.keys()) changed.add(relativePath);
237
- } else {
238
- const listing = git(
239
- input.workspacePath,
240
- ["diff-tree", "-r", "-z", "--no-renames", "--raw", basisSubtree[1], input.mountTree],
241
- "compare workspace projection with its basis"
242
- );
243
- const records = nulRecords(listing);
244
- for (let index = 0; index + 1 < records.length; index += 2) changed.add(records[index + 1].toString());
245
- }
246
- const stale = [];
247
- const merge = [];
248
- const cleared = [];
249
- for (const [relativePath, entry] of entries) {
250
- const synthesized = input.blobs.get(relativePath);
251
- if (synthesized === void 0) {
252
- if (entry.postBlob === null) continue;
253
- cleared.push(relativePath);
254
- continue;
255
- }
256
- if (ledgerStatMatches(entry.stat, input.projectedFiles[relativePath]) || synthesized === entry.postBlob) continue;
257
- if (!changed.has(relativePath)) {
258
- cleared.push(relativePath);
259
- continue;
260
- }
261
- if (entry.processLiveAcrossHydration) {
262
- merge.push({ relativePath, preBlob: entry.preBlob, postBlob: entry.postBlob });
263
- continue;
264
- }
265
- if (synthesized === entry.preBlob) {
266
- stale.push(relativePath);
267
- continue;
268
- }
269
- cleared.push(relativePath);
270
- }
271
- if (merge.length > 0) {
272
- const ids = [...new Set(merge.flatMap(({ preBlob, postBlob }) => postBlob ? [preBlob, postBlob] : [preBlob]))];
273
- const checked = gitText(input.workspacePath, ["cat-file", "--batch-check"], "verify hydration ledger blobs", {
274
- stdin: Buffer.from(`${ids.join("\n")}
275
- `)
276
- });
277
- const missing = new Set(
278
- checked.split("\n").filter((line) => / missing$/u.test(line)).map((line) => line.split(" ")[0])
279
- );
280
- if (missing.size > 0) {
281
- for (let index = merge.length - 1; index >= 0; index -= 1) {
282
- const { relativePath, preBlob, postBlob } = merge[index];
283
- if (missing.has(preBlob) || postBlob !== null && missing.has(postBlob)) {
284
- merge.splice(index, 1);
285
- cleared.push(relativePath);
286
- }
287
- }
288
- }
289
- }
290
- return { stale: stale.sort(), merge: merge.sort((left, right) => left.relativePath.localeCompare(right.relativePath)), cleared: cleared.sort() };
291
- }
292
- function hydrationLedgerMergeBase(input) {
293
- if (input.merges.length === 0) return input.basisHead;
294
- return commitWithLedgerPaths({
295
- workspacePath: input.workspacePath,
296
- workspaceRelativePath: input.workspaceRelativePath,
297
- parent: input.basisHead,
298
- paths: input.merges.map(({ relativePath, preBlob }) => ({ relativePath, blob: preBlob })),
299
- message: JSON.stringify({
300
- type: "workspace_hydration_ledger_basis",
301
- mountId: input.mountId,
302
- attemptId: input.attemptId,
303
- paths: input.merges.map(({ relativePath }) => relativePath)
304
- }),
305
- action: "hydration ledger merge base"
306
- });
307
- }
308
- function hydrationLedgerTheirs(input) {
309
- const paths = input.merges.flatMap(({ relativePath, postBlob }) => postBlob ? [{ relativePath, blob: postBlob }] : []);
310
- if (paths.length === 0) return input.currentHead;
311
- return commitWithLedgerPaths({
312
- workspacePath: input.workspacePath,
313
- workspaceRelativePath: input.workspaceRelativePath,
314
- parent: input.currentHead,
315
- paths,
316
- message: JSON.stringify({
317
- type: "workspace_hydration_ledger_theirs",
318
- mountId: input.mountId,
319
- attemptId: input.attemptId,
320
- paths: paths.map(({ relativePath }) => relativePath)
321
- }),
322
- action: "hydration ledger theirs"
323
- });
324
- }
325
- function commitWithLedgerPaths(input) {
326
- const indexPath = temporaryIndexPath(input.workspacePath);
327
- const environment = temporaryIndexEnvironment(indexPath);
328
- try {
329
- git(input.workspacePath, ["read-tree", input.parent], `read workspace tree for the ${input.action}`, { environment });
330
- const paths = input.paths.map(({ relativePath }) => `${input.workspaceRelativePath}/${relativePath}`);
331
- const listed = git(
332
- input.workspacePath,
333
- ["ls-files", "-z", "--stage", "--", ...paths.map((entry) => `:(literal)${entry}`)],
334
- `inspect modes for the ${input.action}`,
335
- { environment }
336
- );
337
- const modes = /* @__PURE__ */ new Map();
338
- for (const record of nulRecords(listed)) {
339
- const match = /^([0-7]{6}) [0-9a-f]{40,64} [0-3]\t(.+)$/su.exec(record.toString());
340
- if (match) modes.set(match[2], match[1]);
341
- }
342
- const records = input.paths.map(
343
- ({ relativePath, blob }, index) => indexInfoRecord(modes.get(paths[index]) ?? "100644", requireObjectId(blob, `hydration ledger blob for ${relativePath}`), paths[index])
344
- );
345
- git(input.workspacePath, ["update-index", "-z", "--index-info"], `rewrite paths for the ${input.action}`, {
346
- environment,
347
- stdin: Buffer.concat(records)
348
- });
349
- const tree = requireObjectId(gitText(input.workspacePath, ["write-tree"], `write the ${input.action}`, { environment }), `${input.action} tree`);
350
- return requireObjectId(
351
- gitText(input.workspacePath, ["commit-tree", tree, "-p", input.parent, "-m", input.message], `commit the ${input.action}`),
352
- `${input.action} commit`
353
- );
354
- } finally {
355
- removeTemporaryIndex(indexPath);
356
- }
357
- }
358
- function nulRecords(content) {
359
- const records = [];
360
- let start = 0;
361
- for (let index = 0; index < content.length; index += 1) {
362
- if (content[index] !== 0) continue;
363
- if (index > start) records.push(content.subarray(start, index));
364
- start = index + 1;
365
- }
366
- if (start < content.length) records.push(content.subarray(start));
367
- return records;
368
- }
369
- function indexedSubtreePaths(content, workspaceRelativePath) {
370
- const prefix = Buffer.from(workspaceRelativePath);
371
- const descendantPrefix = Buffer.from(`${workspaceRelativePath}/`);
372
- const records = nulRecords(content).filter(
373
- (record) => record.equals(prefix) || record.length > descendantPrefix.length && record.subarray(0, descendantPrefix.length).equals(descendantPrefix)
374
- );
375
- return records.length > 0 ? Buffer.concat(records.flatMap((record) => [record, Buffer.from([0])])) : Buffer.alloc(0);
376
- }
377
- function graftMountTree(input) {
378
- const indexPath = temporaryIndexPath(input.workspacePath);
379
- const environment = temporaryIndexEnvironment(indexPath);
380
- try {
381
- git(input.workspacePath, ["read-tree", input.basisHead], "read workspace projection basis", { environment });
382
- const indexedPaths = git(input.workspacePath, ["ls-files", "-z"], "enumerate workspace projection basis", { environment });
383
- const removedPaths = indexedSubtreePaths(indexedPaths, input.workspaceRelativePath);
384
- if (removedPaths.length > 0) {
385
- git(input.workspacePath, ["update-index", "--force-remove", "-z", "--stdin"], "remove prior workspace projection subtree", {
386
- environment,
387
- stdin: removedPaths
388
- });
389
- }
390
- if (input.mountTree !== null) {
391
- git(
392
- input.workspacePath,
393
- ["read-tree", "-i", `--prefix=${input.workspaceRelativePath}/`, input.mountTree],
394
- "graft workspace projection mount tree",
395
- { environment }
396
- );
397
- }
398
- return requireObjectId(
399
- gitText(input.workspacePath, ["write-tree"], "write grafted workspace projection tree", { environment }),
400
- "Grafted workspace projection tree"
401
- );
402
- } finally {
403
- removeTemporaryIndex(indexPath);
404
- }
405
- }
406
- function mountSubtreeObjectId(workspacePath, revision, workspaceRelativePath, label) {
407
- const spec = `${revision}:${workspaceRelativePath}`;
408
- if (gitResult(workspacePath, ["cat-file", "-e", spec]).exitCode !== 0) return null;
409
- const type = gitText(workspacePath, ["cat-file", "-t", spec], `inspect ${label}`);
410
- if (type !== "tree") throw new Error(`${label} is not a directory at ${workspaceRelativePath}`);
411
- return requireObjectId(gitText(workspacePath, ["rev-parse", spec], `resolve ${label}`), label);
412
- }
413
- function synthesizeOursCommit(input) {
414
- const synthesized = synthesizeMountTree({
415
- workspacePath: input.workspacePath,
416
- sourcePath: input.mount.sourcePath,
417
- sourceMode: input.mount.sourceMode
418
- });
419
- const classified = classifyHydrationLedger({
420
- workspacePath: input.workspacePath,
421
- workspaceRelativePath: input.workspaceRelativePath,
422
- basisHead: input.basisHead,
423
- mountTree: synthesized.tree,
424
- blobs: synthesized.blobs,
425
- projectedFiles: synthesized.projectedFiles,
426
- hydrationLedger: input.hydrationLedger
427
- });
428
- if (classified.stale.length > 0) return { staleRewritePaths: classified.stale, staleRewriteCleared: classified.cleared };
429
- const mergeBase = hydrationLedgerMergeBase({
430
- workspacePath: input.workspacePath,
431
- workspaceRelativePath: input.workspaceRelativePath,
432
- basisHead: input.basisHead,
433
- merges: classified.merge,
434
- mountId: input.mount.id,
435
- attemptId: input.attemptId
436
- });
437
- const rootTree = graftMountTree({
438
- workspacePath: input.workspacePath,
439
- workspaceRelativePath: input.workspaceRelativePath,
440
- basisHead: mergeBase,
441
- mountTree: synthesized.tree
442
- });
443
- const message = JSON.stringify({
444
- type: "workspace_projection_basis",
445
- mountId: input.mount.id,
446
- attemptId: input.attemptId
447
- });
448
- const oursCommit = requireObjectId(
449
- gitText(
450
- input.workspacePath,
451
- ["commit-tree", rootTree, "-p", mergeBase, "-m", message],
452
- `commit synthesized workspace projection for mount ${input.mount.id}`
453
- ),
454
- `Synthesized workspace projection commit for mount ${input.mount.id}`
455
- );
456
- return {
457
- oursCommit,
458
- mergeBase,
459
- projectedFiles: synthesized.projectedFiles,
460
- staleRewriteCleared: classified.cleared,
461
- ledgerMerges: classified.merge
462
- };
463
- }
464
- function mergeWorkspaceProjectionMount(input) {
465
- const support = workspaceMergeProjectionSupport();
466
- if (!support.supported) throw new Error(support.error);
467
- const workspacePath = path.resolve(input.workspacePath);
468
- const workspaceRelativePath = normalizeWorkspaceRelativePath(input.mount.workspaceRelativePath);
469
- const basisHead = requireCommit(workspacePath, input.basisHead, `workspace projection basis for mount ${input.mount.id}`);
470
- const currentHead = requireCommit(workspacePath, input.currentHead, "current workspace projection head");
471
- const synthesized = synthesizeOursCommit({
472
- workspacePath,
473
- mount: input.mount,
474
- workspaceRelativePath,
475
- basisHead,
476
- attemptId: input.attemptId,
477
- ...input.hydrationLedger ? { hydrationLedger: input.hydrationLedger } : {}
478
- });
479
- if ("staleRewritePaths" in synthesized) {
480
- return { kind: "stale_rewrite", paths: synthesized.staleRewritePaths, staleRewriteCleared: synthesized.staleRewriteCleared };
481
- }
482
- const { oursCommit, mergeBase, projectedFiles, staleRewriteCleared, ledgerMerges } = synthesized;
483
- const ledgerMergedPaths = ledgerMerges.map(({ relativePath }) => relativePath);
484
- const theirsHead = hydrationLedgerTheirs({
485
- workspacePath,
486
- workspaceRelativePath,
487
- currentHead,
488
- merges: ledgerMerges,
489
- mountId: input.mount.id,
490
- attemptId: input.attemptId
491
- });
492
- const readAtMs = Date.now();
493
- const inboundMountTree = mountSubtreeObjectId(
494
- workspacePath,
495
- theirsHead,
496
- workspaceRelativePath,
497
- `current workspace head subtree for mount ${input.mount.id}`
498
- );
499
- const inboundCommit = requireObjectId(
500
- gitText(
501
- workspacePath,
502
- [
503
- "commit-tree",
504
- graftMountTree({ workspacePath, workspaceRelativePath, basisHead: mergeBase, mountTree: inboundMountTree }),
505
- "-p",
506
- mergeBase,
507
- "-m",
508
- JSON.stringify({ type: "workspace_projection_inbound", mountId: input.mount.id, attemptId: input.attemptId })
509
- ],
510
- `commit inbound workspace projection for mount ${input.mount.id}`
511
- ),
512
- `Inbound workspace projection commit for mount ${input.mount.id}`
513
- );
514
- const merged = gitResult(workspacePath, [
515
- "merge-tree",
516
- "--write-tree",
517
- `--merge-base=${mergeBase}`,
518
- "--name-only",
519
- "-z",
520
- "--no-messages",
521
- oursCommit,
522
- inboundCommit
523
- ]);
524
- if (merged.exitCode !== 0 && merged.exitCode !== 1) {
525
- const detail = merged.stderr.toString().trim() || merged.stdout.toString().trim() || `git exited ${merged.exitCode}`;
526
- throw new Error(`Merge workspace projection for mount ${input.mount.id}: ${detail}`);
527
- }
528
- const records = nulRecords(merged.stdout);
529
- const mergedTree = records.shift()?.toString() ?? "";
530
- requireObjectId(mergedTree, `Merged workspace projection tree for mount ${input.mount.id}`);
531
- if (merged.exitCode === 0) {
532
- const resultTree = graftMountTree({
533
- workspacePath,
534
- workspaceRelativePath,
535
- basisHead: currentHead,
536
- mountTree: mountSubtreeObjectId(workspacePath, mergedTree, workspaceRelativePath, `merged subtree for mount ${input.mount.id}`)
537
- });
538
- return { kind: "clean", resultTree, oursCommit, projectedFiles, readAtMs, staleRewriteCleared, ledgerMergedPaths };
539
- }
540
- const conflictPaths = records.map((record) => record.toString()).sort();
541
- return {
542
- kind: "conflict",
543
- oursCommit,
544
- conflictPaths,
545
- error: `Workspace projection for mount ${input.mount.id} conflicted with the current workspace head`,
546
- projectedFiles,
547
- readAtMs,
548
- staleRewriteCleared,
549
- ledgerMergedPaths
550
- };
551
- }
552
- function materializeWorkspaceProjectionTree(input) {
553
- const workspacePath = path.resolve(input.workspacePath);
554
- const workspaceRelativePath = normalizeWorkspaceRelativePath(input.workspaceRelativePath);
555
- const resultTree = requireObjectId(input.resultTree, "Workspace projection result tree");
556
- const targetPath = path.join(workspacePath, ...workspaceRelativePath.split("/"));
557
- assertManagedDirectoryPath({
558
- trustedRoot: workspacePath,
559
- candidate: targetPath,
560
- label: "Workspace merge-projection target"
561
- });
562
- git(workspacePath, ["cat-file", "-e", `${resultTree}^{tree}`], "resolve workspace merge-projection result tree");
563
- const subtree = gitResult(workspacePath, ["cat-file", "-e", `${resultTree}:${workspaceRelativePath}`]);
564
- if (subtree.exitCode !== 0) {
565
- fs.rmSync(targetPath, { recursive: true, force: true });
566
- return;
567
- }
568
- const subtreeType = gitText(
569
- workspacePath,
570
- ["cat-file", "-t", `${resultTree}:${workspaceRelativePath}`],
571
- "inspect workspace merge-projection result subtree"
572
- );
573
- if (subtreeType !== "tree") {
574
- throw new Error(`Workspace merge-projection result is not a directory at ${workspaceRelativePath}`);
575
- }
576
- fs.rmSync(targetPath, { recursive: true, force: true });
577
- git(
578
- workspacePath,
579
- ["checkout", "--no-recurse-submodules", resultTree, "--", `:(literal)${workspaceRelativePath}`],
580
- "materialize merged workspace projection subtree"
581
- );
582
- }
583
- const workspaceMergeProjectionTestHarness = {
584
- gitVersionIsSupported,
585
- resetSupportProbe() {
586
- cachedWorkspaceMergeProjectionSupport = null;
587
- }
588
- };
589
- export {
590
- materializeWorkspaceProjectionTree,
591
- mergeWorkspaceProjectionMount,
592
- workspaceMergeProjectionSupport,
593
- workspaceMergeProjectionTestHarness
594
- };
@@ -1,37 +0,0 @@
1
- import fs from "node:fs";
2
- import path from "node:path";
3
- function containedRelative(root, candidate) {
4
- const relative = path.relative(root, candidate);
5
- if (!relative || relative === ".." || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
6
- throw new Error(`Managed directory path escapes its trusted root: ${candidate}`);
7
- }
8
- return relative;
9
- }
10
- function assertManagedDirectoryPath(input) {
11
- const trustedRoot = path.resolve(input.trustedRoot);
12
- const candidate = path.resolve(input.candidate);
13
- const relative = containedRelative(trustedRoot, candidate);
14
- const rootStatus = fs.lstatSync(trustedRoot);
15
- if (!rootStatus.isDirectory() || rootStatus.isSymbolicLink()) {
16
- throw new Error(`${input.label} trusted root is not a regular directory: ${trustedRoot}`);
17
- }
18
- const realRoot = fs.realpathSync(trustedRoot);
19
- let current = trustedRoot;
20
- for (const segment of relative.split(path.sep)) {
21
- current = path.join(current, segment);
22
- let status;
23
- try {
24
- status = fs.lstatSync(current);
25
- } catch (error) {
26
- if (error.code === "ENOENT") return;
27
- throw error;
28
- }
29
- if (!status.isDirectory() || status.isSymbolicLink()) {
30
- throw new Error(`${input.label} contains a symlink or non-directory component: ${current}`);
31
- }
32
- containedRelative(realRoot, fs.realpathSync(current));
33
- }
34
- }
35
- export {
36
- assertManagedDirectoryPath
37
- };