@openclaw/fs-safe 0.5.6 → 0.7.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 (289) hide show
  1. package/CHANGELOG.md +89 -0
  2. package/README.md +23 -10
  3. package/dist/advanced.d.ts +2 -1
  4. package/dist/advanced.d.ts.map +1 -1
  5. package/dist/advanced.js +1 -0
  6. package/dist/archive-crc32.d.ts +2 -0
  7. package/dist/archive-crc32.d.ts.map +1 -0
  8. package/dist/archive-crc32.js +14 -0
  9. package/dist/archive-deadline.d.ts +3 -0
  10. package/dist/archive-deadline.d.ts.map +1 -1
  11. package/dist/archive-deadline.js +44 -8
  12. package/dist/archive-entry.d.ts.map +1 -1
  13. package/dist/archive-entry.js +1 -0
  14. package/dist/archive-errors.d.ts +1 -0
  15. package/dist/archive-errors.d.ts.map +1 -1
  16. package/dist/archive-errors.js +3 -0
  17. package/dist/archive-input.d.ts.map +1 -1
  18. package/dist/archive-input.js +26 -16
  19. package/dist/archive-kind.js +2 -2
  20. package/dist/archive-limits.d.ts +11 -3
  21. package/dist/archive-limits.d.ts.map +1 -1
  22. package/dist/archive-limits.js +24 -0
  23. package/dist/archive-native.d.ts +3 -2
  24. package/dist/archive-native.d.ts.map +1 -1
  25. package/dist/archive-native.js +31 -9
  26. package/dist/archive-policy.d.ts +2 -0
  27. package/dist/archive-policy.d.ts.map +1 -1
  28. package/dist/archive-policy.js +9 -1
  29. package/dist/archive-read.d.ts.map +1 -1
  30. package/dist/archive-read.js +88 -41
  31. package/dist/archive-staging.d.ts +3 -0
  32. package/dist/archive-staging.d.ts.map +1 -1
  33. package/dist/archive-staging.js +91 -43
  34. package/dist/archive-tar-admission.d.ts +7 -0
  35. package/dist/archive-tar-admission.d.ts.map +1 -0
  36. package/dist/archive-tar-admission.js +43 -0
  37. package/dist/archive-tar-gnu.d.ts +2 -0
  38. package/dist/archive-tar-gnu.d.ts.map +1 -0
  39. package/dist/archive-tar-gnu.js +20 -0
  40. package/dist/archive-tar-header.d.ts +8 -0
  41. package/dist/archive-tar-header.d.ts.map +1 -0
  42. package/dist/archive-tar-header.js +47 -0
  43. package/dist/archive-tar-meta.d.ts +29 -1
  44. package/dist/archive-tar-meta.d.ts.map +1 -1
  45. package/dist/archive-tar-meta.js +135 -19
  46. package/dist/archive-tar-pax.d.ts +8 -0
  47. package/dist/archive-tar-pax.d.ts.map +1 -0
  48. package/dist/archive-tar-pax.js +100 -0
  49. package/dist/archive-tar-runtime.d.ts +4 -0
  50. package/dist/archive-tar-runtime.d.ts.map +1 -1
  51. package/dist/archive-tar-runtime.js +3 -0
  52. package/dist/archive-tar.d.ts.map +1 -1
  53. package/dist/archive-tar.js +8 -2
  54. package/dist/archive-zip-admission.d.ts +7 -0
  55. package/dist/archive-zip-admission.d.ts.map +1 -0
  56. package/dist/archive-zip-admission.js +60 -0
  57. package/dist/archive-zip-count.d.ts +2 -0
  58. package/dist/archive-zip-count.d.ts.map +1 -0
  59. package/dist/archive-zip-count.js +140 -0
  60. package/dist/archive-zip-directory.d.ts +8 -0
  61. package/dist/archive-zip-directory.d.ts.map +1 -0
  62. package/dist/archive-zip-directory.js +224 -0
  63. package/dist/archive-zip-integrity.d.ts.map +1 -1
  64. package/dist/archive-zip-integrity.js +1 -14
  65. package/dist/archive-zip-names.d.ts +12 -0
  66. package/dist/archive-zip-names.d.ts.map +1 -0
  67. package/dist/archive-zip-names.js +99 -0
  68. package/dist/archive-zip-preflight.d.ts +1 -1
  69. package/dist/archive-zip-preflight.d.ts.map +1 -1
  70. package/dist/archive-zip-preflight.js +8 -145
  71. package/dist/archive.d.ts.map +1 -1
  72. package/dist/archive.js +63 -42
  73. package/dist/atomic.d.ts +1 -1
  74. package/dist/atomic.d.ts.map +1 -1
  75. package/dist/bounded-read-stream.d.ts.map +1 -1
  76. package/dist/bounded-read-stream.js +2 -4
  77. package/dist/bounded-read.d.ts.map +1 -1
  78. package/dist/bounded-read.js +3 -10
  79. package/dist/byte-budget.d.ts +5 -0
  80. package/dist/byte-budget.d.ts.map +1 -0
  81. package/dist/byte-budget.js +9 -0
  82. package/dist/file-hash.d.ts.map +1 -1
  83. package/dist/file-hash.js +25 -19
  84. package/dist/file-lock-sync.d.ts.map +1 -1
  85. package/dist/file-lock-sync.js +26 -9
  86. package/dist/file-store-boundary.d.ts.map +1 -1
  87. package/dist/file-store-boundary.js +9 -5
  88. package/dist/file-store-limit.d.ts +2 -0
  89. package/dist/file-store-limit.d.ts.map +1 -0
  90. package/dist/file-store-limit.js +8 -0
  91. package/dist/file-store-sync-write.d.ts.map +1 -1
  92. package/dist/file-store-sync-write.js +38 -8
  93. package/dist/file-store.d.ts.map +1 -1
  94. package/dist/file-store.js +26 -26
  95. package/dist/json-durable-queue-directory.d.ts +2 -0
  96. package/dist/json-durable-queue-directory.d.ts.map +1 -0
  97. package/dist/json-durable-queue-directory.js +20 -0
  98. package/dist/json-durable-queue-ownership.d.ts +14 -0
  99. package/dist/json-durable-queue-ownership.d.ts.map +1 -0
  100. package/dist/json-durable-queue-ownership.js +168 -0
  101. package/dist/json-durable-queue-retirement.d.ts +9 -0
  102. package/dist/json-durable-queue-retirement.d.ts.map +1 -0
  103. package/dist/json-durable-queue-retirement.js +126 -0
  104. package/dist/json-durable-queue-transfer-lock.d.ts +2 -0
  105. package/dist/json-durable-queue-transfer-lock.d.ts.map +1 -0
  106. package/dist/json-durable-queue-transfer-lock.js +19 -0
  107. package/dist/json-durable-queue.d.ts +1 -0
  108. package/dist/json-durable-queue.d.ts.map +1 -1
  109. package/dist/json-durable-queue.js +90 -57
  110. package/dist/json.d.ts.map +1 -1
  111. package/dist/json.js +27 -8
  112. package/dist/local-roots.d.ts.map +1 -1
  113. package/dist/local-roots.js +4 -2
  114. package/dist/native-binding.d.ts +15 -3
  115. package/dist/native-binding.d.ts.map +1 -1
  116. package/dist/native-operations.d.ts +4 -1
  117. package/dist/native-operations.d.ts.map +1 -1
  118. package/dist/native-operations.js +22 -6
  119. package/dist/native-pinned-write-windows.d.ts +8 -0
  120. package/dist/native-pinned-write-windows.d.ts.map +1 -0
  121. package/dist/native-pinned-write-windows.js +92 -0
  122. package/dist/native-pinned-write.d.ts.map +1 -1
  123. package/dist/native-pinned-write.js +136 -127
  124. package/dist/native-staged-file.d.ts +24 -0
  125. package/dist/native-staged-file.d.ts.map +1 -0
  126. package/dist/native-staged-file.js +337 -0
  127. package/dist/native.d.ts.map +1 -1
  128. package/dist/native.js +4 -4
  129. package/dist/opened-realpath.d.ts +2 -0
  130. package/dist/opened-realpath.d.ts.map +1 -1
  131. package/dist/opened-realpath.js +12 -7
  132. package/dist/output-sibling.d.ts.map +1 -1
  133. package/dist/output-sibling.js +11 -110
  134. package/dist/output.d.ts.map +1 -1
  135. package/dist/output.js +4 -2
  136. package/dist/owner-dacl.d.ts.map +1 -1
  137. package/dist/owner-dacl.js +2 -1
  138. package/dist/permission-exec.d.ts +19 -0
  139. package/dist/permission-exec.d.ts.map +1 -1
  140. package/dist/permission-exec.js +57 -11
  141. package/dist/permissions-public.d.ts +1 -1
  142. package/dist/permissions-public.d.ts.map +1 -1
  143. package/dist/permissions-windows.d.ts +3 -0
  144. package/dist/permissions-windows.d.ts.map +1 -1
  145. package/dist/permissions-windows.js +16 -5
  146. package/dist/permissions.d.ts +5 -0
  147. package/dist/permissions.d.ts.map +1 -1
  148. package/dist/pinned-open.d.ts.map +1 -1
  149. package/dist/pinned-open.js +27 -48
  150. package/dist/pinned-write.d.ts +6 -0
  151. package/dist/pinned-write.d.ts.map +1 -1
  152. package/dist/pinned-write.js +26 -37
  153. package/dist/private-directory.d.ts.map +1 -1
  154. package/dist/private-directory.js +3 -2
  155. package/dist/private-temp-workspace.d.ts +3 -1
  156. package/dist/private-temp-workspace.d.ts.map +1 -1
  157. package/dist/private-temp-workspace.js +81 -56
  158. package/dist/publish-file.d.ts.map +1 -1
  159. package/dist/publish-file.js +2 -4
  160. package/dist/read-opened-file.d.ts.map +1 -1
  161. package/dist/read-opened-file.js +6 -4
  162. package/dist/regular-file.d.ts.map +1 -1
  163. package/dist/regular-file.js +136 -82
  164. package/dist/replace-file-copy-fallback.d.ts +3 -1
  165. package/dist/replace-file-copy-fallback.d.ts.map +1 -1
  166. package/dist/replace-file-copy-fallback.js +21 -36
  167. package/dist/replace-file-copy-source.d.ts +21 -0
  168. package/dist/replace-file-copy-source.d.ts.map +1 -0
  169. package/dist/replace-file-copy-source.js +112 -0
  170. package/dist/replace-file-descriptor.d.ts +13 -3
  171. package/dist/replace-file-descriptor.d.ts.map +1 -1
  172. package/dist/replace-file-descriptor.js +32 -7
  173. package/dist/replace-file-rename-policy.d.ts +7 -0
  174. package/dist/replace-file-rename-policy.d.ts.map +1 -0
  175. package/dist/replace-file-rename-policy.js +30 -0
  176. package/dist/replace-file-temp-owner.d.ts +46 -0
  177. package/dist/replace-file-temp-owner.d.ts.map +1 -0
  178. package/dist/replace-file-temp-owner.js +346 -0
  179. package/dist/replace-file.d.ts +6 -1
  180. package/dist/replace-file.d.ts.map +1 -1
  181. package/dist/replace-file.js +72 -58
  182. package/dist/root-impl.d.ts.map +1 -1
  183. package/dist/root-impl.js +118 -98
  184. package/dist/root-paths.d.ts +11 -14
  185. package/dist/root-paths.d.ts.map +1 -1
  186. package/dist/root-paths.js +36 -27
  187. package/dist/root-write-verification.d.ts +11 -0
  188. package/dist/root-write-verification.d.ts.map +1 -0
  189. package/dist/root-write-verification.js +91 -0
  190. package/dist/secret-file.d.ts +1 -6
  191. package/dist/secret-file.d.ts.map +1 -1
  192. package/dist/secret-file.js +49 -120
  193. package/dist/secret-read-async.d.ts +1 -1
  194. package/dist/secret-read-async.d.ts.map +1 -1
  195. package/dist/secret-read-async.js +51 -72
  196. package/dist/secret-read-policy.d.ts +13 -0
  197. package/dist/secret-read-policy.d.ts.map +1 -0
  198. package/dist/secret-read-policy.js +28 -0
  199. package/dist/secret.d.ts +2 -1
  200. package/dist/secret.d.ts.map +1 -1
  201. package/dist/secret.js +2 -1
  202. package/dist/secure-file.d.ts.map +1 -1
  203. package/dist/secure-file.js +42 -29
  204. package/dist/sibling-staged-file.d.ts +15 -0
  205. package/dist/sibling-staged-file.d.ts.map +1 -0
  206. package/dist/sibling-staged-file.js +148 -0
  207. package/dist/sibling-temp.d.ts +3 -0
  208. package/dist/sibling-temp.d.ts.map +1 -1
  209. package/dist/sibling-temp.js +30 -74
  210. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  211. package/dist/sidecar-lock-acquire.js +48 -27
  212. package/dist/sidecar-lock-handle.d.ts +6 -2
  213. package/dist/sidecar-lock-handle.d.ts.map +1 -1
  214. package/dist/sidecar-lock-handle.js +17 -3
  215. package/dist/sidecar-lock-policy.d.ts +2 -0
  216. package/dist/sidecar-lock-policy.d.ts.map +1 -1
  217. package/dist/sidecar-lock-policy.js +29 -0
  218. package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
  219. package/dist/sidecar-lock-reclaim.js +29 -7
  220. package/dist/sidecar-lock.d.ts.map +1 -1
  221. package/dist/sidecar-lock.js +28 -16
  222. package/dist/staged-directory.d.ts +16 -0
  223. package/dist/staged-directory.d.ts.map +1 -0
  224. package/dist/staged-directory.js +60 -0
  225. package/dist/staged-file-types.d.ts +56 -0
  226. package/dist/staged-file-types.d.ts.map +1 -0
  227. package/dist/staged-file-types.js +1 -0
  228. package/dist/staged-file.d.ts +10 -0
  229. package/dist/staged-file.d.ts.map +1 -0
  230. package/dist/staged-file.js +15 -0
  231. package/dist/strict-file-identity.d.ts +6 -0
  232. package/dist/strict-file-identity.d.ts.map +1 -0
  233. package/dist/strict-file-identity.js +48 -0
  234. package/dist/suppressed-error.d.ts +6 -0
  235. package/dist/suppressed-error.d.ts.map +1 -0
  236. package/dist/suppressed-error.js +15 -0
  237. package/dist/temp-cleanup.d.ts +2 -0
  238. package/dist/temp-cleanup.d.ts.map +1 -1
  239. package/dist/temp-cleanup.js +25 -10
  240. package/dist/temp-workspace-owner.d.ts +23 -0
  241. package/dist/temp-workspace-owner.d.ts.map +1 -0
  242. package/dist/temp-workspace-owner.js +320 -0
  243. package/dist/temp.d.ts +1 -1
  244. package/dist/temp.d.ts.map +1 -1
  245. package/dist/test-hooks.d.ts +5 -0
  246. package/dist/test-hooks.d.ts.map +1 -1
  247. package/dist/windows-owner.d.ts +3 -0
  248. package/dist/windows-owner.d.ts.map +1 -1
  249. package/dist/windows-owner.js +10 -2
  250. package/docs/advanced.md +19 -2
  251. package/docs/archive.md +250 -35
  252. package/docs/atomic.md +11 -2
  253. package/docs/config.md +7 -0
  254. package/docs/contributing.md +45 -7
  255. package/docs/durability.md +18 -5
  256. package/docs/errors.md +16 -1
  257. package/docs/file-store.md +2 -0
  258. package/docs/index.md +3 -1
  259. package/docs/install.md +20 -8
  260. package/docs/json.md +8 -4
  261. package/docs/migrating-to-0.5.md +7 -7
  262. package/docs/migrating-to-0.6.md +43 -0
  263. package/docs/native-helper.md +30 -8
  264. package/docs/native.md +73 -17
  265. package/docs/output.md +10 -0
  266. package/docs/path-scope.md +28 -2
  267. package/docs/permissions.md +13 -2
  268. package/docs/public-api.md +6 -3
  269. package/docs/quickstart.md +1 -1
  270. package/docs/reading.md +1 -1
  271. package/docs/regular-file.md +9 -2
  272. package/docs/root.md +3 -1
  273. package/docs/secret-file.md +12 -0
  274. package/docs/secure-file.md +21 -3
  275. package/docs/security-model.md +33 -1
  276. package/docs/sidecar-lock.md +15 -1
  277. package/docs/staged-file.md +178 -0
  278. package/docs/store.md +13 -0
  279. package/docs/temp.md +128 -14
  280. package/docs/testing.md +1 -1
  281. package/docs/writing.md +19 -0
  282. package/package.json +16 -9
  283. package/dist/native/darwin-arm64/fs-safe-native.node +0 -0
  284. package/dist/native/darwin-x64/fs-safe-native.node +0 -0
  285. package/dist/native/linux-arm64-gnu/fs-safe-native.node +0 -0
  286. package/dist/native/linux-arm64-musl/fs-safe-native.node +0 -0
  287. package/dist/native/linux-x64-gnu/fs-safe-native.node +0 -0
  288. package/dist/native/linux-x64-musl/fs-safe-native.node +0 -0
  289. package/dist/native/win32-x64-msvc/fs-safe-native.node +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"sidecar-lock-policy.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAEvE,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,uBAAuB,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAOjG;AASD,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGnF;AAED,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,OAAO,CAGT;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAU7E;AAED,wBAAsB,+BAA+B,CAAC,MAAM,EAAE;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,OAAO,CAAC,CAQnB"}
