@openclaw/fs-safe 0.14.0 → 0.15.0

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 (235) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/README.md +10 -4
  3. package/dist/advanced.d.ts +1 -1
  4. package/dist/advanced.d.ts.map +1 -1
  5. package/dist/advanced.js +1 -1
  6. package/dist/archive-merge.d.ts.map +1 -1
  7. package/dist/archive-merge.js +113 -46
  8. package/dist/archive-read.d.ts.map +1 -1
  9. package/dist/archive-read.js +4 -4
  10. package/dist/archive-zip-directory.d.ts +4 -0
  11. package/dist/archive-zip-directory.d.ts.map +1 -1
  12. package/dist/archive-zip-directory.js +2 -0
  13. package/dist/archive-zip-entry.d.ts +6 -2
  14. package/dist/archive-zip-entry.d.ts.map +1 -1
  15. package/dist/archive-zip-entry.js +23 -8
  16. package/dist/archive-zip-integrity.d.ts.map +1 -1
  17. package/dist/archive-zip-integrity.js +3 -4
  18. package/dist/archive-zip-loader.d.ts.map +1 -1
  19. package/dist/archive-zip-loader.js +107 -31
  20. package/dist/archive-zip-names.d.ts +1 -0
  21. package/dist/archive-zip-names.d.ts.map +1 -1
  22. package/dist/archive-zip-names.js +6 -0
  23. package/dist/archive.js +6 -5
  24. package/dist/bounded-read-stream.d.ts +0 -1
  25. package/dist/bounded-read-stream.d.ts.map +1 -1
  26. package/dist/bounded-read-stream.js +0 -6
  27. package/dist/copy-publication.d.ts +6 -0
  28. package/dist/copy-publication.d.ts.map +1 -1
  29. package/dist/copy-publication.js +3 -0
  30. package/dist/copy-tree-portable.d.ts.map +1 -1
  31. package/dist/copy-tree-portable.js +44 -24
  32. package/dist/copy.d.ts.map +1 -1
  33. package/dist/copy.js +29 -11
  34. package/dist/directory-mode-owner.js +5 -5
  35. package/dist/file-handle-transfer.d.ts +2 -0
  36. package/dist/file-handle-transfer.d.ts.map +1 -1
  37. package/dist/file-handle-transfer.js +57 -2
  38. package/dist/file-identity.d.ts.map +1 -1
  39. package/dist/file-identity.js +18 -4
  40. package/dist/file-lock-sync-admission.d.ts +19 -0
  41. package/dist/file-lock-sync-admission.d.ts.map +1 -0
  42. package/dist/file-lock-sync-admission.js +93 -0
  43. package/dist/file-lock-sync-root-acquire.d.ts +4 -0
  44. package/dist/file-lock-sync-root-acquire.d.ts.map +1 -0
  45. package/dist/file-lock-sync-root-acquire.js +370 -0
  46. package/dist/file-lock-sync-root-arbitration.d.ts +18 -0
  47. package/dist/file-lock-sync-root-arbitration.d.ts.map +1 -0
  48. package/dist/file-lock-sync-root-arbitration.js +66 -0
  49. package/dist/file-lock-sync-root-held.d.ts +34 -0
  50. package/dist/file-lock-sync-root-held.d.ts.map +1 -0
  51. package/dist/file-lock-sync-root-held.js +393 -0
  52. package/dist/file-lock-sync-root-io.d.ts +44 -0
  53. package/dist/file-lock-sync-root-io.d.ts.map +1 -0
  54. package/dist/file-lock-sync-root-io.js +209 -0
  55. package/dist/file-lock-sync-root-mutation.d.ts +17 -0
  56. package/dist/file-lock-sync-root-mutation.d.ts.map +1 -0
  57. package/dist/file-lock-sync-root-mutation.js +277 -0
  58. package/dist/file-lock-sync-root-options.d.ts +20 -0
  59. package/dist/file-lock-sync-root-options.d.ts.map +1 -0
  60. package/dist/file-lock-sync-root-options.js +58 -0
  61. package/dist/file-lock-sync-root-registration.d.ts +2 -0
  62. package/dist/file-lock-sync-root-registration.d.ts.map +1 -0
  63. package/dist/file-lock-sync-root-registration.js +90 -0
  64. package/dist/file-lock-sync-root.d.ts +36 -0
  65. package/dist/file-lock-sync-root.d.ts.map +1 -0
  66. package/dist/file-lock-sync-root.js +361 -0
  67. package/dist/file-lock-sync-stale-admission.d.ts +24 -0
  68. package/dist/file-lock-sync-stale-admission.d.ts.map +1 -0
  69. package/dist/file-lock-sync-stale-admission.js +205 -0
  70. package/dist/file-lock-sync.d.ts.map +1 -1
  71. package/dist/file-lock-sync.js +245 -205
  72. package/dist/file-store-prune.d.ts.map +1 -1
  73. package/dist/file-store-prune.js +5 -1
  74. package/dist/file-store-sync-write.js +3 -3
  75. package/dist/file-store.d.ts.map +1 -1
  76. package/dist/file-store.js +47 -12
  77. package/dist/json-document-store.d.ts.map +1 -1
  78. package/dist/json-document-store.js +22 -15
  79. package/dist/json-durable-queue-ownership.d.ts +0 -1
  80. package/dist/json-durable-queue-ownership.d.ts.map +1 -1
  81. package/dist/json-durable-queue-ownership.js +0 -6
  82. package/dist/native-binding.d.ts +2 -0
  83. package/dist/native-binding.d.ts.map +1 -1
  84. package/dist/native-parent-admission.d.ts +3 -2
  85. package/dist/native-parent-admission.d.ts.map +1 -1
  86. package/dist/native-parent-admission.js +24 -5
  87. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  88. package/dist/native-pinned-write-windows.js +7 -7
  89. package/dist/native-pinned-write.d.ts.map +1 -1
  90. package/dist/native-pinned-write.js +7 -11
  91. package/dist/native-policy-parent-windows.d.ts +14 -0
  92. package/dist/native-policy-parent-windows.d.ts.map +1 -0
  93. package/dist/native-policy-parent-windows.js +200 -0
  94. package/dist/native-rename-outcome.d.ts +4 -0
  95. package/dist/native-rename-outcome.d.ts.map +1 -0
  96. package/dist/native-rename-outcome.js +8 -0
  97. package/dist/native-staged-file.d.ts +2 -2
  98. package/dist/native-staged-file.d.ts.map +1 -1
  99. package/dist/native-staged-file.js +42 -40
  100. package/dist/output.d.ts.map +1 -1
  101. package/dist/output.js +12 -8
  102. package/dist/path-prefix.d.ts.map +1 -1
  103. package/dist/path-prefix.js +30 -8
  104. package/dist/path-suffix-aliases.d.ts +2 -0
  105. package/dist/path-suffix-aliases.d.ts.map +1 -1
  106. package/dist/path-suffix-aliases.js +25 -17
  107. package/dist/permission-exec.d.ts +2 -0
  108. package/dist/permission-exec.d.ts.map +1 -1
  109. package/dist/permission-exec.js +150 -21
  110. package/dist/permissions-windows.js +1 -1
  111. package/dist/pinned-mutation-admission.d.ts.map +1 -1
  112. package/dist/pinned-mutation-admission.js +10 -5
  113. package/dist/pinned-mutation-observation.d.ts +0 -1
  114. package/dist/pinned-mutation-observation.d.ts.map +1 -1
  115. package/dist/pinned-mutation-observation.js +0 -19
  116. package/dist/pinned-mutation-shared-route.d.ts +1 -0
  117. package/dist/pinned-mutation-shared-route.d.ts.map +1 -1
  118. package/dist/pinned-mutation-shared-route.js +1 -1
  119. package/dist/pinned-write-types.d.ts +2 -0
  120. package/dist/pinned-write-types.d.ts.map +1 -1
  121. package/dist/private-temp-workspace.d.ts.map +1 -1
  122. package/dist/private-temp-workspace.js +75 -121
  123. package/dist/regular-file.d.ts.map +1 -1
  124. package/dist/regular-file.js +1 -15
  125. package/dist/replace-directory.d.ts.map +1 -1
  126. package/dist/replace-directory.js +256 -18
  127. package/dist/replace-file-copy-fallback.d.ts.map +1 -1
  128. package/dist/replace-file-copy-fallback.js +62 -70
  129. package/dist/replace-file-copy-source.d.ts.map +1 -1
  130. package/dist/replace-file-copy-source.js +10 -12
  131. package/dist/replace-file-temp-owner.d.ts +5 -2
  132. package/dist/replace-file-temp-owner.d.ts.map +1 -1
  133. package/dist/replace-file-temp-owner.js +65 -30
  134. package/dist/replace-file.js +6 -6
  135. package/dist/retained-directory-replacement.d.ts +26 -0
  136. package/dist/retained-directory-replacement.d.ts.map +1 -0
  137. package/dist/retained-directory-replacement.js +193 -0
  138. package/dist/root-boundary.d.ts +1 -0
  139. package/dist/root-boundary.d.ts.map +1 -1
  140. package/dist/root-boundary.js +4 -0
  141. package/dist/root-context.d.ts +0 -8
  142. package/dist/root-context.d.ts.map +1 -1
  143. package/dist/root-context.js +0 -3
  144. package/dist/root-create-input.d.ts +6 -0
  145. package/dist/root-create-input.d.ts.map +1 -1
  146. package/dist/root-create-input.js +5 -1
  147. package/dist/root-directory-list.d.ts +1 -0
  148. package/dist/root-directory-list.d.ts.map +1 -1
  149. package/dist/root-directory-list.js +1 -0
  150. package/dist/root-impl.d.ts.map +1 -1
  151. package/dist/root-impl.js +18 -10
  152. package/dist/root-move-noreplace.d.ts.map +1 -1
  153. package/dist/root-move-noreplace.js +2 -2
  154. package/dist/root-path-errors.d.ts +1 -0
  155. package/dist/root-path-errors.d.ts.map +1 -1
  156. package/dist/root-path-errors.js +11 -2
  157. package/dist/root-path-existing.d.ts.map +1 -1
  158. package/dist/root-path-existing.js +11 -35
  159. package/dist/root-path.js +1 -13
  160. package/dist/root-remove.d.ts +1 -0
  161. package/dist/root-remove.d.ts.map +1 -1
  162. package/dist/root-remove.js +4 -0
  163. package/dist/root-walk.d.ts +1 -1
  164. package/dist/root-walk.d.ts.map +1 -1
  165. package/dist/root-walk.js +17 -2
  166. package/dist/root-write-admission.d.ts +0 -2
  167. package/dist/root-write-admission.d.ts.map +1 -1
  168. package/dist/root-write-admission.js +1 -15
  169. package/dist/root-write-complete-parent.d.ts.map +1 -1
  170. package/dist/root-write-complete-parent.js +7 -23
  171. package/dist/root-write-verification.d.ts.map +1 -1
  172. package/dist/root-write-verification.js +29 -42
  173. package/dist/secret-file.d.ts.map +1 -1
  174. package/dist/secret-file.js +2 -24
  175. package/dist/secret-read-async.d.ts.map +1 -1
  176. package/dist/secret-read-async.js +3 -24
  177. package/dist/secret-read-policy.d.ts +6 -2
  178. package/dist/secret-read-policy.d.ts.map +1 -1
  179. package/dist/secret-read-policy.js +26 -2
  180. package/dist/sibling-temp.d.ts.map +1 -1
  181. package/dist/sibling-temp.js +23 -12
  182. package/dist/sidecar-lock-acquire.d.ts +2 -28
  183. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  184. package/dist/sidecar-lock-acquire.js +288 -199
  185. package/dist/sidecar-lock-admission-context.d.ts +19 -0
  186. package/dist/sidecar-lock-admission-context.d.ts.map +1 -0
  187. package/dist/sidecar-lock-admission-context.js +60 -0
  188. package/dist/sidecar-lock-admission-parser.d.ts +43 -0
  189. package/dist/sidecar-lock-admission-parser.d.ts.map +1 -0
  190. package/dist/sidecar-lock-admission-parser.js +113 -0
  191. package/dist/sidecar-lock-admission.d.ts +35 -0
  192. package/dist/sidecar-lock-admission.d.ts.map +1 -0
  193. package/dist/sidecar-lock-admission.js +7 -0
  194. package/dist/sidecar-lock-reclaim.d.ts +9 -4
  195. package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
  196. package/dist/sidecar-lock-reclaim.js +80 -25
  197. package/dist/sidecar-lock-stale-admission.d.ts +39 -0
  198. package/dist/sidecar-lock-stale-admission.d.ts.map +1 -0
  199. package/dist/sidecar-lock-stale-admission.js +232 -0
  200. package/dist/sidecar-lock-target.d.ts +8 -0
  201. package/dist/sidecar-lock-target.d.ts.map +1 -0
  202. package/dist/sidecar-lock-target.js +55 -0
  203. package/dist/sidecar-lock.d.ts.map +1 -1
  204. package/dist/sidecar-lock.js +100 -16
  205. package/dist/temp-workspace-descriptor.d.ts.map +1 -1
  206. package/dist/temp-workspace-descriptor.js +9 -27
  207. package/dist/temp-workspace-owner.d.ts.map +1 -1
  208. package/dist/temp-workspace-owner.js +8 -8
  209. package/dist/walk.d.ts +5 -1
  210. package/dist/walk.d.ts.map +1 -1
  211. package/dist/walk.js +19 -6
  212. package/dist/windows-owner.d.ts.map +1 -1
  213. package/dist/windows-owner.js +2 -2
  214. package/docs/advanced.md +1 -1
  215. package/docs/archive.md +36 -9
  216. package/docs/atomic.md +85 -8
  217. package/docs/copy.md +35 -0
  218. package/docs/file-store.md +19 -0
  219. package/docs/json-store.md +5 -0
  220. package/docs/native-helper.md +10 -3
  221. package/docs/output.md +6 -0
  222. package/docs/path-prefix.md +10 -0
  223. package/docs/path-suffix-aliases.md +51 -6
  224. package/docs/permissions.md +13 -0
  225. package/docs/public-api.md +3 -2
  226. package/docs/root.md +22 -3
  227. package/docs/sidecar-lock.md +109 -4
  228. package/docs/staged-file.md +7 -3
  229. package/docs/temp.md +20 -3
  230. package/docs/walk.md +67 -1
  231. package/docs/writing.md +5 -1
  232. package/package.json +10 -10
  233. package/dist/darwin-acl.d.ts +0 -4
  234. package/dist/darwin-acl.d.ts.map +0 -1
  235. package/dist/darwin-acl.js +0 -24
