@ricsam/r5d-worker 0.0.136 → 0.0.138

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 (240) hide show
  1. package/README.md +4 -94
  2. package/dist/cjs/package.json +1 -1
  3. package/dist/mjs/main.mjs +46 -7537
  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 +328 -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 +17 -0
  41. package/dist/types/runtime/daemon.d.ts +8 -0
  42. package/dist/types/runtime/executor.d.ts +35 -0
  43. package/dist/types/runtime/index.d.ts +5 -0
  44. package/dist/types/runtime/protocol.d.ts +544 -0
  45. package/dist/types/runtime/pty.d.ts +30 -0
  46. package/dist/types/runtime/releases/artifacts.d.ts +51 -0
  47. package/dist/types/runtime/releases/grants.d.ts +16 -0
  48. package/dist/types/runtime/releases/manager.d.ts +145 -0
  49. package/dist/types/runtime/releases/process.d.ts +14 -0
  50. package/dist/types/runtime/releases/retirement.d.ts +15 -0
  51. package/dist/types/runtime/releases/rpc-client.d.ts +52 -0
  52. package/dist/types/runtime/releases/rpc-main.d.ts +10 -0
  53. package/dist/types/runtime/releases/rpc-protocol.d.ts +1139 -0
  54. package/dist/types/runtime/storage.d.ts +21 -0
  55. package/dist/types/runtime/test-harness.d.ts +53 -0
  56. package/dist/types/runtime/workspace/artifacts.d.ts +30 -0
  57. package/dist/types/runtime/workspace/authority.d.ts +338 -0
  58. package/dist/types/runtime/workspace/contracts.d.ts +86 -0
  59. package/dist/types/runtime/workspace/file-write.d.ts +14 -0
  60. package/dist/types/runtime/workspace/files.d.ts +21 -0
  61. package/dist/types/runtime/workspace/http-body.d.ts +9 -0
  62. package/dist/types/runtime/workspace/storage-client.d.ts +23 -0
  63. package/dist/types/runtime/workspace/storage-wire.d.ts +138 -0
  64. package/package.json +8 -6
  65. package/dist/cjs/atomic-rename.cjs +0 -303
  66. package/dist/cjs/browser-upload.cjs +0 -73
  67. package/dist/cjs/cli-update.cjs +0 -108
  68. package/dist/cjs/command-launcher.cjs +0 -423
  69. package/dist/cjs/control-command-policy.cjs +0 -177
  70. package/dist/cjs/git-blob-hash.cjs +0 -41
  71. package/dist/cjs/git-identity.cjs +0 -39
  72. package/dist/cjs/git-process-environment.cjs +0 -133
  73. package/dist/cjs/inline-output.cjs +0 -62
  74. package/dist/cjs/main.cjs +0 -7511
  75. package/dist/cjs/managed-paths.cjs +0 -215
  76. package/dist/cjs/plan-lint.cjs +0 -165
  77. package/dist/cjs/plan-parser.cjs +0 -127
  78. package/dist/cjs/plan-paths.cjs +0 -53
  79. package/dist/cjs/port-forward-client.cjs +0 -206
  80. package/dist/cjs/process-tree.cjs +0 -117
  81. package/dist/cjs/project-checkout-garbage.cjs +0 -219
  82. package/dist/cjs/project-mirror-fetch-policy.cjs +0 -82
  83. package/dist/cjs/project-mirror-refs-token.cjs +0 -85
  84. package/dist/cjs/project-workspace-state.cjs +0 -972
  85. package/dist/cjs/project-worktrees.cjs +0 -1713
  86. package/dist/cjs/pty-output-coalescer.cjs +0 -87
  87. package/dist/cjs/recovery-journal-protocol.cjs +0 -56
  88. package/dist/cjs/recovery-journal-runtime.cjs +0 -133
  89. package/dist/cjs/recovery-journal-thread.cjs +0 -9
  90. package/dist/cjs/recovery-journal.cjs +0 -742
  91. package/dist/cjs/recovery-output-window.cjs +0 -77
  92. package/dist/cjs/recovery-protocol.cjs +0 -31
  93. package/dist/cjs/recovery-store.cjs +0 -252
  94. package/dist/cjs/registry-auth.cjs +0 -310
  95. package/dist/cjs/repository-transition-policy.cjs +0 -49
  96. package/dist/cjs/runtime-version.cjs +0 -88
  97. package/dist/cjs/session-file-mutations.cjs +0 -61
  98. package/dist/cjs/supervisor.cjs +0 -147
  99. package/dist/cjs/three-way-merge.cjs +0 -346
  100. package/dist/cjs/working-tree-mirror.cjs +0 -1872
  101. package/dist/cjs/workspace-automatic-sync-policy.cjs +0 -79
  102. package/dist/cjs/workspace-branch-incarnation-policy.cjs +0 -37
  103. package/dist/cjs/workspace-command-sync-policy.cjs +0 -112
  104. package/dist/cjs/workspace-command-targets.cjs +0 -63
  105. package/dist/cjs/workspace-filesystem-executor-thread.cjs +0 -36
  106. package/dist/cjs/workspace-filesystem-executor.cjs +0 -327
  107. package/dist/cjs/workspace-filesystem-job-types.cjs +0 -144
  108. package/dist/cjs/workspace-filesystem-jobs.cjs +0 -59
  109. package/dist/cjs/workspace-git-sync.cjs +0 -3450
  110. package/dist/cjs/workspace-hydration-ledger.cjs +0 -66
  111. package/dist/cjs/workspace-hydration-merge.cjs +0 -433
  112. package/dist/cjs/workspace-hydration-recovery-state.cjs +0 -53
  113. package/dist/cjs/workspace-incident-state.cjs +0 -37
  114. package/dist/cjs/workspace-merge-projection.cjs +0 -467
  115. package/dist/cjs/workspace-mount-boundary.cjs +0 -71
  116. package/dist/cjs/workspace-mount-hold-fence.cjs +0 -120
  117. package/dist/cjs/workspace-mutation-gate.cjs +0 -127
  118. package/dist/cjs/workspace-path-move.cjs +0 -195
  119. package/dist/cjs/workspace-preserve-only-policy.cjs +0 -36
  120. package/dist/cjs/workspace-project-config-policy.cjs +0 -53
  121. package/dist/cjs/workspace-projection-ledger.cjs +0 -81
  122. package/dist/cjs/workspace-publication-evidence.cjs +0 -46
  123. package/dist/cjs/workspace-sync-coalescer.cjs +0 -58
  124. package/dist/mjs/atomic-rename.mjs +0 -261
  125. package/dist/mjs/browser-upload.mjs +0 -42
  126. package/dist/mjs/command-launcher.mjs +0 -393
  127. package/dist/mjs/control-command-policy.mjs +0 -146
  128. package/dist/mjs/git-blob-hash.mjs +0 -16
  129. package/dist/mjs/git-identity.mjs +0 -15
  130. package/dist/mjs/git-process-environment.mjs +0 -105
  131. package/dist/mjs/inline-output.mjs +0 -37
  132. package/dist/mjs/managed-paths.mjs +0 -174
  133. package/dist/mjs/plan-lint.mjs +0 -129
  134. package/dist/mjs/plan-parser.mjs +0 -94
  135. package/dist/mjs/plan-paths.mjs +0 -25
  136. package/dist/mjs/port-forward-client.mjs +0 -172
  137. package/dist/mjs/project-checkout-garbage.mjs +0 -178
  138. package/dist/mjs/project-mirror-fetch-policy.mjs +0 -56
  139. package/dist/mjs/project-mirror-refs-token.mjs +0 -53
  140. package/dist/mjs/project-workspace-state.mjs +0 -937
  141. package/dist/mjs/project-worktrees.mjs +0 -1657
  142. package/dist/mjs/pty-output-coalescer.mjs +0 -61
  143. package/dist/mjs/recovery-journal-protocol.mjs +0 -30
  144. package/dist/mjs/recovery-journal-runtime.mjs +0 -112
  145. package/dist/mjs/recovery-journal-thread.mjs +0 -8
  146. package/dist/mjs/recovery-journal.mjs +0 -694
  147. package/dist/mjs/recovery-output-window.mjs +0 -53
  148. package/dist/mjs/recovery-protocol.mjs +0 -6
  149. package/dist/mjs/recovery-store.mjs +0 -217
  150. package/dist/mjs/registry-auth.mjs +0 -269
  151. package/dist/mjs/repository-transition-policy.mjs +0 -22
  152. package/dist/mjs/runtime-version.mjs +0 -54
  153. package/dist/mjs/session-file-mutations.mjs +0 -37
  154. package/dist/mjs/supervisor.mjs +0 -114
  155. package/dist/mjs/three-way-merge.mjs +0 -318
  156. package/dist/mjs/working-tree-mirror.mjs +0 -1817
  157. package/dist/mjs/workspace-automatic-sync-policy.mjs +0 -48
  158. package/dist/mjs/workspace-branch-incarnation-policy.mjs +0 -13
  159. package/dist/mjs/workspace-command-sync-policy.mjs +0 -84
  160. package/dist/mjs/workspace-command-targets.mjs +0 -37
  161. package/dist/mjs/workspace-filesystem-executor-thread.mjs +0 -38
  162. package/dist/mjs/workspace-filesystem-executor.mjs +0 -287
  163. package/dist/mjs/workspace-filesystem-job-types.mjs +0 -116
  164. package/dist/mjs/workspace-filesystem-jobs.mjs +0 -55
  165. package/dist/mjs/workspace-git-sync.mjs +0 -3407
  166. package/dist/mjs/workspace-hydration-ledger.mjs +0 -42
  167. package/dist/mjs/workspace-hydration-merge.mjs +0 -399
  168. package/dist/mjs/workspace-hydration-recovery-state.mjs +0 -29
  169. package/dist/mjs/workspace-incident-state.mjs +0 -12
  170. package/dist/mjs/workspace-merge-projection.mjs +0 -433
  171. package/dist/mjs/workspace-mount-boundary.mjs +0 -37
  172. package/dist/mjs/workspace-mount-hold-fence.mjs +0 -95
  173. package/dist/mjs/workspace-mutation-gate.mjs +0 -103
  174. package/dist/mjs/workspace-path-move.mjs +0 -160
  175. package/dist/mjs/workspace-preserve-only-policy.mjs +0 -11
  176. package/dist/mjs/workspace-project-config-policy.mjs +0 -27
  177. package/dist/mjs/workspace-projection-ledger.mjs +0 -57
  178. package/dist/mjs/workspace-publication-evidence.mjs +0 -21
  179. package/dist/mjs/workspace-sync-coalescer.mjs +0 -34
  180. package/dist/types/atomic-rename.d.ts +0 -78
  181. package/dist/types/browser-upload.d.ts +0 -7
  182. package/dist/types/command-launcher.d.ts +0 -104
  183. package/dist/types/control-command-policy.d.ts +0 -53
  184. package/dist/types/git-blob-hash.d.ts +0 -10
  185. package/dist/types/git-identity.d.ts +0 -9
  186. package/dist/types/git-process-environment.d.ts +0 -9
  187. package/dist/types/inline-output.d.ts +0 -3
  188. package/dist/types/managed-paths.d.ts +0 -19
  189. package/dist/types/plan-lint.d.ts +0 -16
  190. package/dist/types/plan-parser.d.ts +0 -38
  191. package/dist/types/plan-paths.d.ts +0 -36
  192. package/dist/types/port-forward-client.d.ts +0 -8
  193. package/dist/types/project-checkout-garbage.d.ts +0 -86
  194. package/dist/types/project-mirror-fetch-policy.d.ts +0 -84
  195. package/dist/types/project-mirror-refs-token.d.ts +0 -53
  196. package/dist/types/project-workspace-state.d.ts +0 -188
  197. package/dist/types/project-worktrees.d.ts +0 -397
  198. package/dist/types/pty-output-coalescer.d.ts +0 -20
  199. package/dist/types/recovery-journal-protocol.d.ts +0 -35
  200. package/dist/types/recovery-journal-runtime.d.ts +0 -12
  201. package/dist/types/recovery-journal-thread.d.ts +0 -1
  202. package/dist/types/recovery-journal.d.ts +0 -248
  203. package/dist/types/recovery-output-window.d.ts +0 -19
  204. package/dist/types/recovery-protocol.d.ts +0 -59
  205. package/dist/types/recovery-store.d.ts +0 -52
  206. package/dist/types/registry-auth.d.ts +0 -47
  207. package/dist/types/repository-transition-policy.d.ts +0 -26
  208. package/dist/types/runtime-version.d.ts +0 -1
  209. package/dist/types/session-file-mutations.d.ts +0 -22
  210. package/dist/types/supervisor-daemonized-fixture.d.ts +0 -1
  211. package/dist/types/supervisor-launcher-cleanup-fixture.d.ts +0 -1
  212. package/dist/types/supervisor-signal-fixture.d.ts +0 -1
  213. package/dist/types/supervisor.d.ts +0 -23
  214. package/dist/types/three-way-merge.d.ts +0 -77
  215. package/dist/types/working-tree-mirror.d.ts +0 -424
  216. package/dist/types/workspace-automatic-sync-policy.d.ts +0 -57
  217. package/dist/types/workspace-branch-incarnation-policy.d.ts +0 -14
  218. package/dist/types/workspace-command-sync-policy.d.ts +0 -57
  219. package/dist/types/workspace-command-targets.d.ts +0 -37
  220. package/dist/types/workspace-filesystem-executor-thread.d.ts +0 -1
  221. package/dist/types/workspace-filesystem-executor.d.ts +0 -123
  222. package/dist/types/workspace-filesystem-job-types.d.ts +0 -288
  223. package/dist/types/workspace-filesystem-jobs.d.ts +0 -7
  224. package/dist/types/workspace-git-sync.d.ts +0 -495
  225. package/dist/types/workspace-hydration-ledger.d.ts +0 -43
  226. package/dist/types/workspace-hydration-merge.d.ts +0 -95
  227. package/dist/types/workspace-hydration-recovery-state.d.ts +0 -10
  228. package/dist/types/workspace-incident-state.d.ts +0 -19
  229. package/dist/types/workspace-merge-projection.d.ts +0 -60
  230. package/dist/types/workspace-mount-boundary.d.ts +0 -10
  231. package/dist/types/workspace-mount-hold-fence.d.ts +0 -42
  232. package/dist/types/workspace-mutation-gate.d.ts +0 -42
  233. package/dist/types/workspace-path-move.d.ts +0 -21
  234. package/dist/types/workspace-preserve-only-policy.d.ts +0 -15
  235. package/dist/types/workspace-project-config-policy.d.ts +0 -48
  236. package/dist/types/workspace-projection-ledger.d.ts +0 -59
  237. package/dist/types/workspace-publication-evidence.d.ts +0 -17
  238. package/dist/types/workspace-sync-coalescer.d.ts +0 -28
  239. /package/dist/types/{credential-authority-lock-fixture.d.ts → runtime/adapter.d.ts} +0 -0
  240. /package/dist/types/{recovery-journal-stall-fixture.d.ts → runtime/releases/main.d.ts} +0 -0