1
+ {"version":3,"file":"sidecar-lock-policy.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-policy.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,yBAAyB,CAAC;AAQvE,wBAAgB,+BAA+B,CAAC,KAAK,EAAE,uBAAuB,GAAG,IAAI,CAkBpF;AAED,wBAAgB,4BAA4B,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAGhF;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,uBAAuB,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAQjG;AASD,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAGnF;AAED,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,GACZ,OAAO,CAGT;AAED,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAU7E;AAED,wBAAsB,+BAA+B,CAAC,MAAM,EAAE;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,OAAO,CAAC,CAQnB"}
@@ -1,5 +1,34 @@
1
1
  import fs from "node:fs/promises";
2
+ function assertFiniteNonNegative(value, label) {
3
+ if (!Number.isFinite(value) || value < 0) {
4
+ throw new RangeError(`${label} must be a finite non-negative number`);
5
+ }
6
+ }
7
+ export function validateSidecarLockRetryOptions(retry) {
8
+ if (retry.retries !== undefined && (!Number.isSafeInteger(retry.retries) || retry.retries < 0)) {
9
+ throw new RangeError("lock retry.retries must be a non-negative safe integer");
10
+ }
11
+ if (retry.factor !== undefined)
12
+ assertFiniteNonNegative(retry.factor, "lock retry.factor");
13
+ if (retry.minTimeout !== undefined) {
14
+ assertFiniteNonNegative(retry.minTimeout, "lock retry.minTimeout");
15
+ }
16
+ if (retry.maxTimeout !== undefined) {
17
+ assertFiniteNonNegative(retry.maxTimeout, "lock retry.maxTimeout");
18
+ }
19
+ if (retry.minTimeout !== undefined &&
20
+ retry.maxTimeout !== undefined &&
21
+ retry.minTimeout > retry.maxTimeout) {
22
+ throw new RangeError("lock retry.minTimeout must not exceed retry.maxTimeout");
23
+ }
24
+ }
25
+ export function validateSidecarLockTimeoutMs(timeoutMs) {
26
+ if (timeoutMs === undefined || timeoutMs === Number.POSITIVE_INFINITY)
27
+ return;
28
+ assertFiniteNonNegative(timeoutMs, "lock timeoutMs");
29
+ }
2
30
  export function computeSidecarLockDelayMs(retry, attempt) {
31
+ validateSidecarLockRetryOptions(retry);
3
32
  const minTimeout = retry.minTimeout ?? 50;
4
33
  const maxTimeout = retry.maxTimeout ?? 1000;
5
34
  const factor = retry.factor ?? 1;
@@ -1 +1 @@
1
- {"version":3,"file":"sidecar-lock-reclaim.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-reclaim.ts"],"names":[],"mappings":"AACA,OAAe,EAAE,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAM7C,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAY3C,MAAM,MAAM,wBAAwB,GAAG;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,CAAC,EAAE,KAAK,CAAC;IACb,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAYF,wBAAgB,6BAA6B,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAE7E;AAED,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;IAC7E,GAAG,EAAE,MAAM,CAAC;IACZ,cAAc,EAAE,MAAM,CAAC;CACxB,CAMA;AAED,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAahF;AAED,wBAAgB,uBAAuB,CACrC,GAAG,EAAE,MAAM,EACX,MAAM,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,GAChC,OAAO,CAUT;AAED,wBAAsB,uBAAuB,CAC3C,QAAQ,EAAE,MAAM,EAChB,OAAO,GAAE;IACP,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,4BAA4B,CAAC,EAAE,OAAO,CAAC;CACnC,GACL,OAAO,CAAC,mBAAmB,GAAG,IAAI,CAAC,CAkErC;AAED,wBAAgB,2BAA2B,CACzC,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EACvC,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,mBAAmB,GAAG,IAAI,CA+B5B;AAED,wBAAgB,gCAAgC,CAC9C,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAKT;AAED,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE,mBAAmB,GAC5B,OAAO,CAkBT;AAED,wBAAsB,4BAA4B,CAChD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,mBAAmB,GAAG,IAAI,EACpC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,IAAI,CAAC;IAAC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CAAO,GACzE,OAAO,CAAC,OAAO,CAAC,CAclB;AAED,wBAAsB,+BAA+B,CACnD,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,mBAAmB,GAAG,IAAI,EACpC,OAAO,GAAE;IAAE,QAAQ,CAAC,EAAE,IAAI,CAAC;IAAC,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAA;CAAO,GACzE,OAAO,CAAC,OAAO,CAAC,CAMlB;AAED,wBAAsB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAUlF;AAED,wBAAsB,6BAA6B,CACjD,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,EAC1B,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,OAAO,CAAC,CAWlB;AAED,wBAAsB,0BAA0B,CAC9C,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,EAC1B,gBAAgB,EAAE,MAAM,GACvB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,+BAA+B,CAAC,MAAM,EAAE;IAC5D,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,QAAQ,EAAE,mBAAmB,CAAC;IAC9B,qBAAqB,CAAC,EAAE,CAAC,QAAQ,EAAE,wBAAwB,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3F,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;CACzC,GAAG,OAAO,CAAC,SAAS,GAAG,SAAS,GAAG,cAAc,CAAC,CAkClD"}
1
+ {"version":3,"file":"sidecar-lock-reclaim.d.ts","sourceRoot":"","sources":["../src/sidecar-lock-reclaim.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,7 @@ import path from "node:path";
5
5
  import { readFileDescriptorBoundedSync, readFileHandleBounded } from "./bounded-read.js";
6
6
  import { FsSafeError } from "./errors.js";
7
7
  import { sameFileIdentity } from "./file-identity.js";
8
+ import { resolveReadOpenFlags } from "./read-open-flags.js";
8
9
  import { getFsSafeTestHooks } from "./test-hooks.js";
9
10
  const MAX_LOCK_PAYLOAD_BYTES = 1024 * 1024;
10
11
  const SIDECAR_LOCK_OWNERSHIP_TOKEN_BYTES = 16;
@@ -59,7 +60,25 @@ export async function readSidecarLockSnapshot(lockPath, options = {}) {
59
60
  let handle;
60
61
  try {
61
62
  if (options.lockRoot) {
62
- const opened = await options.lockRoot.open(relativeSidecarLockPath(options.lockRoot, lockPath));
63
+ const lockRoot = options.lockRoot;
64
+ const relative = relativeSidecarLockPath(lockRoot, lockPath);
65
+ const opened = await lockRoot.open(relative).catch(async (error) => {
66
+ if (error instanceof FsSafeError && error.code === "path-mismatch") {
67
+ // An owner can unlink after open. Prove absence through the same root;
68
+ // acquisition retries create-only, so a later replacement stays guarded.
69
+ try {
70
+ await lockRoot.stat(relative);
71
+ }
72
+ catch (probeError) {
73
+ if (probeError instanceof FsSafeError && probeError.code === "not-found") {
74
+ return null;
75
+ }
76
+ }
77
+ }
78
+ throw error;
79
+ });
80
+ if (!opened)
81
+ return null;
63
82
  try {
64
83
  const raw = (await readFileHandleBounded(opened.handle, MAX_LOCK_PAYLOAD_BYTES)).toString("utf8");
65
84
  return {
@@ -132,11 +151,14 @@ export function readSidecarLockSnapshotSync(lockPath, parsePayload, options = {}
132
151
  }
133
152
  return null;
134
153
  }
135
- const noFollow = process.platform !== "win32" && typeof fsSync.constants.O_NOFOLLOW === "number"
136
- ? fsSync.constants.O_NOFOLLOW
137
- : 0;
138
- fd = fsSync.openSync(lockPath, fsSync.constants.O_RDONLY | noFollow);
154
+ fd = fsSync.openSync(lockPath, resolveReadOpenFlags());
139
155
  const opened = fsSync.fstatSync(fd);
156
+ if (!opened.isFile()) {
157
+ if (options.rejectNonFile) {
158
+ throw new FsSafeError("not-file", `sidecar lock is not a regular file: ${lockPath}`);
159
+ }
160
+ return null;
161
+ }
140
162
  const raw = readFileDescriptorBoundedSync(fd, MAX_LOCK_PAYLOAD_BYTES).toString("utf8");
141
163
  const after = fsSync.lstatSync(lockPath);
142
164
  if (!sameFileIdentity(before, opened) || !sameFileIdentity(opened, after))
@@ -191,10 +213,10 @@ export async function removeSidecarLockIfUnchanged(lockPath, observed, options =
191
213
  return false;
192
214
  }
193
215
  if (options.lockRoot) {
194
- await options.lockRoot.remove(relativeSidecarLockPath(options.lockRoot, lockPath)).catch(() => undefined);
216
+ await options.lockRoot.remove(relativeSidecarLockPath(options.lockRoot, lockPath));
195
217
  }
196
218
  else {
197
- await fs.rm(lockPath, { force: true }).catch(() => undefined);
219
+ await fs.rm(lockPath, { force: true });
198
220
  }
199
221
  return true;
200
222
  }
@@ -1 +1 @@
1
- {"version":3,"file":"sidecar-lock.d.ts","sourceRoot":"","sources":["../src/sidecar-lock.ts"],"names":[],"mappings":"AASA,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;AAmLjC,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM;cAS3B,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WACpD,yBAAyB,CAAC,QAAQ,CAAC,KAC3C,OAAO,CAAC,iBAAiB,CAAC;eAYL,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;iBASW,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":"AAUA,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;AAsLjC,wBAAgB,wBAAwB,CAAC,GAAG,EAAE,MAAM;cAS3B,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,WACpD,yBAAyB,CAAC,QAAQ,CAAC,KAC3C,OAAO,CAAC,iBAAiB,CAAC;eAYL,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"}
@@ -3,6 +3,7 @@ import { sameFileIdentity } from "./file-identity.js";
3
3
  import { removeSidecarLockIfUnchanged, sidecarLockSnapshotMatches, } from "./sidecar-lock-reclaim.js";
4
4
  import { acquireSidecarLock } from "./sidecar-lock-acquire.js";
5
5
  import { createHeldSidecarLockHandle } from "./sidecar-lock-handle.js";
6
+ import { createSuppressedError } from "./suppressed-error.js";
6
7
  const GLOBAL_STATE_KEY = Symbol.for("fsSafe.sidecarLockManagers");
7
8
  const GLOBAL_CLEANUP_KEY = Symbol.for("fsSafe.sidecarLockCleanupRegistered");
8
9
  const GLOBAL_CLEANUP_HANDLER_KEY = Symbol.for("fsSafe.sidecarLockCleanupHandler");
@@ -121,23 +122,18 @@ async function releaseHeldLock(state, normalizedTargetPath, held, options = {})
121
122
  if (current !== held) {
122
123
  return false;
123
124
  }
125
+ if (held.releasePromise) {
126
+ await held.releasePromise;
127
+ return true;
128
+ }
124
129
  if (options.force) {
125
130
  held.refCount = 0;
126
131
  }
127
- else {
132
+ else if (!options.retry && held.refCount > 0) {
128
133
  held.refCount -= 1;
129
- if (held.refCount > 0) {
130
- return false;
131
- }
132
- }
133
- if (held.releasePromise) {
134
- await held.releasePromise.catch(() => undefined);
135
- return true;
136
134
  }
137
- state.held.delete(normalizedTargetPath);
138
- if (held.compromiseTimer) {
139
- clearInterval(held.compromiseTimer);
140
- held.compromiseTimer = undefined;
135
+ if (held.refCount > 0) {
136
+ return false;
141
137
  }
142
138
  held.releasePromise = (async () => {
143
139
  await held.handle.close().catch(() => undefined);
@@ -145,6 +141,13 @@ async function releaseHeldLock(state, normalizedTargetPath, held, options = {})
145
141
  lockRoot: held.lockRoot,
146
142
  parsePayload: held.parsePayload,
147
143
  });
144
+ if (state.held.get(normalizedTargetPath) === held) {
145
+ state.held.delete(normalizedTargetPath);
146
+ }
147
+ if (held.compromiseTimer) {
148
+ clearInterval(held.compromiseTimer);
149
+ held.compromiseTimer = undefined;
150
+ }
148
151
  })();
149
152
  try {
150
153
  await held.releasePromise;
@@ -158,7 +161,7 @@ function handleForHeldLock(state, normalizedTargetPath, held) {
158
161
  return createHeldSidecarLockHandle({
159
162
  normalizedTargetPath,
160
163
  held,
161
- release: async () => await releaseHeldLock(state, normalizedTargetPath, held),
164
+ release: async (options) => await releaseHeldLock(state, normalizedTargetPath, held, { retry: options?.retry }),
162
165
  });
163
166
  }
164
167
  export function createSidecarLockManager(key) {
@@ -179,12 +182,21 @@ export function createSidecarLockManager(key) {
179
182
  }
180
183
  async function withLock(options, fn) {
181
184
  const lock = await acquire(options);
185
+ let result;
182
186
  try {
183
- return await fn();
187
+ result = await fn();
184
188
  }
185
- finally {
186
- await lock.release();
189
+ catch (bodyError) {
190
+ try {
191
+ await lock.release();
192
+ }
193
+ catch (releaseError) {
194
+ throw createSuppressedError(releaseError, bodyError, "file lock callback and release both failed");
195
+ }
196
+ throw bodyError;
187
197
  }
198
+ await lock.release();
199
+ return result;
188
200
  }
189
201
  async function drain() {
190
202
  for (const [normalizedTargetPath, held] of Array.from(state.held.entries())) {
@@ -0,0 +1,16 @@
1
+ import type { DirectoryReceipt } from "./directory-durability.js";
2
+ import type { FileIdentityStat } from "./file-identity.js";
3
+ import type { StagedFileReceipt } from "./staged-file-types.js";
4
+ type DirectorySnapshot = StagedFileReceipt["directory"];
5
+ export declare function exactIdentityMatches(expected: FileIdentityStat, actual: Readonly<{
6
+ dev: bigint;
7
+ ino: bigint;
8
+ }>): boolean;
9
+ export declare function describeStagedDirectory(fd: number, pathname: string): DirectorySnapshot;
10
+ export declare function assertStagedDirectoryCurrent(receipt: DirectorySnapshot): void;
11
+ export declare function openStagedDirectory(directory: string | DirectoryReceipt): {
12
+ fd: number;
13
+ receipt: DirectorySnapshot;
14
+ };
15
+ export {};
16
+ //# sourceMappingURL=staged-directory.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staged-directory.d.ts","sourceRoot":"","sources":["../src/staged-directory.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAElE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAC3D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,KAAK,iBAAiB,GAAG,iBAAiB,CAAC,WAAW,CAAC,CAAC;AAExD,wBAAgB,oBAAoB,CAClC,QAAQ,EAAE,gBAAgB,EAC1B,MAAM,EAAE,QAAQ,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,GAC7C,OAAO,CAKT;AAED,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,iBAAiB,CAYvF;AAED,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,iBAAiB,GAAG,IAAI,CAQ7E;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,GAAG,gBAAgB,GAAG;IACzE,EAAE,EAAE,MAAM,CAAC;IACX,OAAO,EAAE,iBAAiB,CAAC;CAC5B,CA+BA"}
@@ -0,0 +1,60 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ import { FsSafeError } from "./errors.js";
4
+ export function exactIdentityMatches(expected, actual) {
5
+ return ["dev", "ino"].every((key) => {
6
+ const value = expected[key];
7
+ return (typeof value === "bigint" || Number.isSafeInteger(value)) && BigInt(value) === actual[key];
8
+ });
9
+ }
10
+ export function describeStagedDirectory(fd, pathname) {
11
+ const identity = fs.fstatSync(fd, { bigint: true });
12
+ if (!identity.isDirectory()) {
13
+ throw new FsSafeError("not-file", "staging parent must be a directory");
14
+ }
15
+ const receipt = Object.freeze({
16
+ path: path.resolve(pathname),
17
+ realPath: fs.realpathSync(pathname),
18
+ identity: Object.freeze({ dev: identity.dev, ino: identity.ino }),
19
+ });
20
+ assertStagedDirectoryCurrent(receipt);
21
+ return receipt;
22
+ }
23
+ export function assertStagedDirectoryCurrent(receipt) {
24
+ const current = fs.lstatSync(receipt.path, { bigint: true });
25
+ if (!current.isDirectory() || !exactIdentityMatches(receipt.identity, current) ||
26
+ fs.realpathSync(receipt.path) !== receipt.realPath) {
27
+ throw new FsSafeError("path-mismatch", "staging directory pathname changed");
28
+ }
29
+ }
30
+ export function openStagedDirectory(directory) {
31
+ // Copy supplied facts before any asynchronous work; receipts are not authority.
32
+ const pathname = path.resolve(typeof directory === "string" ? directory : directory.path);
33
+ const expected = typeof directory === "string" ? undefined : {
34
+ realPath: directory.realPath, dev: directory.identity.dev, ino: directory.identity.ino,
35
+ };
36
+ const before = fs.lstatSync(pathname, { bigint: true });
37
+ if (!before.isDirectory()) {
38
+ throw new FsSafeError("not-file", "staging parent must be a real directory");
39
+ }
40
+ if (expected && (!exactIdentityMatches(expected, before) || fs.realpathSync(pathname) !== expected.realPath)) {
41
+ throw new FsSafeError("path-mismatch", "stale staging directory receipt");
42
+ }
43
+ const fd = fs.openSync(pathname, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
44
+ try {
45
+ const receipt = describeStagedDirectory(fd, pathname);
46
+ if (!exactIdentityMatches(before, receipt.identity)) {
47
+ throw new FsSafeError("path-mismatch", "staging directory changed while opening");
48
+ }
49
+ return { fd, receipt };
50
+ }
51
+ catch (error) {
52
+ try {
53
+ fs.closeSync(fd);
54
+ }
55
+ catch (closeError) {
56
+ throw new AggregateError([error, closeError], "staging directory admission and close failed");
57
+ }
58
+ throw error;
59
+ }
60
+ }
@@ -0,0 +1,56 @@
1
+ export type StagedFileReceipt = Readonly<{
2
+ directory: Readonly<{
3
+ path: string;
4
+ realPath: string;
5
+ identity: Readonly<{
6
+ dev: bigint;
7
+ ino: bigint;
8
+ }>;
9
+ }>;
10
+ temporaryBasename: string;
11
+ /** Preparation-time metadata of the private stage, not a final-file fingerprint. */
12
+ identity: Readonly<{
13
+ dev: bigint;
14
+ ino: bigint;
15
+ mode: number;
16
+ nlink: bigint;
17
+ size: bigint;
18
+ uid: number;
19
+ gid: number;
20
+ mtimeNs: bigint;
21
+ ctimeNs: bigint;
22
+ }>;
23
+ }>;
24
+ export type PublishedFileReceipt = Readonly<{
25
+ status: "published";
26
+ staged: StagedFileReceipt;
27
+ basename: string;
28
+ overwrite: boolean;
29
+ }>;
30
+ export type StagedFilePublication = Readonly<{
31
+ status: "not-published";
32
+ }> | PublishedFileReceipt | Readonly<{
33
+ status: "indeterminate";
34
+ basename: string;
35
+ overwrite: boolean;
36
+ }>;
37
+ export type StagedFileCleanupReceipt = Readonly<{
38
+ temporaryBasename: string;
39
+ publication: StagedFilePublication;
40
+ status: "removed" | "name-absent" | "preserved" | "failed" | "not-needed";
41
+ resources: "closed" | "close-failed";
42
+ }>;
43
+ export type StagedFileFailureDetails = Readonly<{
44
+ phase: "prepare" | "publish" | "cleanup";
45
+ publication: StagedFilePublication;
46
+ cleanup?: StagedFileCleanupReceipt;
47
+ }>;
48
+ export interface StagedFile extends AsyncDisposable {
49
+ readonly receipt: StagedFileReceipt;
50
+ assertCurrent(): Promise<void>;
51
+ publish(basename: string, options: {
52
+ overwrite: boolean;
53
+ }): Promise<PublishedFileReceipt>;
54
+ cleanup(): Promise<StagedFileCleanupReceipt>;
55
+ }
56
+ //# sourceMappingURL=staged-file-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staged-file-types.d.ts","sourceRoot":"","sources":["../src/staged-file-types.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG,QAAQ,CAAC;IACvC,SAAS,EAAE,QAAQ,CAAC;QAClB,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;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B,oFAAoF;IACpF,QAAQ,EAAE,QAAQ,CAAC;QACjB,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,EAAE,MAAM,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;CACJ,CAAC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,QAAQ,CAAC;IAC1C,MAAM,EAAE,WAAW,CAAC;IACpB,MAAM,EAAE,iBAAiB,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,OAAO,CAAC;CACpB,CAAC,CAAC;AAEH,MAAM,MAAM,qBAAqB,GAC7B,QAAQ,CAAC;IAAE,MAAM,EAAE,eAAe,CAAA;CAAE,CAAC,GACrC,oBAAoB,GACpB,QAAQ,CAAC;IAAE,MAAM,EAAE,eAAe,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,CAAC,CAAC;AAEhF,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,iBAAiB,EAAE,MAAM,CAAC;IAC1B,WAAW,EAAE,qBAAqB,CAAC;IACnC,MAAM,EAAE,SAAS,GAAG,aAAa,GAAG,WAAW,GAAG,QAAQ,GAAG,YAAY,CAAC;IAC1E,SAAS,EAAE,QAAQ,GAAG,cAAc,CAAC;CACtC,CAAC,CAAC;AAEH,MAAM,MAAM,wBAAwB,GAAG,QAAQ,CAAC;IAC9C,KAAK,EAAE,SAAS,GAAG,SAAS,GAAG,SAAS,CAAC;IACzC,WAAW,EAAE,qBAAqB,CAAC;IACnC,OAAO,CAAC,EAAE,wBAAwB,CAAC;CACpC,CAAC,CAAC;AAEH,MAAM,WAAW,UAAW,SAAQ,eAAe;IACjD,QAAQ,CAAC,OAAO,EAAE,iBAAiB,CAAC;IACpC,aAAa,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,SAAS,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAC1F,OAAO,IAAI,OAAO,CAAC,wBAAwB,CAAC,CAAC;CAC9C"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import type { DirectoryReceipt } from "./directory-durability.js";
2
+ import type { StagedFile } from "./staged-file-types.js";
3
+ export type { PublishedFileReceipt, StagedFile, StagedFileCleanupReceipt, StagedFileFailureDetails, StagedFilePublication, StagedFileReceipt, } from "./staged-file-types.js";
4
+ export declare function stageFileInDirectory(options: {
5
+ directory: string | DirectoryReceipt;
6
+ content: string | Uint8Array;
7
+ /** Published mode; the unpublished stage stays at 0600. Defaults to 0600. */
8
+ mode?: number;
9
+ }): Promise<StagedFile>;
10
+ //# sourceMappingURL=staged-file.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staged-file.d.ts","sourceRoot":"","sources":["../src/staged-file.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAKlE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAEzD,YAAY,EACV,oBAAoB,EAAE,UAAU,EAAE,wBAAwB,EAAE,wBAAwB,EACpF,qBAAqB,EAAE,iBAAiB,GACzC,MAAM,wBAAwB,CAAC;AAEhC,wBAAsB,oBAAoB,CAAC,OAAO,EAAE;IAClD,SAAS,EAAE,MAAM,GAAG,gBAAgB,CAAC;IACrC,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7B,6EAA6E;IAC7E,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,GAAG,OAAO,CAAC,UAAU,CAAC,CAUtB"}
@@ -0,0 +1,15 @@
1
+ import { FsSafeError } from "./errors.js";
2
+ import { assertNativeStaging, createNativeStage } from "./native-staged-file.js";
3
+ import { requireNativeBinding } from "./native.js";
4
+ import { openStagedDirectory } from "./staged-directory.js";
5
+ export async function stageFileInDirectory(options) {
6
+ if (process.platform !== "linux" && process.platform !== "darwin") {
7
+ throw new FsSafeError("unsupported-platform", "retained-directory staging requires Linux or macOS");
8
+ }
9
+ const binding = requireNativeBinding();
10
+ assertNativeStaging(binding);
11
+ const input = { kind: "buffer", data: Buffer.from(options.content) };
12
+ const mode = options.mode ?? 0o600;
13
+ const parent = openStagedDirectory(options.directory);
14
+ return await createNativeStage(binding, parent.fd, parent.receipt, input, mode);
15
+ }
@@ -0,0 +1,6 @@
1
+ import type { BigIntStats } from "node:fs";
2
+ type ExactFileIdentity = Pick<BigIntStats, "dev" | "ino">;
3
+ export declare function inspectFileIdentity<T extends ExactFileIdentity>(inspect: () => Promise<T>, expected?: ExactFileIdentity, platform?: NodeJS.Platform): Promise<T>;
4
+ export declare function inspectFileIdentitySync<T extends ExactFileIdentity>(inspect: () => T, expected?: ExactFileIdentity, platform?: NodeJS.Platform): T;
5
+ export {};
6
+ //# sourceMappingURL=strict-file-identity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strict-file-identity.d.ts","sourceRoot":"","sources":["../src/strict-file-identity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AAG3C,KAAK,iBAAiB,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,GAAG,KAAK,CAAC,CAAC;AA6B1D,wBAAsB,mBAAmB,CAAC,CAAC,SAAS,iBAAiB,EACnE,OAAO,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACzB,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,OAAO,CAAC,CAAC,CAAC,CAOZ;AAED,wBAAgB,uBAAuB,CAAC,CAAC,SAAS,iBAAiB,EACjE,OAAO,EAAE,MAAM,CAAC,EAChB,QAAQ,CAAC,EAAE,iBAAiB,EAC5B,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC3C,CAAC,CAOH"}
@@ -0,0 +1,48 @@
1
+ import { FsSafeError } from "./errors.js";
2
+ function identityMismatch() {
3
+ return new FsSafeError("path-mismatch", "file identity changed or could not be verified");
4
+ }
5
+ function identityCheck(expected, platform) {
6
+ const known = {};
7
+ const check = (stat) => {
8
+ let complete = true;
9
+ for (const field of ["dev", "ino"]) {
10
+ const value = stat[field];
11
+ // Numeric receipts cannot recover identity bits already lost to rounding.
12
+ if (typeof value !== "bigint")
13
+ throw identityMismatch();
14
+ if (platform === "win32" && value === 0n) {
15
+ complete = false;
16
+ }
17
+ else {
18
+ if (known[field] !== undefined && known[field] !== value)
19
+ throw identityMismatch();
20
+ known[field] = value;
21
+ }
22
+ }
23
+ return complete;
24
+ };
25
+ if (expected && !check(expected))
26
+ throw identityMismatch();
27
+ return check;
28
+ }
29
+ // Retry only unknown Windows identities, retaining every known component so a
30
+ // later observation cannot erase a definite mismatch. Never reopen the file.
31
+ export async function inspectFileIdentity(inspect, expected, platform = process.platform) {
32
+ const check = identityCheck(expected, platform);
33
+ for (let attempt = 0; attempt < 2; attempt++) {
34
+ const stat = await inspect();
35
+ if (check(stat))
36
+ return stat;
37
+ }
38
+ throw identityMismatch();
39
+ }
40
+ export function inspectFileIdentitySync(inspect, expected, platform = process.platform) {
41
+ const check = identityCheck(expected, platform);
42
+ for (let attempt = 0; attempt < 2; attempt++) {
43
+ const stat = inspect();
44
+ if (check(stat))
45
+ return stat;
46
+ }
47
+ throw identityMismatch();
48
+ }
@@ -0,0 +1,6 @@
1
+ export type SuppressedErrorLike = Error & {
2
+ error: unknown;
3
+ suppressed: unknown;
4
+ };
5
+ export declare function createSuppressedError(error: unknown, suppressed: unknown, message: string): SuppressedErrorLike;
6
+ //# sourceMappingURL=suppressed-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"suppressed-error.d.ts","sourceRoot":"","sources":["../src/suppressed-error.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,mBAAmB,GAAG,KAAK,GAAG;IACxC,KAAK,EAAE,OAAO,CAAC;IACf,UAAU,EAAE,OAAO,CAAC;CACrB,CAAC;AAQF,wBAAgB,qBAAqB,CACnC,KAAK,EAAE,OAAO,EACd,UAAU,EAAE,OAAO,EACnB,OAAO,EAAE,MAAM,GACd,mBAAmB,CAgBrB"}
@@ -0,0 +1,15 @@
1
+ export function createSuppressedError(error, suppressed, message) {
2
+ const constructor = globalThis.SuppressedError;
3
+ if (constructor) {
4
+ return new constructor(error, suppressed, message);
5
+ }
6
+ const combined = new Error(message, {
7
+ cause: error instanceof Error ? error : undefined,
8
+ });
9
+ combined.name = "SuppressedError";
10
+ Object.defineProperties(combined, {
11
+ error: { configurable: true, value: error },
12
+ suppressed: { configurable: true, value: suppressed },
13
+ });
14
+ return combined;
15
+ }
@@ -6,6 +6,8 @@ export type TempPathRegistration = (() => void) & {
6
6
  export declare function registerTempPathForExit(tempPath: string, options?: {
7
7
  recursive?: boolean;
8
8
  identity?: FileIdentityStat;
9
+ singleLinkFile?: boolean;
10
+ cleanupSync?: () => unknown;
9
11
  }): TempPathRegistration;
10
12
  export declare function __cleanupRegisteredTempPathsForTest(): void;
11
13
  export declare function __cleanupRegisteredTempPathForTest(tempPath: string): void;
@@ -1 +1 @@
1
- {"version":3,"file":"temp-cleanup.d.ts","sourceRoot":"","sources":["../src/temp-cleanup.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEvF,MAAM,MAAM,uBAAuB,GAAG,gBAAgB,CAAC;AAEvD,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG;IAChD,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC/C,CAAC;AAsCF,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IAAE,SAAS,CAAC,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,EAAE,gBAAgB,CAAA;CAAE,GAC7D,oBAAoB,CAyBtB;AAED,wBAAgB,mCAAmC,IAAI,IAAI,CAE1D;AAED,wBAAgB,kCAAkC,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAYzE"}
1
+ {"version":3,"file":"temp-cleanup.d.ts","sourceRoot":"","sources":["../src/temp-cleanup.ts"],"names":[],"mappings":"AACA,OAAO,EAA8B,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEvF,MAAM,MAAM,uBAAuB,GAAG,gBAAgB,CAAC;AAEvD,MAAM,MAAM,oBAAoB,GAAG,CAAC,MAAM,IAAI,CAAC,GAAG;IAChD,WAAW,CAAC,QAAQ,EAAE,gBAAgB,GAAG,IAAI,CAAC;CAC/C,CAAC;AAkDF,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE;IACR,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC;CAC7B,GACA,oBAAoB,CA2BtB;AAED,wBAAgB,mCAAmC,IAAI,IAAI,CAE1D;AAED,wBAAgB,kCAAkC,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAUzE"}
@@ -7,18 +7,32 @@ function pathStillMatchesReceipt(entry) {
7
7
  return false;
8
8
  }
9
9
  try {
10
- return sameFileIdentityForCleanup(fsSync.lstatSync(entry.path, { bigint: true }), entry.identity);
10
+ const current = fsSync.lstatSync(entry.path, { bigint: true });
11
+ return (!entry.singleLinkFile || (current.isFile() && current.nlink === 1n)) &&
12
+ sameFileIdentityForCleanup(current, entry.identity);
11
13
  }
12
14
  catch (error) {
13
- return error.code === "ENOENT";
15
+ return !entry.singleLinkFile && error.code === "ENOENT";
16
+ }
17
+ }
18
+ function removeRegisteredPathSync(entry) {
19
+ if (entry.singleLinkFile)
20
+ fsSync.unlinkSync(entry.path);
21
+ else
22
+ fsSync.rmSync(entry.path, { force: true, recursive: entry.recursive });
23
+ }
24
+ function cleanupEntrySync(entry) {
25
+ if (entry.cleanupSync) {
26
+ entry.cleanupSync();
27
+ }
28
+ else if (pathStillMatchesReceipt(entry)) {
29
+ removeRegisteredPathSync(entry);
14
30
  }
15
31
  }
16
32
  function cleanupRegisteredTempPathsSync() {
17
33
  for (const entry of tempCleanupEntries.values()) {
18
34
  try {
19
- if (pathStillMatchesReceipt(entry)) {
20
- fsSync.rmSync(entry.path, { force: true, recursive: entry.recursive });
21
- }
35
+ cleanupEntrySync(entry);
22
36
  }
23
37
  catch {
24
38
  // Process-exit cleanup is best-effort.
@@ -35,8 +49,10 @@ export function registerTempPathForExit(tempPath, options) {
35
49
  path: tempPath,
36
50
  recursive: options?.recursive === true,
37
51
  identity: options?.identity,
52
+ singleLinkFile: options?.singleLinkFile,
53
+ cleanupSync: options?.cleanupSync,
38
54
  };
39
- if (!entry.identity) {
55
+ if (!entry.identity && !entry.cleanupSync) {
40
56
  try {
41
57
  entry.identity = fsSync.lstatSync(tempPath, { bigint: true });
42
58
  }
@@ -46,7 +62,8 @@ export function registerTempPathForExit(tempPath, options) {
46
62
  }
47
63
  tempCleanupEntries.set(tempPath, entry);
48
64
  const unregister = (() => {
49
- tempCleanupEntries.delete(tempPath);
65
+ if (tempCleanupEntries.get(tempPath) === entry)
66
+ tempCleanupEntries.delete(tempPath);
50
67
  });
51
68
  unregister.setIdentity = (identity) => {
52
69
  entry.identity = identity;
@@ -62,9 +79,7 @@ export function __cleanupRegisteredTempPathForTest(tempPath) {
62
79
  return;
63
80
  }
64
81
  try {
65
- if (pathStillMatchesReceipt(entry)) {
66
- fsSync.rmSync(entry.path, { force: true, recursive: entry.recursive });
67
- }
82
+ cleanupEntrySync(entry);
68
83
  }
69
84
  finally {
70
85
  tempCleanupEntries.delete(tempPath);
@@ -0,0 +1,23 @@
1
+ import { type FileIdentityStat } from "./file-identity.js";
2
+ import { type NativeBinding } from "./native.js";
3
+ import { openStagedDirectory } from "./staged-directory.js";
4
+ export type TempWorkspaceCleanupResult = "removed" | "missing" | "identity-mismatch" | "indeterminate";
5
+ export type TempWorkspaceCleanupSafety = "compatible" | "require-bounded";
6
+ type RetainedDirectory = ReturnType<typeof openStagedDirectory>;
7
+ export declare class TempWorkspaceCleanupCapability {
8
+ #private;
9
+ readonly binding: NativeBinding | undefined;
10
+ readonly parent: RetainedDirectory | undefined;
11
+ constructor(root: string, safety: TempWorkspaceCleanupSafety);
12
+ get canRemoveOwnedTree(): boolean;
13
+ assertCurrent(): void;
14
+ close(): void;
15
+ }
16
+ export declare class TempWorkspaceCleanupOwner {
17
+ #private;
18
+ constructor(dir: string, identity: FileIdentityStat, capability: TempWorkspaceCleanupCapability);
19
+ cleanup(): Promise<TempWorkspaceCleanupResult>;
20
+ cleanupSync(): TempWorkspaceCleanupResult;
21
+ }
22
+ export {};
23
+ //# sourceMappingURL=temp-workspace-owner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"temp-workspace-owner.d.ts","sourceRoot":"","sources":["../src/temp-workspace-owner.ts"],"names":[],"mappings":"AAMA,OAAO,EAA8B,KAAK,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEvF,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAEnE,OAAO,EAAgC,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAG1F,MAAM,MAAM,0BAA0B,GAAG,SAAS,GAAG,SAAS,GAAG,mBAAmB,GAAG,eAAe,CAAC;AACvG,MAAM,MAAM,0BAA0B,GAAG,YAAY,GAAG,iBAAiB,CAAC;AAE1E,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAoBhE,qBAAa,8BAA8B;;IACzC,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,SAAS,CAAC;IAI/C,YAAY,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,0BAA0B,EAiC3D;IAED,IAAI,kBAAkB,IAAI,OAAO,CAEhC;IAED,aAAa,IAAI,IAAI,CASpB;IAED,KAAK,IAAI,IAAI,CAIZ;CACF;AAED,qBAAa,yBAAyB;;IAWpC,YAAY,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,UAAU,EAAE,8BAA8B,EAe9F;IAuLD,OAAO,IAAI,OAAO,CAAC,0BAA0B,CAAC,CAM7C;IAED,WAAW,IAAI,0BAA0B,CAexC;CACF"}