@@ -0,0 +1,232 @@
1
+ import { defaultSidecarLockShouldReclaim, isTransientLockFileDenial } from "./sidecar-lock-policy.js";
2
+ import { readSidecarLockRawSnapshot, removeStaleSidecarLockIfAllowed, sidecarLockSnapshotStillPresent, tryAcquireSidecarReclaimGuard, } from "./sidecar-lock-reclaim.js";
3
+ import { observeSidecarLockParser, parseSidecarLockSnapshotUnderAdmission, scopedSidecarLockParser, } from "./sidecar-lock-admission-parser.js";
4
+ /** Handle one historical EEXIST attempt while its canonical admission is held. */
5
+ export async function handleStaleSidecarAdmission(params) {
6
+ const { admission, lockPath, normalizedTargetPath, options } = params;
7
+ const holderChanged = {};
8
+ const assertUnheld = () => {
9
+ params.assertToken();
10
+ if (params.currentHeld())
11
+ throw holderChanged;
12
+ };
13
+ const retryHolderChange = async (error) => {
14
+ if (error !== holderChanged)
15
+ return false;
16
+ await params.waitForRetry();
17
+ return true;
18
+ };
19
+ const retryChangedBoundary = async () => {
20
+ try {
21
+ assertUnheld();
22
+ return false;
23
+ }
24
+ catch (error) {
25
+ if (await retryHolderChange(error))
26
+ return true;
27
+ throw error;
28
+ }
29
+ };
30
+ let rawSnapshot;
31
+ let lockFileOpenDenied = false;
32
+ try {
33
+ rawSnapshot = await admission.run(async () => await readSidecarLockRawSnapshot(lockPath, {
34
+ lockRoot: params.lockRoot,
35
+ rejectNonFile: true,
36
+ discardObservation: "changed",
37
+ onOpenFailure: (error) => {
38
+ lockFileOpenDenied = isTransientLockFileDenial(error, lockPath);
39
+ },
40
+ }));
41
+ }
42
+ catch (error) {
43
+ if (await retryChangedBoundary())
44
+ return;
45
+ if (!lockFileOpenDenied || !params.withinDenialBudget())
46
+ throw error;
47
+ await params.retryOrRethrowDenial(error);
48
+ return;
49
+ }
50
+ params.assertToken();
51
+ if (params.currentHeld()) {
52
+ await params.waitForRetry();
53
+ return;
54
+ }
55
+ let snapshot;
56
+ try {
57
+ observeSidecarLockParser(params.parserState, admission, () => options.parsePayload, assertUnheld);
58
+ snapshot = parseSidecarLockSnapshotUnderAdmission(rawSnapshot, params.parserState, admission, assertUnheld);
59
+ assertUnheld();
60
+ }
61
+ catch (error) {
62
+ if (await retryHolderChange(error))
63
+ return;
64
+ if (await retryChangedBoundary())
65
+ return;
66
+ throw error;
67
+ }
68
+ if (!snapshot) {
69
+ await params.waitForRetry();
70
+ return;
71
+ }
72
+ const staleOptions = params.staleOptionsState;
73
+ try {
74
+ if (!staleOptions.shouldReclaimObserved) {
75
+ assertUnheld();
76
+ staleOptions.shouldReclaim = admission.run(() => options.shouldReclaim);
77
+ staleOptions.shouldReclaimObserved = true;
78
+ assertUnheld();
79
+ }
80
+ if (!staleOptions.shouldRemoveObserved) {
81
+ assertUnheld();
82
+ staleOptions.shouldRemove = admission.run(() => options.shouldRemoveStaleLock);
83
+ staleOptions.shouldRemoveObserved = true;
84
+ assertUnheld();
85
+ }
86
+ if (!staleOptions.staleRecoveryObserved) {
87
+ assertUnheld();
88
+ staleOptions.staleRecovery = admission.run(() => options.staleRecovery);
89
+ staleOptions.staleRecoveryObserved = true;
90
+ assertUnheld();
91
+ }
92
+ }
93
+ catch (error) {
94
+ if (await retryHolderChange(error))
95
+ return;
96
+ if (await retryChangedBoundary())
97
+ return;
98
+ throw error;
99
+ }
100
+ const shouldReclaim = staleOptions.shouldReclaim ?? defaultSidecarLockShouldReclaim;
101
+ let reclaim;
102
+ try {
103
+ reclaim = await admission.run(async () => {
104
+ assertUnheld();
105
+ try {
106
+ return await Reflect.apply(shouldReclaim, undefined, [{
107
+ lockPath,
108
+ normalizedTargetPath,
109
+ payload: snapshot.payload ?? null,
110
+ staleMs: params.staleMs,
111
+ nowMs: Date.now(),
112
+ heldByThisProcess: false,
113
+ }]);
114
+ }
115
+ finally {
116
+ assertUnheld();
117
+ }
118
+ });
119
+ assertUnheld();
120
+ }
121
+ catch (error) {
122
+ if (await retryHolderChange(error))
123
+ return;
124
+ if (await retryChangedBoundary())
125
+ return;
126
+ throw error;
127
+ }
128
+ if (!reclaim) {
129
+ await params.waitForRetry();
130
+ return;
131
+ }
132
+ let stillPresent;
133
+ try {
134
+ stillPresent = await sidecarLockSnapshotStillPresent(lockPath, snapshot, {
135
+ lockRoot: params.lockRoot,
136
+ parsePayload: scopedSidecarLockParser(params.parserState, admission, assertUnheld),
137
+ });
138
+ assertUnheld();
139
+ }
140
+ catch (error) {
141
+ if (await retryHolderChange(error))
142
+ return;
143
+ if (await retryChangedBoundary())
144
+ return;
145
+ throw error;
146
+ }
147
+ if (!stillPresent) {
148
+ await params.waitForRetry();
149
+ return;
150
+ }
151
+ if ((staleOptions.staleRecovery ?? "fail-closed") === "remove-if-unchanged") {
152
+ const acquiredGuard = await admission.run(() => tryAcquireSidecarReclaimGuard(params.reclaimGuards, params.reclaimGuardPath, params.lockRoot));
153
+ if (acquiredGuard)
154
+ params.setReclaimGuard(acquiredGuard);
155
+ params.assertToken();
156
+ if (params.currentHeld()) {
157
+ if (acquiredGuard) {
158
+ await params.releaseReclaimGuard();
159
+ }
160
+ await params.waitForRetry();
161
+ return;
162
+ }
163
+ if (!acquiredGuard) {
164
+ await params.waitForRetry();
165
+ return;
166
+ }
167
+ const shouldRemove = staleOptions.shouldRemove;
168
+ const assertRemovalAuthorized = assertUnheld;
169
+ const guardedShouldRemove = shouldRemove && function (staleSnapshot) {
170
+ return admission.run(async () => {
171
+ assertRemovalAuthorized();
172
+ try {
173
+ return await Reflect.apply(shouldRemove, this, [staleSnapshot]);
174
+ }
175
+ finally {
176
+ assertRemovalAuthorized();
177
+ }
178
+ });
179
+ };
180
+ let removal;
181
+ try {
182
+ removal = await removeStaleSidecarLockIfAllowed({
183
+ lockPath,
184
+ normalizedTargetPath,
185
+ snapshot,
186
+ assertGuardHeld: acquiredGuard.assertHeld && (() => admission.run(async () => {
187
+ assertUnheld();
188
+ await acquiredGuard.assertHeld();
189
+ assertUnheld();
190
+ })),
191
+ shouldRemoveStaleLock: guardedShouldRemove,
192
+ lockRoot: params.lockRoot,
193
+ parsePayload: scopedSidecarLockParser(params.parserState, admission, assertRemovalAuthorized),
194
+ assertAuthorized: assertRemovalAuthorized,
195
+ });
196
+ }
197
+ catch (error) {
198
+ if (error !== holderChanged) {
199
+ try {
200
+ assertRemovalAuthorized();
201
+ }
202
+ catch (boundaryError) {
203
+ if (boundaryError !== holderChanged)
204
+ throw boundaryError;
205
+ }
206
+ if (!params.currentHeld())
207
+ throw error;
208
+ }
209
+ await params.releaseReclaimGuard();
210
+ await params.waitForRetry();
211
+ return;
212
+ }
213
+ params.assertToken();
214
+ if (params.currentHeld()) {
215
+ await params.releaseReclaimGuard();
216
+ await params.waitForRetry();
217
+ return;
218
+ }
219
+ if (removal === "removed")
220
+ return;
221
+ if (removal === "changed") {
222
+ await params.waitForRetry();
223
+ return;
224
+ }
225
+ await params.releaseReclaimGuard();
226
+ }
227
+ throw Object.assign(new Error(`file lock stale for ${normalizedTargetPath}`), {
228
+ code: "file_lock_stale",
229
+ lockPath,
230
+ normalizedTargetPath,
231
+ });
232
+ }
@@ -0,0 +1,8 @@
1
+ import type { Root } from "./root-impl.js";
2
+ export declare function isCwdIndependentAbsolutePath(filePath: string): boolean;
3
+ export declare function resolveSidecarTargetPath(resolved: string, lockRoot?: Root): Promise<string>;
4
+ export declare function resolveSidecarLockPaths(targetPath: string, explicitLockPath: string | undefined, lockRoot: Root | undefined): Promise<{
5
+ lockPath: string;
6
+ normalizedTargetPath: string;
7
+ }>;
8
+ //# sourceMappingURL=sidecar-lock-target.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidecar-lock-target.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-target.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAG3C,wBAAgB,4BAA4B,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAItE;AAED,wBAAsB,wBAAwB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAwBjG;AAED,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,gBAAgB,EAAE,MAAM,GAAG,SAAS,EACpC,QAAQ,EAAE,IAAI,GAAG,SAAS,GACzB,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,oBAAoB,EAAE,MAAM,CAAA;CAAE,CAAC,CAgB7D"}
@@ -0,0 +1,55 @@
1
+ import fs from "node:fs/promises";
2
+ import path from "node:path";
3
+ import { canonicalPathFromExistingAncestor } from "./absolute-path.js";
4
+ import { realpathSync } from "./realpath.js";
5
+ import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
6
+ import { anchorWindowsDriveRelativePath, assertNoWindowsPathAlias } from "./windows-path-alias.js";
7
+ export function isCwdIndependentAbsolutePath(filePath) {
8
+ if (!path.isAbsolute(filePath))
9
+ return false;
10
+ // A leading separator on Windows is rooted on the process's current drive.
11
+ return process.platform !== "win32" || path.parse(filePath).root.length > 1;
12
+ }
13
+ export async function resolveSidecarTargetPath(resolved, lockRoot) {
14
+ assertNoWindowsPathAlias(resolved);
15
+ const dir = path.dirname(resolved);
16
+ if (lockRoot) {
17
+ // The target is an arbitration key, not necessarily inside the lock Root.
18
+ await lockRoot.resolve(".");
19
+ const parent = await canonicalPathFromExistingAncestor(dir);
20
+ await lockRoot.resolve(".");
21
+ assertNoWindowsPathAlias(parent);
22
+ const normalized = path.join(parent, path.basename(resolved));
23
+ assertNoWindowsPathAlias(normalized);
24
+ return normalized;
25
+ }
26
+ await fs.mkdir(recursiveMkdirPath(dir), { recursive: true });
27
+ let parent;
28
+ try {
29
+ parent = realpathSync.native(dir);
30
+ }
31
+ catch {
32
+ return resolved;
33
+ }
34
+ assertNoWindowsPathAlias(parent);
35
+ const normalized = path.join(parent, path.basename(resolved));
36
+ assertNoWindowsPathAlias(normalized);
37
+ return normalized;
38
+ }
39
+ export async function resolveSidecarLockPaths(targetPath, explicitLockPath, lockRoot) {
40
+ const anchoredTargetPath = anchorWindowsDriveRelativePath(targetPath);
41
+ assertNoWindowsPathAlias(anchoredTargetPath);
42
+ const resolvedTargetPath = path.resolve(anchoredTargetPath);
43
+ let resolvedLockPath;
44
+ if (explicitLockPath !== undefined) {
45
+ const anchoredLockPath = anchorWindowsDriveRelativePath(explicitLockPath);
46
+ assertNoWindowsPathAlias(anchoredLockPath);
47
+ resolvedLockPath = isCwdIndependentAbsolutePath(explicitLockPath)
48
+ ? explicitLockPath
49
+ : path.resolve(anchoredLockPath);
50
+ }
51
+ const normalizedTargetPath = await resolveSidecarTargetPath(resolvedTargetPath, lockRoot);
52
+ const lockPath = resolvedLockPath ?? `${normalizedTargetPath}.lock`;
53
+ assertNoWindowsPathAlias(lockPath);
54
+ return { lockPath, normalizedTargetPath };
55
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"sidecar-lock.d.ts","sourceRoot":"","sources":["../src/sidecar-lock.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,yBAAyB,EACzB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,YAAY,EACV,yBAAyB,EACzB,0BAA0B,EAC1B,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AAmIjC,0FAA0F;AAC1F,wBAAgB,uBAAuB,IAAI,OAAO,CAMjD;AAyFD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM;cAU3B,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WACpD,yBAAyB,CAAC,QAAQ,CAAC,KAC3C,OAAO,CAAC,iBAAiB,CAAC;eAkBL,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WACxD,yBAAyB,CAAC,QAAQ,CAAC,MACxC,MAAM,OAAO,CAAC,CAAC,CAAC,KACnB,OAAO,CAAC,CAAC,CAAC;iBAqBW,OAAO,CAAC,IAAI,CAAC;iBAQnB,IAAI;uBAIE,oBAAoB,EAAE;EAW/C;AAED,wBAAsB,eAAe,CAAC,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,sBAAsB,CAAC,QAAQ,CAAC,EACzC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAMZ"}
1
+ {"version":3,"file":"sidecar-lock.d.ts","sourceRoot":"","sources":["../src/sidecar-lock.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EACV,yBAAyB,EACzB,iBAAiB,EACjB,oBAAoB,EACpB,sBAAsB,EACvB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EAAE,wBAAwB,EAAE,MAAM,2BAA2B,CAAC;AAC1E,YAAY,EACV,yBAAyB,EACzB,0BAA0B,EAC1B,iBAAiB,EACjB,oBAAoB,EACpB,uBAAuB,EACvB,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,yBAAyB,CAAC;AA2KjC,0FAA0F;AAC1F,wBAAgB,uBAAuB,IAAI,OAAO,CAMjD;AA8HD,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM;cAuB3B,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WACpD,yBAAyB,CAAC,QAAQ,CAAC,KAC3C,OAAO,CAAC,iBAAiB,CAAC;eAaL,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WACxD,yBAAyB,CAAC,QAAQ,CAAC,MACxC,MAAM,OAAO,CAAC,CAAC,CAAC,KACnB,OAAO,CAAC,CAAC,CAAC;iBAqBW,OAAO,CAAC,IAAI,CAAC;iBAQnB,IAAI;uBAIE,oBAAoB,EAAE;EAW/C;AAED,wBAAsB,eAAe,CAAC,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,sBAAsB,CAAC,QAAQ,CAAC,EACzC,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GACnB,OAAO,CAAC,CAAC,CAAC,CAMZ"}
@@ -8,7 +8,10 @@ import { createSuppressedError } from "./suppressed-error.js";
8
8
  const GLOBAL_STATE_KEY = Symbol.for("fsSafe.sidecarLockManagers");
9
9
  const GLOBAL_CLEANUP_KEY = Symbol.for("fsSafe.sidecarLockCleanupRegistered");
10
10
  const GLOBAL_CLEANUP_HANDLER_KEY = Symbol.for("fsSafe.sidecarLockCleanupHandler");
11
+ const GLOBAL_CLEANUP_REGISTRATION_KEY = Symbol.for("fsSafe.sidecarLockCleanupRegistration");
11
12
  const GLOBAL_BEFORE_EXIT_KEY = Symbol.for("fsSafe.sidecarLockBeforeExitCleanup");
13
+ const GLOBAL_BEFORE_EXIT_HANDLER_KEY = Symbol.for("fsSafe.sidecarLockBeforeExitCleanupHandler");
14
+ const GLOBAL_BEFORE_EXIT_REGISTRATION_KEY = Symbol.for("fsSafe.sidecarLockBeforeExitCleanupRegistration");
12
15
  // Set by copies whose exit handlers honor retainOnExit. When an older package
13
16
  // copy registered the handlers first, this marker stays absent and retained
14
17
  // acquisitions must fail closed rather than silently lose the guarantee.
@@ -27,6 +30,7 @@ function resolveManagerState(key) {
27
30
  state = {
28
31
  cleanupRegistered: false,
29
32
  held: new Map(),
33
+ admissions: new Map(),
30
34
  reclaimCleanupRegistered: false,
31
35
  reclaimGuards: new Set(),
32
36
  };
@@ -34,7 +38,9 @@ function resolveManagerState(key) {
34
38
  }
35
39
  else {
36
40
  // The global manager symbol is shared across package copies and hot reloads.
37
- // Backfill state created by fs-safe versions that predate reclaim guards.
41
+ // Backfill state created by fs-safe versions that predate admission tokens
42
+ // or reclaim guards without placing an incomplete value in the held map.
43
+ state.admissions ??= new Map();
38
44
  state.reclaimCleanupRegistered ??= false;
39
45
  state.reclaimGuards ??= new Set();
40
46
  }
@@ -107,21 +113,55 @@ function releaseAllLocksSync(state, options) {
107
113
  }
108
114
  state.held.delete(normalizedTargetPath);
109
115
  }
116
+ state.admissions?.clear();
110
117
  releaseAllReclaimGuardsSync(state);
111
118
  }
112
119
  function ensureGlobalExitCleanupRegistered() {
113
120
  const globalWithCleanup = globalThis;
114
121
  if (globalWithCleanup[GLOBAL_CLEANUP_KEY])
115
122
  return;
116
- globalWithCleanup[GLOBAL_CLEANUP_KEY] = true;
117
- globalWithCleanup[GLOBAL_RETAIN_AWARE_KEY] = true;
123
+ if (globalWithCleanup[GLOBAL_CLEANUP_REGISTRATION_KEY]) {
124
+ throw new FsSafeError("helper-unavailable", "sidecar lock exit cleanup registration is already in progress");
125
+ }
126
+ const registration = {};
118
127
  const cleanup = () => {
119
128
  for (const state of getGlobalManagers().values()) {
120
129
  releaseAllLocksSync(state, { preserveRetained: true });
121
130
  }
122
131
  };
123
- globalWithCleanup[GLOBAL_CLEANUP_HANDLER_KEY] = cleanup;
124
- process.on("exit", cleanup);
132
+ globalWithCleanup[GLOBAL_CLEANUP_REGISTRATION_KEY] = registration;
133
+ try {
134
+ process.on("exit", cleanup);
135
+ globalWithCleanup[GLOBAL_CLEANUP_HANDLER_KEY] = cleanup;
136
+ globalWithCleanup[GLOBAL_RETAIN_AWARE_KEY] = true;
137
+ globalWithCleanup[GLOBAL_CLEANUP_KEY] = true;
138
+ }
139
+ catch (error) {
140
+ if (process.listeners("exit").includes(cleanup)) {
141
+ try {
142
+ process.off("exit", cleanup);
143
+ }
144
+ catch {
145
+ // Reconcile the marker with the actual listener below.
146
+ }
147
+ }
148
+ if (process.listeners("exit").includes(cleanup)) {
149
+ globalWithCleanup[GLOBAL_CLEANUP_HANDLER_KEY] = cleanup;
150
+ globalWithCleanup[GLOBAL_RETAIN_AWARE_KEY] = true;
151
+ globalWithCleanup[GLOBAL_CLEANUP_KEY] = true;
152
+ }
153
+ else {
154
+ if (globalWithCleanup[GLOBAL_CLEANUP_HANDLER_KEY] === cleanup) {
155
+ delete globalWithCleanup[GLOBAL_CLEANUP_HANDLER_KEY];
156
+ }
157
+ }
158
+ throw error;
159
+ }
160
+ finally {
161
+ if (globalWithCleanup[GLOBAL_CLEANUP_REGISTRATION_KEY] === registration) {
162
+ delete globalWithCleanup[GLOBAL_CLEANUP_REGISTRATION_KEY];
163
+ }
164
+ }
125
165
  }
126
166
  /** True when a retain-unaware package copy registered the process-exit handlers first. */
127
167
  export function exitCleanupCannotRetain() {
@@ -133,10 +173,13 @@ function ensureGlobalBeforeExitCleanupRegistered() {
133
173
  if (globalWithCleanup[GLOBAL_BEFORE_EXIT_KEY]) {
134
174
  return globalWithCleanup[GLOBAL_BEFORE_EXIT_KEY];
135
175
  }
176
+ if (globalWithCleanup[GLOBAL_BEFORE_EXIT_REGISTRATION_KEY]) {
177
+ throw new FsSafeError("helper-unavailable", "sidecar lock before-exit cleanup registration is already in progress");
178
+ }
136
179
  // Independent of the exit registration, which an older package copy may own.
137
180
  const lifecycle = { armed: false };
138
- globalWithCleanup[GLOBAL_BEFORE_EXIT_KEY] = lifecycle;
139
- process.on("beforeExit", () => {
181
+ const registration = {};
182
+ const cleanup = () => {
140
183
  if (!lifecycle.armed)
141
184
  return;
142
185
  // Cleanup itself schedules I/O; retry only after another acquisition.
@@ -148,8 +191,42 @@ function ensureGlobalBeforeExitCleanupRegistered() {
148
191
  }
149
192
  }
150
193
  }
151
- });
152
- return lifecycle;
194
+ };
195
+ globalWithCleanup[GLOBAL_BEFORE_EXIT_REGISTRATION_KEY] = registration;
196
+ try {
197
+ process.on("beforeExit", cleanup);
198
+ globalWithCleanup[GLOBAL_BEFORE_EXIT_HANDLER_KEY] = cleanup;
199
+ globalWithCleanup[GLOBAL_BEFORE_EXIT_KEY] = lifecycle;
200
+ return lifecycle;
201
+ }
202
+ catch (error) {
203
+ if (process.listeners("beforeExit").includes(cleanup)) {
204
+ try {
205
+ process.off("beforeExit", cleanup);
206
+ }
207
+ catch {
208
+ // Reconcile the marker with the actual listener below.
209
+ }
210
+ }
211
+ if (process.listeners("beforeExit").includes(cleanup)) {
212
+ globalWithCleanup[GLOBAL_BEFORE_EXIT_HANDLER_KEY] = cleanup;
213
+ globalWithCleanup[GLOBAL_BEFORE_EXIT_KEY] = lifecycle;
214
+ }
215
+ else {
216
+ if (globalWithCleanup[GLOBAL_BEFORE_EXIT_HANDLER_KEY] === cleanup) {
217
+ delete globalWithCleanup[GLOBAL_BEFORE_EXIT_HANDLER_KEY];
218
+ }
219
+ if (globalWithCleanup[GLOBAL_BEFORE_EXIT_KEY] === lifecycle) {
220
+ delete globalWithCleanup[GLOBAL_BEFORE_EXIT_KEY];
221
+ }
222
+ }
223
+ throw error;
224
+ }
225
+ finally {
226
+ if (globalWithCleanup[GLOBAL_BEFORE_EXIT_REGISTRATION_KEY] === registration) {
227
+ delete globalWithCleanup[GLOBAL_BEFORE_EXIT_REGISTRATION_KEY];
228
+ }
229
+ }
153
230
  }