@@ -1,433 +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 staleRewritePaths(input) {
222
- const entries = Object.entries(input.staleRewriteBlobs ?? {});
223
- if (entries.length === 0) return { stale: [], cleared: [] };
224
- const probed = gitText(input.workspacePath, ["cat-file", "--batch-check"], "resolve workspace projection basis subtree", {
225
- stdin: Buffer.from(`${input.basisHead}:${input.workspaceRelativePath}
226
- `)
227
- });
228
- const basisSubtree = /^([0-9a-f]{40,64}) tree \d+$/u.exec(probed);
229
- if (!basisSubtree && !/ missing$/u.test(probed))
230
- throw new Error(`Resolve workspace projection basis subtree: unexpected answer ${probed}`);
231
- const changed = /* @__PURE__ */ new Set();
232
- if (!basisSubtree) {
233
- for (const relativePath of input.blobs.keys()) changed.add(relativePath);
234
- } else {
235
- const listing = git(
236
- input.workspacePath,
237
- ["diff-tree", "-r", "-z", "--no-renames", "--raw", basisSubtree[1], input.mountTree],
238
- "compare workspace projection with its basis"
239
- );
240
- const records = nulRecords(listing);
241
- for (let index = 0; index + 1 < records.length; index += 2) changed.add(records[index + 1].toString());
242
- }
243
- const stale = [];
244
- const cleared = [];
245
- for (const [relativePath, blob] of entries) {
246
- const synthesized = input.blobs.get(relativePath);
247
- if (synthesized === void 0) {
248
- cleared.push(relativePath);
249
- continue;
250
- }
251
- if (synthesized === blob) {
252
- if (changed.has(relativePath)) stale.push(relativePath);
253
- else cleared.push(relativePath);
254
- continue;
255
- }
256
- if (changed.has(relativePath)) cleared.push(relativePath);
257
- }
258
- return { stale: stale.sort(), cleared: cleared.sort() };
259
- }
260
- function nulRecords(content) {
261
- const records = [];
262
- let start = 0;
263
- for (let index = 0; index < content.length; index += 1) {
264
- if (content[index] !== 0) continue;
265
- if (index > start) records.push(content.subarray(start, index));
266
- start = index + 1;
267
- }
268
- if (start < content.length) records.push(content.subarray(start));
269
- return records;
270
- }
271
- function indexedSubtreePaths(content, workspaceRelativePath) {
272
- const prefix = Buffer.from(workspaceRelativePath);
273
- const descendantPrefix = Buffer.from(`${workspaceRelativePath}/`);
274
- const records = nulRecords(content).filter(
275
- (record) => record.equals(prefix) || record.length > descendantPrefix.length && record.subarray(0, descendantPrefix.length).equals(descendantPrefix)
276
- );
277
- return records.length > 0 ? Buffer.concat(records.flatMap((record) => [record, Buffer.from([0])])) : Buffer.alloc(0);
278
- }
279
- function graftMountTree(input) {
280
- const indexPath = temporaryIndexPath(input.workspacePath);
281
- const environment = temporaryIndexEnvironment(indexPath);
282
- try {
283
- git(input.workspacePath, ["read-tree", input.basisHead], "read workspace projection basis", { environment });
284
- const indexedPaths = git(input.workspacePath, ["ls-files", "-z"], "enumerate workspace projection basis", { environment });
285
- const removedPaths = indexedSubtreePaths(indexedPaths, input.workspaceRelativePath);
286
- if (removedPaths.length > 0) {
287
- git(input.workspacePath, ["update-index", "--force-remove", "-z", "--stdin"], "remove prior workspace projection subtree", {
288
- environment,
289
- stdin: removedPaths
290
- });
291
- }
292
- git(
293
- input.workspacePath,
294
- ["read-tree", "-i", `--prefix=${input.workspaceRelativePath}/`, input.mountTree],
295
- "graft workspace projection mount tree",
296
- { environment }
297
- );
298
- return requireObjectId(
299
- gitText(input.workspacePath, ["write-tree"], "write grafted workspace projection tree", { environment }),
300
- "Grafted workspace projection tree"
301
- );
302
- } finally {
303
- removeTemporaryIndex(indexPath);
304
- }
305
- }
306
- function synthesizeOursCommit(input) {
307
- const synthesized = synthesizeMountTree({
308
- workspacePath: input.workspacePath,
309
- sourcePath: input.mount.sourcePath,
310
- sourceMode: input.mount.sourceMode
311
- });
312
- const detected = staleRewritePaths({
313
- workspacePath: input.workspacePath,
314
- workspaceRelativePath: input.workspaceRelativePath,
315
- basisHead: input.basisHead,
316
- mountTree: synthesized.tree,
317
- blobs: synthesized.blobs,
318
- staleRewriteBlobs: input.staleRewriteBlobs
319
- });
320
- if (detected.stale.length > 0) return { staleRewritePaths: detected.stale, staleRewriteCleared: detected.cleared };
321
- const rootTree = graftMountTree({
322
- workspacePath: input.workspacePath,
323
- workspaceRelativePath: input.workspaceRelativePath,
324
- basisHead: input.basisHead,
325
- mountTree: synthesized.tree
326
- });
327
- const message = JSON.stringify({
328
- type: "workspace_projection_basis",
329
- mountId: input.mount.id,
330
- attemptId: input.attemptId
331
- });
332
- const oursCommit = requireObjectId(
333
- gitText(
334
- input.workspacePath,
335
- ["commit-tree", rootTree, "-p", input.basisHead, "-m", message],
336
- `commit synthesized workspace projection for mount ${input.mount.id}`
337
- ),
338
- `Synthesized workspace projection commit for mount ${input.mount.id}`
339
- );
340
- return { oursCommit, projectedFiles: synthesized.projectedFiles, staleRewriteCleared: detected.cleared };
341
- }
342
- function mergeWorkspaceProjectionMount(input) {
343
- const support = workspaceMergeProjectionSupport();
344
- if (!support.supported) throw new Error(support.error);
345
- const workspacePath = path.resolve(input.workspacePath);
346
- const workspaceRelativePath = normalizeWorkspaceRelativePath(input.mount.workspaceRelativePath);
347
- const basisHead = requireCommit(workspacePath, input.basisHead, `workspace projection basis for mount ${input.mount.id}`);
348
- const currentHead = requireCommit(workspacePath, input.currentHead, "current workspace projection head");
349
- const synthesized = synthesizeOursCommit({
350
- workspacePath,
351
- mount: input.mount,
352
- workspaceRelativePath,
353
- basisHead,
354
- attemptId: input.attemptId,
355
- ...input.staleRewriteBlobs ? { staleRewriteBlobs: input.staleRewriteBlobs } : {}
356
- });
357
- if ("staleRewritePaths" in synthesized) {
358
- return { kind: "stale_rewrite", paths: synthesized.staleRewritePaths, staleRewriteCleared: synthesized.staleRewriteCleared };
359
- }
360
- const { oursCommit, projectedFiles, staleRewriteCleared } = synthesized;
361
- const readAtMs = Date.now();
362
- const merged = gitResult(workspacePath, [
363
- "merge-tree",
364
- "--write-tree",
365
- `--merge-base=${basisHead}`,
366
- "--name-only",
367
- "-z",
368
- "--no-messages",
369
- oursCommit,
370
- currentHead
371
- ]);
372
- if (merged.exitCode !== 0 && merged.exitCode !== 1) {
373
- const detail = merged.stderr.toString().trim() || merged.stdout.toString().trim() || `git exited ${merged.exitCode}`;
374
- throw new Error(`Merge workspace projection for mount ${input.mount.id}: ${detail}`);
375
- }
376
- const records = nulRecords(merged.stdout);
377
- const resultTree = records.shift()?.toString() ?? "";
378
- requireObjectId(resultTree, `Merged workspace projection tree for mount ${input.mount.id}`);
379
- if (merged.exitCode === 0) return { kind: "clean", resultTree, oursCommit, projectedFiles, readAtMs, staleRewriteCleared };
380
- const conflictPaths = records.map((record) => record.toString()).sort();
381
- return {
382
- kind: "conflict",
383
- oursCommit,
384
- conflictPaths,
385
- error: `Workspace projection for mount ${input.mount.id} conflicted with the current workspace head`,
386
- projectedFiles,
387
- readAtMs,
388
- staleRewriteCleared
389
- };
390
- }
391
- function materializeWorkspaceProjectionTree(input) {
392
- const workspacePath = path.resolve(input.workspacePath);
393
- const workspaceRelativePath = normalizeWorkspaceRelativePath(input.workspaceRelativePath);
394
- const resultTree = requireObjectId(input.resultTree, "Workspace projection result tree");
395
- const targetPath = path.join(workspacePath, ...workspaceRelativePath.split("/"));
396
- assertManagedDirectoryPath({
397
- trustedRoot: workspacePath,
398
- candidate: targetPath,
399
- label: "Workspace merge-projection target"
400
- });
401
- git(workspacePath, ["cat-file", "-e", `${resultTree}^{tree}`], "resolve workspace merge-projection result tree");
402
- const subtree = gitResult(workspacePath, ["cat-file", "-e", `${resultTree}:${workspaceRelativePath}`]);
403
- if (subtree.exitCode !== 0) {
404
- fs.rmSync(targetPath, { recursive: true, force: true });
405
- return;
406
- }
407
- const subtreeType = gitText(
408
- workspacePath,
409
- ["cat-file", "-t", `${resultTree}:${workspaceRelativePath}`],
410
- "inspect workspace merge-projection result subtree"
411
- );
412
- if (subtreeType !== "tree") {
413
- throw new Error(`Workspace merge-projection result is not a directory at ${workspaceRelativePath}`);
414
- }
415
- fs.rmSync(targetPath, { recursive: true, force: true });
416
- git(
417
- workspacePath,
418
- ["checkout", "--no-recurse-submodules", resultTree, "--", `:(literal)${workspaceRelativePath}`],
419
- "materialize merged workspace projection subtree"
420
- );
421
- }
422
- const workspaceMergeProjectionTestHarness = {
423
- gitVersionIsSupported,
424
- resetSupportProbe() {
425
- cachedWorkspaceMergeProjectionSupport = null;
426
- }
427
- };
428
- export {
429
- materializeWorkspaceProjectionTree,
430
- mergeWorkspaceProjectionMount,
431
- workspaceMergeProjectionSupport,
432
- workspaceMergeProjectionTestHarness
433
- };
@@ -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
- };
@@ -1,95 +0,0 @@
1
- class WorkspaceMountHoldAbortedError extends Error {
2
- constructor(reason) {
3
- super(reason);
4
- this.name = "WorkspaceMountHoldAbortedError";
5
- }
6
- }
7
- class WorkspaceMountHoldFence {
8
- holds = /* @__PURE__ */ new Map();
9
- waiters = /* @__PURE__ */ new Set();
10
- now;
11
- constructor(options = {}) {
12
- this.now = options.now ?? (() => Date.now());
13
- }
14
- /** Take one hold on every listed mount; the returned release is idempotent. */
15
- hold(mountIds, description) {
16
- const ids = [...new Set(mountIds)];
17
- for (const id of ids) {
18
- const existing = this.holds.get(id);
19
- if (existing) {
20
- existing.count += 1;
21
- continue;
22
- }
23
- this.holds.set(id, { count: 1, description, sinceMs: this.now() });
24
- }
25
- let released = false;
26
- return () => {
27
- if (released) return;
28
- released = true;
29
- for (const id of ids) {
30
- const existing = this.holds.get(id);
31
- if (!existing) continue;
32
- existing.count -= 1;
33
- if (existing.count === 0) this.holds.delete(id);
34
- }
35
- this.wake();
36
- };
37
- }
38
- isHeld(mountIds) {
39
- return mountIds.some((id) => this.holds.has(id));
40
- }
41
- /** Human-readable holder of the first held mount among `mountIds`, for diagnostics. */
42
- describeHold(mountIds) {
43
- for (const id of mountIds) {
44
- const hold = this.holds.get(id);
45
- if (hold) return `${hold.description} (held for ${Math.round((this.now() - hold.sinceMs) / 1e3)}s)`;
46
- }
47
- return null;
48
- }
49
- /** Every held mount id, sorted, for status reporting. */
50
- heldMountIds() {
51
- return [...this.holds.keys()].sort();
52
- }
53
- /**
54
- * Resolves in the same turn as the release that leaves none of `mountIds`
55
- * held, so the caller can re-check and reserve without an interleaving
56
- * hold. Resolves immediately when nothing is held. Rejects with
57
- * WorkspaceMountHoldAbortedError when `signal` aborts first.
58
- */
59
- waitForRelease(mountIds, options = {}) {
60
- if (options.signal?.aborted) {
61
- return Promise.reject(new WorkspaceMountHoldAbortedError(abortReason(options.signal)));
62
- }
63
- if (!this.isHeld(mountIds)) return Promise.resolve();
64
- return new Promise((resolve, reject) => {
65
- const waiter = { mountIds, resolve, reject, signal: options.signal };
66
- if (options.signal) {
67
- const signal = options.signal;
68
- waiter.onAbort = () => {
69
- this.waiters.delete(waiter);
70
- reject(new WorkspaceMountHoldAbortedError(abortReason(signal)));
71
- };
72
- signal.addEventListener("abort", waiter.onAbort, { once: true });
73
- }
74
- this.waiters.add(waiter);
75
- });
76
- }
77
- wake() {
78
- for (const waiter of [...this.waiters]) {
79
- if (this.isHeld(waiter.mountIds)) continue;
80
- this.waiters.delete(waiter);
81
- if (waiter.signal && waiter.onAbort) waiter.signal.removeEventListener("abort", waiter.onAbort);
82
- waiter.resolve();
83
- }
84
- }
85
- }
86
- function abortReason(signal) {
87
- const reason = signal.reason;
88
- if (reason instanceof Error) return reason.message;
89
- if (typeof reason === "string" && reason) return reason;
90
- return "Workspace mount reservation was aborted";
91
- }
92
- export {
93
- WorkspaceMountHoldAbortedError,
94
- WorkspaceMountHoldFence
95
- };
@@ -1,103 +0,0 @@
1
- class WorkspaceMutationGate {
2
- activeMutations = 0;
3
- syncActive = false;
4
- waiters = [];
5
- acquireMutation() {
6
- return new Promise((resolve) => {
7
- this.waiters.push({ kind: "mutation", resolve });
8
- this.drain();
9
- });
10
- }
11
- async runMutation(operation) {
12
- const release = await this.acquireMutation();
13
- try {
14
- return await operation();
15
- } finally {
16
- release();
17
- }
18
- }
19
- async runSync(operation) {
20
- const release = await new Promise((resolve) => {
21
- this.waiters.push({ kind: "sync", resolve });
22
- this.drain();
23
- });
24
- try {
25
- return await operation();
26
- } finally {
27
- release();
28
- }
29
- }
30
- /** A worktree create/delete lease; see the branch lane in the class comment. */
31
- acquireBranchMutation() {
32
- return new Promise((resolve) => {
33
- this.waiters.push({ kind: "branch", resolve });
34
- this.drain();
35
- });
36
- }
37
- async runBranchMutation(operation) {
38
- const release = await this.acquireBranchMutation();
39
- try {
40
- return await operation();
41
- } finally {
42
- release();
43
- }
44
- }
45
- /**
46
- * Operations a branch mutation requested now would wait for: the running
47
- * sync, if any, plus branch mutations queued behind it. Zero means it runs
48
- * at once. Ordinary waiters are not counted because the branch lane admits
49
- * ahead of them.
50
- */
51
- branchMutationQueuePosition() {
52
- if (!this.syncActive) return 0;
53
- return 1 + this.waiters.filter((waiter) => waiter.kind === "branch").length;
54
- }
55
- drain() {
56
- if (this.syncActive || this.waiters.length === 0) return;
57
- for (let index = 0; index < this.waiters.length; ) {
58
- const waiter = this.waiters[index];
59
- if (waiter?.kind !== "branch") {
60
- index += 1;
61
- continue;
62
- }
63
- this.waiters.splice(index, 1);
64
- this.activeMutations += 1;
65
- waiter.resolve(this.releaseMutation());
66
- }
67
- const first = this.waiters[0];
68
- if (first?.kind === "sync") {
69
- if (this.activeMutations > 0) return;
70
- this.waiters.shift();
71
- this.syncActive = true;
72
- first.resolve(this.releaseSync());
73
- return;
74
- }
75
- while (this.waiters[0]?.kind === "mutation") {
76
- const waiter = this.waiters.shift();
77
- if (!waiter || waiter.kind !== "mutation") break;
78
- this.activeMutations += 1;
79
- waiter.resolve(this.releaseMutation());
80
- }
81
- }
82
- releaseMutation() {
83
- let released = false;
84
- return () => {
85
- if (released) return;
86
- released = true;
87
- this.activeMutations -= 1;
88
- this.drain();
89
- };
90
- }
91
- releaseSync() {
92
- let released = false;
93
- return () => {
94
- if (released) return;
95
- released = true;
96
- this.syncActive = false;
97
- this.drain();
98
- };
99
- }
100
- }
101
- export {
102
- WorkspaceMutationGate
103
- };