154
231
  async function releaseHeldLock(state, normalizedTargetPath, held, options = {}) {
155
232
  const current = state.held.get(normalizedTargetPath);
@@ -194,7 +271,6 @@ async function releaseHeldLock(state, normalizedTargetPath, held, options = {})
194
271
  }
195
272
  }
196
273
  function handleForHeldLock(state, normalizedTargetPath, held) {
197
- ensureGlobalBeforeExitCleanupRegistered().armed = true;
198
274
  return createHeldSidecarLockHandle({
199
275
  normalizedTargetPath,
200
276
  held,
@@ -204,21 +280,29 @@ function handleForHeldLock(state, normalizedTargetPath, held) {
204
280
  export function createSidecarLockManager(key) {
205
281
  const state = resolveManagerState(key);
206
282
  function ensureExitCleanupRegistered() {
283
+ ensureGlobalExitCleanupRegistered();
284
+ const lifecycle = ensureGlobalBeforeExitCleanupRegistered();
207
285
  state.cleanupRegistered = true;
208
286
  state.reclaimCleanupRegistered = true;
209
- ensureGlobalExitCleanupRegistered();
210
- ensureGlobalBeforeExitCleanupRegistered().armed = true;
287
+ return lifecycle;
288
+ }
289
+ function armExitCleanup(lifecycle) {
290
+ lifecycle.armed = true;
291
+ }
292
+ function assertRetainOnExitSupported(retainOnExit) {
293
+ if (retainOnExit !== true || !exitCleanupCannotRetain())
294
+ return;
295
+ throw new FsSafeError("helper-unavailable", "retainOnExit requires this process's exit handlers to be retain-aware; an older package copy registered them first");
211
296
  }
212
297
  async function acquire(options) {
213
- if (options.retainOnExit === true && exitCleanupCannotRetain()) {
214
- throw new FsSafeError("helper-unavailable", "retainOnExit requires this process's exit handlers to be retain-aware; an older package copy registered them first");
215
- }
216
298
  return await acquireSidecarLock(options, {
217
299
  held: state.held,
300
+ admissions: state.admissions,
218
301
  reclaimGuards: state.reclaimGuards,
219
302
  ensureExitCleanupRegistered,
303
+ armExitCleanup,
304
+ assertRetainOnExitSupported,
220
305
  handleForHeldLock: (normalizedTargetPath, held) => handleForHeldLock(state, normalizedTargetPath, held),
221
- releaseHeldLock: async (normalizedTargetPath, held, releaseOptions) => await releaseHeldLock(state, normalizedTargetPath, held, releaseOptions),
222
306
  });
223
307
  }
224
308
  async function withLock(options, fn) {
@@ -1 +1 @@
1
- {"version":3,"file":"temp-workspace-descriptor.d.ts","sourceRoot":"","sources":["../src/temp-workspace-descriptor.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAC;AAU7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAMhF,OAAO,EAML,KAAK,yBAAyB,EAC/B,MAAM,8BAA8B,CAAC;AAEtC,KAAK,yBAAyB,GAAG,MAAM,GAAG,QAAQ,CAAC;AASnD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,yBAAyB,CAAC;IAClC,OAAO,EAAE,QAAQ,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,QAAQ,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAClD,CAAC,CAAC;CACJ,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAmDpD,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,0BAA0B,GACpC,iBAAiB,CA8BnB;AAED,+EAA+E;AAC/E,qBAAa,0BAA0B;;IAWrC,OAAO,eAqBN;IAED,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,0BAA0B,CAcjF;IAED,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG;QACjC,QAAQ,EAAE,0BAA0B,CAAC;QACrC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC;KAC1B,CAoBA;IAED,qBAAqB,IAAI,IAAI,CAE5B;IAUD,iBAAiB,CACf,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE,MAAM,GACX,yBAAyB,CAsB3B;IAoEK,cAAc,CAClB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,EAAE,MAAM,IAAI,GACvB,OAAO,CAAC,IAAI,CAAC,CAgCf;IAED,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,EAAE,MAAM,IAAI,GACvB,IAAI,CAsBN;IAED,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,cAAc,IAAI,OAAO,CAmDxB;IAED,QAAQ,CAAC,gBAAgB,EAAE,OAAO,GAAG;QACnC,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,QAAQ,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACjD,SAAS,EAAE,sBAAsB,GAAG,SAAS,CAAC;KAC/C,CAiCA;IAED,KAAK,IAAI,IAAI,CAOZ;CACF"}
1
+ {"version":3,"file":"temp-workspace-descriptor.d.ts","sourceRoot":"","sources":["../src/temp-workspace-descriptor.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAC;AAU7B,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAMhF,OAAO,EAML,KAAK,yBAAyB,EAC/B,MAAM,8BAA8B,CAAC;AAEtC,KAAK,yBAAyB,GAAG,MAAM,GAAG,QAAQ,CAAC;AASnD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,yBAAyB,CAAC;IAClC,OAAO,EAAE,QAAQ,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC;QACb,QAAQ,EAAE,MAAM,CAAC;QACjB,QAAQ,EAAE,QAAQ,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KAClD,CAAC,CAAC;CACJ,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAAC;AAmDpD,wBAAgB,8BAA8B,CAC5C,IAAI,EAAE,MAAM,EACZ,SAAS,EAAE,0BAA0B,GACpC,iBAAiB,CA8BnB;AAED,+EAA+E;AAC/E,qBAAa,0BAA0B;;IAWrC,OAAO,eAqBN;IAED,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,GAAG,0BAA0B,CAcjF;IAED,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG;QACjC,QAAQ,EAAE,0BAA0B,CAAC;QACrC,IAAI,EAAE,MAAM,CAAC,WAAW,CAAC;KAC1B,CAoBA;IAED,qBAAqB,IAAI,IAAI,CAE5B;IAUD,iBAAiB,CACf,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,IAAI,EAAE,MAAM,GACX,yBAAyB,CAsB3B;IAgEK,cAAc,CAClB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,EAAE,MAAM,IAAI,GACvB,OAAO,CAAC,IAAI,CAAC,CAgCf;IAED,kBAAkB,CAChB,IAAI,EAAE,MAAM,EACZ,QAAQ,EAAE,MAAM,GAAG,SAAS,EAC5B,YAAY,EAAE,MAAM,IAAI,GACvB,IAAI,CAsBN;IAED,IAAI,YAAY,IAAI,OAAO,CAE1B;IAED,cAAc,IAAI,OAAO,CA2CxB;IAED,QAAQ,CAAC,gBAAgB,EAAE,OAAO,GAAG;QACnC,GAAG,EAAE,MAAM,CAAC;QACZ,QAAQ,EAAE,QAAQ,CAAC;YAAE,GAAG,EAAE,MAAM,CAAC;YAAC,GAAG,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;QACjD,SAAS,EAAE,sBAAsB,GAAG,SAAS,CAAC;KAC/C,CA0BA;IAED,KAAK,IAAI,IAAI,CAOZ;CACF"}
@@ -185,18 +185,15 @@ export class TempWorkspaceRetainedChild {
185
185
  return descriptor;
186
186
  }
187
187
  async #assertProcAuthority(fd, ownerUid) {
188
- if ((await fs.statfs("/proc/self/fd", { bigint: true })).type !== 0x9fa0n) {
189
- throw new FsSafeError("path-mismatch", "directory mode requires a trusted procfs fd namespace");
190
- }
191
- const procPath = `/proc/self/fd/${fd}`;
192
- const opened = inspectFileIdentitySync(() => fsSync.fstatSync(fd, { bigint: true }), this.#identity);
193
- const followed = inspectFileIdentitySync(() => fsSync.statSync(procPath, { bigint: true }), this.#identity);
194
- assertOwnedDirectory(opened, followed);
195
- assertTempWorkspaceChildState(opened, ownerUid);
196
- assertTempWorkspaceChildState(followed, ownerUid);
188
+ const filesystem = await fs.statfs("/proc/self/fd", { bigint: true });
189
+ this.#assertProcDescriptorAuthority(fd, ownerUid, filesystem.type);
197
190
  }
198
191
  #assertProcAuthoritySync(fd, ownerUid) {
199
- if (fsSync.statfsSync("/proc/self/fd", { bigint: true }).type !== 0x9fa0n) {
192
+ const filesystem = fsSync.statfsSync("/proc/self/fd", { bigint: true });
193
+ this.#assertProcDescriptorAuthority(fd, ownerUid, filesystem.type);
194
+ }
195
+ #assertProcDescriptorAuthority(fd, ownerUid, filesystemType) {
196
+ if (filesystemType !== 0x9fa0n) {
200
197
  throw new FsSafeError("path-mismatch", "directory mode requires a trusted procfs fd namespace");
201
198
  }
202
199
  const procPath = `/proc/self/fd/${fd}`;
@@ -309,13 +306,7 @@ export class TempWorkspaceRetainedChild {
309
306
  catch (error) {
310
307
  // The readable replacement has not been installed. Give it exactly one
311
308
  // close attempt and leave neither indeterminate descriptor owned here.
312
- try {
313
- fsSync.closeSync(fd);
314
- }
315
- catch (closeError) {
316
- throw new AggregateError([error, closeError], "temp workspace child descriptor replacement close failed");
317
- }
318
- throw error;
309
+ closeAfterAdmissionFailure(fd, error, "temp workspace child descriptor replacement close failed");
319
310
  }
320
311
  this.#fd = fd;
321
312
  this.#access = "read";
@@ -334,16 +325,7 @@ export class TempWorkspaceRetainedChild {
334
325
  const fd = this.#fd;
335
326
  this.#fd = undefined;
336
327
  if (retainDescriptor && !this.canEnumerate) {
337
- try {
338
- fsSync.closeSync(fd);
339
- }
340
- catch (closeError) {
341
- throw new AggregateError([
342
- new FsSafeError("helper-unavailable", "temp workspace cleanup requires a readable child descriptor"),
343
- closeError,
344
- ], "temp workspace child descriptor rejection and close failed");
345
- }
346
- throw new FsSafeError("helper-unavailable", "temp workspace cleanup requires a readable child descriptor");
328
+ closeAfterAdmissionFailure(fd, new FsSafeError("helper-unavailable", "temp workspace cleanup requires a readable child descriptor"), "temp workspace child descriptor rejection and close failed");
347
329
  }
348
330
  if (!retainDescriptor) {
349
331
  fsSync.closeSync(fd);
@@ -1 +1 @@
1
- {"version":3,"file":"temp-workspace-owner.d.ts","sourceRoot":"","sources":["../src/temp-workspace-owner.ts"],"names":[],"mappings":"AAOA,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAEL,0BAA0B,EAE1B,KAAK,iBAAiB,EACvB,MAAM,gCAAgC,CAAC;AAGxC,MAAM,MAAM,0BAA0B,GAAG,SAAS,GAAG,SAAS,GAAG,mBAAmB,GAAG,eAAe,CAAC;AACvG,MAAM,MAAM,0BAA0B,GAAG,YAAY,GAAG,iBAAiB,CAAC;AAsB1E,qBAAa,8BAA8B;;IACzC,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAM/C,YACE,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,0BAA0B,EAClC,SAAS,EAAE,0BAA0B,EACrC,OAAO,EAAE,MAAM,EAuEhB;IAED,IAAI,kBAAkB,IAAI,OAAO,CAGhC;IAED,oBAAoB,IAAI,IAAI,CAiB3B;IAUD,oBAAoB,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAenD;IAED,aAAa,IAAI,IAAI,CAEpB;IAED,qBAAqB,IAAI,IAAI,CAE5B;IAED,KAAK,IAAI,IAAI,CAIZ;CACF;AAED,qBAAa,yBAAyB;;IAWpC,YACE,QAAQ,EAAE,0BAA0B,EACpC,UAAU,EAAE,8BAA8B,EAC1C,gBAAgB,EAAE,OAAO,EAO1B;IAkLD,OAAO,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAM7C;IAED,WAAW,IAAI,0BAA0B,CAexC;CACF"}
1
+ {"version":3,"file":"temp-workspace-owner.d.ts","sourceRoot":"","sources":["../src/temp-workspace-owner.ts"],"names":[],"mappings":"AAOA,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEnE,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,+BAA+B,CAAC;AAChF,OAAO,EAEL,0BAA0B,EAE1B,KAAK,iBAAiB,EACvB,MAAM,gCAAgC,CAAC;AAGxC,MAAM,MAAM,0BAA0B,GAAG,SAAS,GAAG,SAAS,GAAG,mBAAmB,GAAG,eAAe,CAAC;AACvG,MAAM,MAAM,0BAA0B,GAAG,YAAY,GAAG,iBAAiB,CAAC;AAuB1E,qBAAa,8BAA8B;;IACzC,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAM/C,YACE,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,0BAA0B,EAClC,SAAS,EAAE,0BAA0B,EACrC,OAAO,EAAE,MAAM,EAuEhB;IAED,IAAI,kBAAkB,IAAI,OAAO,CAGhC;IAED,oBAAoB,IAAI,IAAI,CAiB3B;IAUD,oBAAoB,CAAC,YAAY,EAAE,OAAO,GAAG,OAAO,CAenD;IAED,aAAa,IAAI,IAAI,CAEpB;IAED,qBAAqB,IAAI,IAAI,CAE5B;IAED,KAAK,IAAI,IAAI,CAIZ;CACF;AAED,qBAAa,yBAAyB;;IAWpC,YACE,QAAQ,EAAE,0BAA0B,EACpC,UAAU,EAAE,8BAA8B,EAC1C,gBAAgB,EAAE,OAAO,EAO1B;IAkLD,OAAO,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAM7C;IAED,WAAW,IAAI,0BAA0B,CAexC;CACF"}
@@ -294,22 +294,22 @@ export class TempWorkspaceCleanupOwner {
294
294
  await beforeNativeRemoval(quarantine.path);
295
295
  return this.#mapRemoval(await this.#capability.binding.removeOwnedTree(this.#capability.parent.fd, quarantine.name, this.#directory.fd));
296
296
  }
297
- let removalError;
297
+ let removalFailure;
298
298
  try {
299
299
  this.#assertQuarantine(quarantine);
300
300
  try {
301
301
  await fs.rm(quarantine.path, { recursive: true, force: true });
302
302
  }
303
303
  catch (error) {
304
- removalError = error;
304
+ removalFailure = { error };
305
305
  throw error;
306
306
  }
307
307
  this.#capability.assertCurrent();
308
308
  return "removed";
309
309
  }
310
310
  catch (error) {
311
- if (error === removalError)
312
- throw error;
311
+ if (removalFailure !== undefined)
312
+ throw removalFailure.error;
313
313
  return "indeterminate";
314
314
  }
315
315
  }
@@ -318,22 +318,22 @@ export class TempWorkspaceCleanupOwner {
318
318
  getFsSafeTestHooks()?.beforeTempWorkspaceNativeRemovalSync?.(quarantine.path);
319
319
  return this.#mapRemoval(this.#capability.binding.removeOwnedTreeSync(this.#capability.parent.fd, quarantine.name, this.#directory.fd));
320
320
  }
321
- let removalError;
321
+ let removalFailure;
322
322
  try {
323
323
  this.#assertQuarantine(quarantine);
324
324
  try {
325
325
  fsSync.rmSync(quarantine.path, { recursive: true, force: true });
326
326
  }
327
327
  catch (error) {
328
- removalError = error;
328
+ removalFailure = { error };
329
329
  throw error;
330
330
  }
331
331
  this.#capability.assertCurrent();
332
332
  return "removed";
333
333
  }
334
334
  catch (error) {
335
- if (error === removalError)
336
- throw error;
335
+ if (removalFailure !== undefined)
336
+ throw removalFailure.error;
337
337
  return "indeterminate";
338
338
  }
339
339
  }
package/dist/walk.d.ts CHANGED
@@ -16,6 +16,10 @@ export type WalkDirectoryOptions = {
16
16
  include?: (entry: WalkDirectoryEntry) => boolean;
17
17
  descend?: (entry: WalkDirectoryEntry) => boolean;
18
18
  };
19
+ export type AsyncWalkDirectoryOptions = Omit<WalkDirectoryOptions, "include" | "descend"> & {
20
+ include?: (entry: WalkDirectoryEntry) => boolean | Promise<boolean>;
21
+ descend?: (entry: WalkDirectoryEntry) => boolean | Promise<boolean>;
22
+ };
19
23
  export type WalkDirectoryFailure = {
20
24
  path: string;
21
25
  relativePath: string;
@@ -32,6 +36,6 @@ type WalkDirectoryResultWithFailures = WalkDirectoryResult & {
32
36
  failedDirs: WalkDirectoryFailure[];
33
37
  };
34
38
  export declare function walkDirectorySync(rootDir: string, options?: WalkDirectoryOptions): WalkDirectoryResultWithFailures;
35
- export declare function walkDirectory(rootDir: string, options?: WalkDirectoryOptions): Promise<WalkDirectoryResultWithFailures>;
39
+ export declare function walkDirectory(rootDir: string, options?: AsyncWalkDirectoryOptions): Promise<WalkDirectoryResultWithFailures>;
36
40
  export {};
37
41
  //# sourceMappingURL=walk.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAC;AAS7B,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AACvE,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAAC;IACjD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IAGnB,UAAU,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACrC,CAAC;AAEF,KAAK,+BAA+B,GAAG,mBAAmB,GAAG;IAC3D,UAAU,EAAE,oBAAoB,EAAE,CAAC;CACpC,CAAC;AA+EF,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,oBAAyB,GACjC,+BAA+B,CA2DjC;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,+BAA+B,CAAC,CA2D1C"}
1
+ {"version":3,"file":"walk.d.ts","sourceRoot":"","sources":["../src/walk.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,SAAS,CAAC;AAS7B,MAAM,MAAM,aAAa,GAAG,MAAM,GAAG,WAAW,GAAG,SAAS,GAAG,OAAO,CAAC;AACvE,MAAM,MAAM,iBAAiB,GAAG,MAAM,GAAG,QAAQ,GAAG,SAAS,CAAC;AAE9D,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IAC7B,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAAC;IACjD,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,CAAC;CAClD,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,IAAI,CAAC,oBAAoB,EAAE,SAAS,GAAG,SAAS,CAAC,GAAG;IAC1F,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACpE,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CACrE,CAAC;AAEF,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;CAChB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,kBAAkB,EAAE,CAAC;IAC9B,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,OAAO,CAAC;IAGnB,UAAU,CAAC,EAAE,oBAAoB,EAAE,CAAC;CACrC,CAAC;AAEF,KAAK,+BAA+B,GAAG,mBAAmB,GAAG;IAC3D,UAAU,EAAE,oBAAoB,EAAE,CAAC;CACpC,CAAC;AAoFF,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,oBAAyB,GACjC,+BAA+B,CA4DjC;AAED,wBAAsB,aAAa,CACjC,OAAO,EAAE,MAAM,EACf,OAAO,GAAE,yBAA8B,GACtC,OAAO,CAAC,+BAA+B,CAAC,CAiE1C"}