@openclaw/fs-safe 0.10.0 → 0.11.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 (259) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/LICENSE +1 -0
  3. package/README.md +36 -5
  4. package/dist/absolute-path.d.ts.map +1 -1
  5. package/dist/absolute-path.js +3 -2
  6. package/dist/advanced.d.ts +4 -0
  7. package/dist/advanced.d.ts.map +1 -1
  8. package/dist/advanced.js +4 -0
  9. package/dist/archive-durability.d.ts +6 -6
  10. package/dist/archive-durability.d.ts.map +1 -1
  11. package/dist/archive-durability.js +1 -1
  12. package/dist/archive-entry.d.ts.map +1 -1
  13. package/dist/archive-entry.js +4 -5
  14. package/dist/archive-gzip-tail.d.ts +1 -0
  15. package/dist/archive-gzip-tail.d.ts.map +1 -1
  16. package/dist/archive-gzip-tail.js +3 -0
  17. package/dist/archive-input.d.ts.map +1 -1
  18. package/dist/archive-input.js +4 -2
  19. package/dist/archive-merge.d.ts +5 -1
  20. package/dist/archive-merge.d.ts.map +1 -1
  21. package/dist/archive-merge.js +15 -12
  22. package/dist/archive-native.js +4 -4
  23. package/dist/archive-parser.wasm +0 -0
  24. package/dist/archive-read.d.ts.map +1 -1
  25. package/dist/archive-read.js +17 -9
  26. package/dist/archive-staging.d.ts +6 -3
  27. package/dist/archive-staging.d.ts.map +1 -1
  28. package/dist/archive-staging.js +42 -22
  29. package/dist/archive-tar-stream.d.ts.map +1 -1
  30. package/dist/archive-tar-stream.js +7 -6
  31. package/dist/archive-tar-wasm.d.ts.map +1 -1
  32. package/dist/archive-tar-wasm.js +16 -13
  33. package/dist/archive-zip-admission.d.ts +1 -1
  34. package/dist/archive-zip-admission.d.ts.map +1 -1
  35. package/dist/archive-zip-admission.js +48 -12
  36. package/dist/archive-zip-loader.d.ts +6 -0
  37. package/dist/archive-zip-loader.d.ts.map +1 -0
  38. package/dist/archive-zip-loader.js +38 -0
  39. package/dist/archive-zip-names.d.ts.map +1 -1
  40. package/dist/archive-zip-names.js +13 -8
  41. package/dist/archive-zip-preflight.d.ts +2 -3
  42. package/dist/archive-zip-preflight.d.ts.map +1 -1
  43. package/dist/archive-zip-preflight.js +2 -34
  44. package/dist/archive.d.ts.map +1 -1
  45. package/dist/archive.js +12 -10
  46. package/dist/bounded-read.d.ts +5 -0
  47. package/dist/bounded-read.d.ts.map +1 -1
  48. package/dist/bounded-read.js +16 -9
  49. package/dist/copy-file-input.d.ts +0 -1
  50. package/dist/copy-file-input.d.ts.map +1 -1
  51. package/dist/copy-file-input.js +4 -26
  52. package/dist/copy-tree-portable.d.ts.map +1 -1
  53. package/dist/copy-tree-portable.js +57 -26
  54. package/dist/copy.d.ts +1 -1
  55. package/dist/copy.d.ts.map +1 -1
  56. package/dist/copy.js +3 -1
  57. package/dist/directory-durability.d.ts.map +1 -1
  58. package/dist/directory-durability.js +5 -4
  59. package/dist/directory-guard.d.ts +11 -1
  60. package/dist/directory-guard.d.ts.map +1 -1
  61. package/dist/directory-guard.js +53 -11
  62. package/dist/durability.d.ts +1 -1
  63. package/dist/durability.d.ts.map +1 -1
  64. package/dist/durability.js +1 -1
  65. package/dist/file-handle-transfer.d.ts +14 -0
  66. package/dist/file-handle-transfer.d.ts.map +1 -0
  67. package/dist/file-handle-transfer.js +64 -0
  68. package/dist/file-hash.d.ts +3 -0
  69. package/dist/file-hash.d.ts.map +1 -1
  70. package/dist/file-hash.js +91 -31
  71. package/dist/file-lock-sync.d.ts.map +1 -1
  72. package/dist/file-lock-sync.js +8 -4
  73. package/dist/file-store-boundary.d.ts.map +1 -1
  74. package/dist/file-store-boundary.js +7 -5
  75. package/dist/file-store-path.d.ts +3 -0
  76. package/dist/file-store-path.d.ts.map +1 -0
  77. package/dist/file-store-path.js +27 -0
  78. package/dist/file-store-prune.d.ts.map +1 -1
  79. package/dist/file-store-prune.js +6 -4
  80. package/dist/file-store-sync-write.d.ts.map +1 -1
  81. package/dist/file-store-sync-write.js +56 -44
  82. package/dist/file-store.d.ts.map +1 -1
  83. package/dist/file-store.js +2 -18
  84. package/dist/filename.d.ts.map +1 -1
  85. package/dist/filename.js +2 -1
  86. package/dist/guarded-mkdir.d.ts.map +1 -1
  87. package/dist/guarded-mkdir.js +3 -2
  88. package/dist/guest-dispatch-python.d.ts +2 -0
  89. package/dist/guest-dispatch-python.d.ts.map +1 -0
  90. package/dist/guest-dispatch-python.js +117 -0
  91. package/dist/guest-native-python.d.ts +4 -0
  92. package/dist/guest-native-python.d.ts.map +1 -0
  93. package/dist/guest-native-python.js +135 -0
  94. package/dist/guest.d.ts +9 -0
  95. package/dist/guest.d.ts.map +1 -0
  96. package/dist/guest.js +413 -0
  97. package/dist/index.d.ts +1 -1
  98. package/dist/index.d.ts.map +1 -1
  99. package/dist/install-path.d.ts.map +1 -1
  100. package/dist/install-path.js +3 -2
  101. package/dist/json-durable-queue-directory.js +3 -3
  102. package/dist/json-durable-queue.d.ts.map +1 -1
  103. package/dist/json-durable-queue.js +6 -4
  104. package/dist/json.d.ts.map +1 -1
  105. package/dist/json.js +2 -1
  106. package/dist/local-roots.d.ts.map +1 -1
  107. package/dist/local-roots.js +2 -1
  108. package/dist/move-path-stage.d.ts.map +1 -1
  109. package/dist/move-path-stage.js +2 -1
  110. package/dist/move-path.d.ts.map +1 -1
  111. package/dist/move-path.js +4 -3
  112. package/dist/mutation-authority.d.ts +1 -0
  113. package/dist/mutation-authority.d.ts.map +1 -1
  114. package/dist/mutation-authority.js +4 -4
  115. package/dist/native-binding.d.ts +7 -2
  116. package/dist/native-binding.d.ts.map +1 -1
  117. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  118. package/dist/native-pinned-write-windows.js +3 -2
  119. package/dist/native-pinned-write.d.ts.map +1 -1
  120. package/dist/native-pinned-write.js +2 -1
  121. package/dist/opened-realpath.d.ts.map +1 -1
  122. package/dist/opened-realpath.js +5 -4
  123. package/dist/output.d.ts +2 -0
  124. package/dist/output.d.ts.map +1 -1
  125. package/dist/output.js +2 -0
  126. package/dist/overwrite-file-handle.d.ts +8 -0
  127. package/dist/overwrite-file-handle.d.ts.map +1 -0
  128. package/dist/overwrite-file-handle.js +42 -0
  129. package/dist/path-case.d.ts +7 -0
  130. package/dist/path-case.d.ts.map +1 -0
  131. package/dist/path-case.js +136 -0
  132. package/dist/path.d.ts.map +1 -1
  133. package/dist/path.js +2 -1
  134. package/dist/permissions-windows.d.ts +1 -1
  135. package/dist/permissions-windows.d.ts.map +1 -1
  136. package/dist/permissions-windows.js +48 -6
  137. package/dist/pinned-open.d.ts.map +1 -1
  138. package/dist/pinned-open.js +3 -1
  139. package/dist/pinned-write.d.ts +2 -2
  140. package/dist/pinned-write.d.ts.map +1 -1
  141. package/dist/pinned-write.js +2 -1
  142. package/dist/private-temp-workspace.d.ts.map +1 -1
  143. package/dist/private-temp-workspace.js +6 -4
  144. package/dist/realpath.d.ts +4 -0
  145. package/dist/realpath.d.ts.map +1 -0
  146. package/dist/realpath.js +43 -0
  147. package/dist/recursive-mkdir-path.d.ts +3 -0
  148. package/dist/recursive-mkdir-path.d.ts.map +1 -0
  149. package/dist/recursive-mkdir-path.js +8 -0
  150. package/dist/replace-directory.d.ts.map +1 -1
  151. package/dist/replace-directory.js +2 -1
  152. package/dist/replace-file-copy-fallback.d.ts.map +1 -1
  153. package/dist/replace-file-copy-fallback.js +23 -31
  154. package/dist/replace-file-copy-source.d.ts.map +1 -1
  155. package/dist/replace-file-copy-source.js +7 -12
  156. package/dist/replace-file-mode.d.ts +3 -0
  157. package/dist/replace-file-mode.d.ts.map +1 -0
  158. package/dist/replace-file-mode.js +10 -0
  159. package/dist/replace-file.d.ts +1 -0
  160. package/dist/replace-file.d.ts.map +1 -1
  161. package/dist/replace-file.js +14 -8
  162. package/dist/root-context.d.ts.map +1 -1
  163. package/dist/root-context.js +8 -8
  164. package/dist/root-create-input.d.ts +10 -0
  165. package/dist/root-create-input.d.ts.map +1 -0
  166. package/dist/root-create-input.js +80 -0
  167. package/dist/root-directory-list.d.ts +3 -1
  168. package/dist/root-directory-list.d.ts.map +1 -1
  169. package/dist/root-directory-list.js +21 -3
  170. package/dist/root-entries.d.ts +11 -0
  171. package/dist/root-entries.d.ts.map +1 -0
  172. package/dist/root-entries.js +61 -0
  173. package/dist/root-errors.d.ts +5 -5
  174. package/dist/root-errors.d.ts.map +1 -1
  175. package/dist/root-errors.js +13 -12
  176. package/dist/root-impl.d.ts +13 -3
  177. package/dist/root-impl.d.ts.map +1 -1
  178. package/dist/root-impl.js +40 -36
  179. package/dist/root-options.d.ts +12 -1
  180. package/dist/root-options.d.ts.map +1 -1
  181. package/dist/root-path-existing.d.ts.map +1 -1
  182. package/dist/root-path-existing.js +4 -3
  183. package/dist/root-path-symlink.d.ts.map +1 -1
  184. package/dist/root-path-symlink.js +3 -2
  185. package/dist/root-paths.d.ts.map +1 -1
  186. package/dist/root-paths.js +13 -9
  187. package/dist/root-remove.d.ts +5 -0
  188. package/dist/root-remove.d.ts.map +1 -0
  189. package/dist/root-remove.js +286 -0
  190. package/dist/root-symlink-policy.d.ts +2 -1
  191. package/dist/root-symlink-policy.d.ts.map +1 -1
  192. package/dist/root-symlink-policy.js +2 -2
  193. package/dist/root-write-mode.d.ts.map +1 -1
  194. package/dist/root-write-mode.js +2 -1
  195. package/dist/root.d.ts +2 -1
  196. package/dist/root.d.ts.map +1 -1
  197. package/dist/secret-file.d.ts.map +1 -1
  198. package/dist/secret-file.js +2 -1
  199. package/dist/secret-read-async.d.ts.map +1 -1
  200. package/dist/secret-read-async.js +2 -1
  201. package/dist/secure-file.d.ts.map +1 -1
  202. package/dist/secure-file.js +21 -4
  203. package/dist/secure-temp-dir.d.ts.map +1 -1
  204. package/dist/secure-temp-dir.js +2 -1
  205. package/dist/sibling-staged-file.d.ts +1 -0
  206. package/dist/sibling-staged-file.d.ts.map +1 -1
  207. package/dist/sibling-staged-file.js +42 -8
  208. package/dist/sibling-temp.d.ts +2 -0
  209. package/dist/sibling-temp.d.ts.map +1 -1
  210. package/dist/sibling-temp.js +6 -4
  211. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  212. package/dist/sidecar-lock-acquire.js +15 -4
  213. package/dist/sidecar-lock-policy.d.ts +2 -0
  214. package/dist/sidecar-lock-policy.d.ts.map +1 -1
  215. package/dist/sidecar-lock-policy.js +17 -0
  216. package/dist/staged-directory.d.ts.map +1 -1
  217. package/dist/staged-directory.js +4 -3
  218. package/dist/temp-target.d.ts +14 -12
  219. package/dist/temp-target.d.ts.map +1 -1
  220. package/dist/temp-target.js +12 -6
  221. package/dist/trash.d.ts.map +1 -1
  222. package/dist/trash.js +7 -5
  223. package/dist/unicode-path.d.ts +3 -0
  224. package/dist/unicode-path.d.ts.map +1 -0
  225. package/dist/unicode-path.js +13 -0
  226. package/dist/walk.d.ts.map +1 -1
  227. package/dist/walk.js +3 -2
  228. package/dist/write-file-handle.d.ts +1 -0
  229. package/dist/write-file-handle.d.ts.map +1 -1
  230. package/dist/write-file-handle.js +3 -2
  231. package/docs/advanced.md +4 -0
  232. package/docs/archive.md +25 -5
  233. package/docs/atomic.md +17 -1
  234. package/docs/config.md +1 -0
  235. package/docs/contributing.md +29 -1
  236. package/docs/copy.md +75 -6
  237. package/docs/directory-identity.md +85 -0
  238. package/docs/durability.md +36 -1
  239. package/docs/entries.md +109 -0
  240. package/docs/errors.md +3 -3
  241. package/docs/file-store.md +15 -0
  242. package/docs/guest.md +141 -0
  243. package/docs/in-place-write.md +81 -0
  244. package/docs/index.md +2 -0
  245. package/docs/install.md +31 -0
  246. package/docs/native-helper.md +10 -3
  247. package/docs/native.md +4 -0
  248. package/docs/output.md +32 -6
  249. package/docs/path-case.md +64 -0
  250. package/docs/path-scope.md +1 -1
  251. package/docs/permissions.md +11 -2
  252. package/docs/public-api.md +31 -2
  253. package/docs/root.md +30 -2
  254. package/docs/secure-file.md +2 -0
  255. package/docs/sidecar-lock.md +12 -3
  256. package/docs/temp.md +35 -6
  257. package/docs/types.md +1 -1
  258. package/docs/writing.md +153 -3
  259. package/package.json +14 -8
@@ -0,0 +1,14 @@
1
+ import type { FileHandle } from "node:fs/promises";
2
+ export type CopyFileHandleOptions = {
3
+ maxBytes?: number;
4
+ signal?: AbortSignal;
5
+ onChunk?: (chunk: Uint8Array) => void;
6
+ assertBeforeMutation?: () => void;
7
+ };
8
+ /** Copies caller-owned regular files from offset zero without moving either cursor. */
9
+ export declare function copyFileHandle(source: FileHandle, target: FileHandle, options?: CopyFileHandleOptions): Promise<number>;
10
+ export declare function transferFileHandle(source: FileHandle, target: FileHandle | number, options: CopyFileHandleOptions & {
11
+ sizeHint: number;
12
+ targetPosition?: number;
13
+ }): Promise<number>;
14
+ //# sourceMappingURL=file-handle-transfer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-handle-transfer.d.ts","sourceRoot":"","sources":["../src/file-handle-transfer.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAWnD,MAAM,MAAM,qBAAqB,GAAG;IAClC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;IACtC,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;CACnC,CAAC;AAEF,uFAAuF;AACvF,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,UAAU,EAClB,OAAO,GAAE,qBAA0B,GAClC,OAAO,CAAC,MAAM,CAAC,CA4BjB;AAGD,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,UAAU,GAAG,MAAM,EAC3B,OAAO,EAAE,qBAAqB,GAAG;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,cAAc,CAAC,EAAE,MAAM,CAAA;CAAE,GAC7E,OAAO,CAAC,MAAM,CAAC,CAyBjB"}
@@ -0,0 +1,64 @@
1
+ import { normalizeMaxBytes } from "./byte-budget.js";
2
+ import { FsSafeError } from "./errors.js";
3
+ import { assertSynchronousCallbackResult, composeMutationAssertions, rethrowMutationAuthorityError, } from "./mutation-authority.js";
4
+ import { writeAllToFile } from "./write-file-handle.js";
5
+ import { inspectFileIdentity } from "./strict-file-identity.js";
6
+ /** Copies caller-owned regular files from offset zero without moving either cursor. */
7
+ export async function copyFileHandle(source, target, options = {}) {
8
+ options.signal?.throwIfAborted();
9
+ const maxBytes = normalizeMaxBytes(options.maxBytes);
10
+ const sourceStat = await inspectFileIdentity(() => {
11
+ options.signal?.throwIfAborted();
12
+ return source.stat({ bigint: true });
13
+ });
14
+ options.signal?.throwIfAborted();
15
+ if (!sourceStat.isFile())
16
+ throw new FsSafeError("not-file", "copy source handle must be a regular file");
17
+ if (maxBytes !== undefined && Number.isFinite(maxBytes) && sourceStat.size > BigInt(maxBytes)) {
18
+ throw new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes`);
19
+ }
20
+ const targetStat = await inspectFileIdentity(() => {
21
+ options.signal?.throwIfAborted();
22
+ return target.stat({ bigint: true });
23
+ });
24
+ options.signal?.throwIfAborted();
25
+ if (!targetStat.isFile())
26
+ throw new FsSafeError("not-file", "copy target handle must be a regular file");
27
+ if (sourceStat.dev === targetStat.dev && sourceStat.ino === targetStat.ino) {
28
+ throw new FsSafeError("path-alias", "copy source and target handles refer to the same file");
29
+ }
30
+ return await transferFileHandle(source, target, {
31
+ ...options,
32
+ maxBytes,
33
+ sizeHint: Number(sourceStat.size),
34
+ targetPosition: 0,
35
+ assertBeforeMutation: composeMutationAssertions(undefined, options.assertBeforeMutation),
36
+ }).catch(rethrowMutationAuthorityError);
37
+ }
38
+ // Root.copyIn supplies already-admitted identities and retains its owned target cursor.
39
+ export async function transferFileHandle(source, target, options) {
40
+ const maxBytes = options.maxBytes ?? Infinity;
41
+ const buffer = Buffer.allocUnsafe(Math.min(512 * 1024, Math.max(64 * 1024, options.sizeHint), maxBytes + 1));
42
+ let position = 0;
43
+ while (true) {
44
+ options.signal?.throwIfAborted();
45
+ const { bytesRead } = await source.read(buffer, 0, Math.min(buffer.length, maxBytes - position + 1), position);
46
+ options.signal?.throwIfAborted();
47
+ if (bytesRead > maxBytes - position) {
48
+ throw new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes`);
49
+ }
50
+ if (bytesRead === 0)
51
+ return position;
52
+ const chunk = buffer.subarray(0, bytesRead);
53
+ if (options.onChunk)
54
+ assertSynchronousCallbackResult(options.onChunk(chunk), "onChunk");
55
+ await writeAllToFile(target, chunk, {
56
+ position: options.targetPosition === undefined ? undefined : options.targetPosition + position,
57
+ assertBeforeMutation: () => {
58
+ options.signal?.throwIfAborted();
59
+ options.assertBeforeMutation?.();
60
+ },
61
+ });
62
+ position += bytesRead;
63
+ }
64
+ }
@@ -1,6 +1,7 @@
1
1
  import type { FileHandle } from "node:fs/promises";
2
2
  import { type NativeBinding } from "./native.js";
3
3
  export type Sha256FileInput = string | FileHandle;
4
+ export type Sha256FileSyncInput = string | number;
4
5
  export type Sha256FileOptions = {
5
6
  maxBytes?: number;
6
7
  signal?: AbortSignal;
@@ -11,4 +12,6 @@ export type Sha256FileResult = {
11
12
  };
12
13
  export declare function hashFileHandle(handle: FileHandle, native?: NativeBinding | undefined, { maxBytes, signal }?: Sha256FileOptions): Promise<Sha256FileResult>;
13
14
  export declare function sha256File(input: Sha256FileInput, options?: Sha256FileOptions): Promise<Sha256FileResult>;
15
+ /** Hash synchronously from offset zero, retaining ownership and position of a borrowed fd. */
16
+ export declare function sha256FileSync(input: Sha256FileSyncInput, options?: Sha256FileOptions): Sha256FileResult;
14
17
  //# sourceMappingURL=file-hash.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"file-hash.d.ts","sourceRoot":"","sources":["../src/file-hash.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAInE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,UAAU,CAAC;AAElD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,MAAM,GAAE,aAAa,GAAG,SAA8B,EACtD,EAAE,QAAmB,EAAE,MAAM,EAAE,GAAE,iBAAsB,GACtD,OAAO,CAAC,gBAAgB,CAAC,CAoD3B;AAoDD,wBAAsB,UAAU,CAC9B,KAAK,EAAE,eAAe,EACtB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAM3B"}
1
+ {"version":3,"file":"file-hash.d.ts","sourceRoot":"","sources":["../src/file-hash.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAInD,OAAO,EAAoB,KAAK,aAAa,EAAE,MAAM,aAAa,CAAC;AAInE,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,UAAU,CAAC;AAClD,MAAM,MAAM,mBAAmB,GAAG,MAAM,GAAG,MAAM,CAAC;AAElD,MAAM,MAAM,iBAAiB,GAAG;IAC9B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF,wBAAsB,cAAc,CAClC,MAAM,EAAE,UAAU,EAClB,MAAM,GAAE,aAAa,GAAG,SAA8B,EACtD,EAAE,QAAmB,EAAE,MAAM,EAAE,GAAE,iBAAsB,GACtD,OAAO,CAAC,gBAAgB,CAAC,CAoD3B;AAyDD,wBAAsB,UAAU,CAC9B,KAAK,EAAE,eAAe,EACtB,OAAO,GAAE,iBAAsB,GAC9B,OAAO,CAAC,gBAAgB,CAAC,CAM3B;AAoDD,8FAA8F;AAC9F,wBAAgB,cAAc,CAC5B,KAAK,EAAE,mBAAmB,EAC1B,OAAO,GAAE,iBAAsB,GAC9B,gBAAgB,CAMlB"}
package/dist/file-hash.js CHANGED
@@ -5,7 +5,7 @@ import { normalizeMaxBytes } from "./byte-budget.js";
5
5
  import { FsSafeError } from "./errors.js";
6
6
  import { getNativeBinding } from "./native.js";
7
7
  import { resolveReadOpenFlags } from "./read-open-flags.js";
8
- import { inspectFileIdentity } from "./strict-file-identity.js";
8
+ import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
9
9
  export async function hashFileHandle(handle, native = getNativeBinding(), { maxBytes = Infinity, signal } = {}) {
10
10
  signal?.throwIfAborted();
11
11
  const stat = fsSync.fstatSync(handle.fd);
@@ -55,46 +55,48 @@ export async function hashFileHandle(handle, native = getNativeBinding(), { maxB
55
55
  position += bytesRead;
56
56
  }
57
57
  }
58
+ function hashPathIdentity(filePath, afterOpen = false) {
59
+ const stat = fsSync.lstatSync(filePath, { bigint: true });
60
+ if (afterOpen && (stat.isSymbolicLink() || !stat.isFile())) {
61
+ throw new FsSafeError("path-mismatch", "SHA-256 path changed while opening");
62
+ }
63
+ if (stat.isSymbolicLink()) {
64
+ throw new FsSafeError("symlink", "SHA-256 path must not be a symbolic link");
65
+ }
66
+ if (!stat.isFile()) {
67
+ throw new FsSafeError("not-file", "SHA-256 path is not a regular file");
68
+ }
69
+ return stat;
70
+ }
71
+ function hashDescriptorIdentity(fd) {
72
+ const stat = fsSync.fstatSync(fd, { bigint: true });
73
+ if (!stat.isFile()) {
74
+ throw new FsSafeError("not-file", "SHA-256 path is not a regular file");
75
+ }
76
+ return stat;
77
+ }
78
+ function rethrowHashOpenError(error) {
79
+ if (error?.code === "ELOOP") {
80
+ throw new FsSafeError("symlink", "SHA-256 path must not be a symbolic link", {
81
+ cause: error,
82
+ });
83
+ }
84
+ throw error;
85
+ }
58
86
  async function hashPath(filePath, options) {
59
- const before = await inspectFileIdentity(async () => {
60
- const stat = fsSync.lstatSync(filePath, { bigint: true });
61
- if (stat.isSymbolicLink()) {
62
- throw new FsSafeError("symlink", "SHA-256 path must not be a symbolic link");
63
- }
64
- if (!stat.isFile()) {
65
- throw new FsSafeError("not-file", "SHA-256 path is not a regular file");
66
- }
67
- return stat;
68
- });
87
+ const before = await inspectFileIdentity(() => hashPathIdentity(filePath));
69
88
  let handle;
70
89
  try {
71
90
  options.signal?.throwIfAborted();
72
91
  handle = await fs.open(filePath, resolveReadOpenFlags());
73
92
  }
74
93
  catch (error) {
75
- if (error?.code === "ELOOP") {
76
- throw new FsSafeError("symlink", "SHA-256 path must not be a symbolic link", {
77
- cause: error,
78
- });
79
- }
80
- throw error;
94
+ rethrowHashOpenError(error);
81
95
  }
82
96
  try {
83
97
  options.signal?.throwIfAborted();
84
- const opened = await inspectFileIdentity(async () => {
85
- const stat = fsSync.fstatSync(handle.fd, { bigint: true });
86
- if (!stat.isFile()) {
87
- throw new FsSafeError("not-file", "SHA-256 path is not a regular file");
88
- }
89
- return stat;
90
- }, before);
91
- await inspectFileIdentity(async () => {
92
- const stat = fsSync.lstatSync(filePath, { bigint: true });
93
- if (stat.isSymbolicLink() || !stat.isFile()) {
94
- throw new FsSafeError("path-mismatch", "SHA-256 path changed while opening");
95
- }
96
- return stat;
97
- }, opened);
98
+ const opened = await inspectFileIdentity(() => hashDescriptorIdentity(handle.fd), before);
99
+ await inspectFileIdentity(() => hashPathIdentity(filePath, true), opened);
98
100
  return await hashFileHandle(handle, getNativeBinding(), options);
99
101
  }
100
102
  finally {
@@ -108,3 +110,61 @@ export async function sha256File(input, options = {}) {
108
110
  ? await hashPath(input, normalized)
109
111
  : await hashFileHandle(input, getNativeBinding(), normalized);
110
112
  }
113
+ function hashDescriptorSync(fd, { maxBytes = Infinity, signal }) {
114
+ signal?.throwIfAborted();
115
+ const stat = fsSync.fstatSync(fd);
116
+ if (!stat.isFile()) {
117
+ throw new FsSafeError("not-file", "SHA-256 input is not a regular file");
118
+ }
119
+ if (stat.size > maxBytes) {
120
+ throw new FsSafeError("too-large", `SHA-256 input exceeds ${maxBytes} bytes`);
121
+ }
122
+ const hash = createHash("sha256");
123
+ const buffer = Buffer.allocUnsafe(Math.min(256 * 1024, Math.max(64 * 1024, stat.size)));
124
+ let position = 0;
125
+ while (true) {
126
+ signal?.throwIfAborted();
127
+ const length = Math.min(buffer.length, maxBytes - position + 1);
128
+ const bytesRead = fsSync.readSync(fd, buffer, 0, length, position);
129
+ signal?.throwIfAborted();
130
+ if (bytesRead > maxBytes - position) {
131
+ throw new FsSafeError("too-large", `SHA-256 input exceeds ${maxBytes} bytes`);
132
+ }
133
+ if (bytesRead === 0) {
134
+ return { bytes: position, digest: hash.digest("hex") };
135
+ }
136
+ hash.update(buffer.subarray(0, bytesRead));
137
+ position += bytesRead;
138
+ }
139
+ }
140
+ function hashPathSync(filePath, options) {
141
+ const before = inspectFileIdentitySync(() => hashPathIdentity(filePath));
142
+ let fd;
143
+ try {
144
+ options.signal?.throwIfAborted();
145
+ fd = fsSync.openSync(filePath, resolveReadOpenFlags());
146
+ }
147
+ catch (error) {
148
+ rethrowHashOpenError(error);
149
+ }
150
+ try {
151
+ options.signal?.throwIfAborted();
152
+ const opened = inspectFileIdentitySync(() => hashDescriptorIdentity(fd), before);
153
+ inspectFileIdentitySync(() => hashPathIdentity(filePath, true), opened);
154
+ return hashDescriptorSync(fd, options);
155
+ }
156
+ finally {
157
+ try {
158
+ fsSync.closeSync(fd);
159
+ }
160
+ catch { /* Preserve the hashing outcome. */ }
161
+ }
162
+ }
163
+ /** Hash synchronously from offset zero, retaining ownership and position of a borrowed fd. */
164
+ export function sha256FileSync(input, options = {}) {
165
+ options.signal?.throwIfAborted();
166
+ const normalized = { maxBytes: normalizeMaxBytes(options.maxBytes), signal: options.signal };
167
+ return typeof input === "string"
168
+ ? hashPathSync(input, normalized)
169
+ : hashDescriptorSync(input, normalized);
170
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"file-lock-sync.d.ts","sourceRoot":"","sources":["../src/file-lock-sync.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EASL,KAAK,wBAAwB,EAC9B,MAAM,2BAA2B,CAAC;AASnC,OAAO,KAAK,EACV,0BAA0B,EAC1B,uBAAuB,EACvB,wBAAwB,EACzB,MAAM,yBAAyB,CAAC;AAIjC,MAAM,MAAM,0BAA0B,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,uBAAuB,CAAC;IAChC,aAAa,CAAC,EAAE,wBAAwB,CAAC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,QAAQ,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE;QACvB,QAAQ,EAAE,MAAM,CAAC;QACjB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,iBAAiB,EAAE,KAAK,CAAC;KAC1B,KAAK,OAAO,CAAC;IACd,qBAAqB,CAAC,EAAE,CAAC,QAAQ,EAAE,wBAAwB,KAAK,OAAO,CAAC;IACxE,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,0BAA0B,KAAK,IAAI,CAAC;IAC3D,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,eAAe,IAAI,OAAO,CAAC;IAC3B,OAAO,IAAI,IAAI,CAAC;IAChB,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B,CAAC;AAoJF,wBAAgB,mBAAmB,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,0BAA0B,CAAC,QAAQ,CAAC,GAC5C,kBAAkB,CAwMpB;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,0BAA0B,CAAC,QAAQ,CAAC,EAC7C,EAAE,EAAE,MAAM,CAAC,GACV,CAAC,CAOH"}
1
+ {"version":3,"file":"file-lock-sync.d.ts","sourceRoot":"","sources":["../src/file-lock-sync.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,gBAAgB,CAAC;AAC3C,OAAO,EASL,KAAK,wBAAwB,EAC9B,MAAM,2BAA2B,CAAC;AAWnC,OAAO,KAAK,EACV,0BAA0B,EAC1B,uBAAuB,EACvB,wBAAwB,EACzB,MAAM,yBAAyB,CAAC;AAMjC,MAAM,MAAM,0BAA0B,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,uBAAuB,CAAC;IAChC,aAAa,CAAC,EAAE,wBAAwB,CAAC;IACzC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,OAAO,EAAE,MAAM,QAAQ,CAAC;IACxB,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE;QACvB,QAAQ,EAAE,MAAM,CAAC;QACjB,oBAAoB,EAAE,MAAM,CAAC;QAC7B,OAAO,EAAE,OAAO,CAAC;QACjB,OAAO,EAAE,MAAM,CAAC;QAChB,KAAK,EAAE,MAAM,CAAC;QACd,iBAAiB,EAAE,KAAK,CAAC;KAC1B,KAAK,OAAO,CAAC;IACd,qBAAqB,CAAC,EAAE,CAAC,QAAQ,EAAE,wBAAwB,KAAK,OAAO,CAAC;IACxE,YAAY,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACxC,QAAQ,CAAC,EAAE,IAAI,CAAC;IAChB,aAAa,CAAC,EAAE,CAAC,IAAI,EAAE,0BAA0B,KAAK,IAAI,CAAC;IAC3D,yBAAyB,CAAC,EAAE,MAAM,CAAC;CACpC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,eAAe,IAAI,OAAO,CAAC;IAC3B,OAAO,IAAI,IAAI,CAAC;IAChB,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;CAC1B,CAAC;AAoJF,wBAAgB,mBAAmB,CAAC,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,0BAA0B,CAAC,QAAQ,CAAC,GAC5C,kBAAkB,CA0MpB;AAED,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,QAAQ,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC1E,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,0BAA0B,CAAC,QAAQ,CAAC,EAC7C,EAAE,EAAE,MAAM,CAAC,GACV,CAAC,CAOH"}
@@ -2,9 +2,11 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { FsSafeError } from "./errors.js";
4
4
  import { readSidecarLockSnapshotSync, readSidecarLockRawSnapshotSync, parseSidecarLockSnapshot, relativeSidecarLockPath, removeSidecarLockIfUnchangedSync, serializeSidecarLockPayload, sidecarLockSnapshotMatches, } from "./sidecar-lock-reclaim.js";
5
- import { computeSidecarLockDelayMs, isTransientLockFileDenial, maxTransientLockDenials, sidecarLockPayloadCreatedAtMs, validateSidecarLockRetryOptions, validateSidecarLockTimeoutMs, } from "./sidecar-lock-policy.js";
5
+ import { computeSidecarLockDelayMs, isTransientLockFileDenial, maxTransientLockDenials, sidecarLockPayloadCreatedAtMs, validateSidecarLockStaleMs, validateSidecarLockCompromiseCheckIntervalMs, validateSidecarLockRetryOptions, validateSidecarLockTimeoutMs, } from "./sidecar-lock-policy.js";
6
6
  import { getFsSafeLockConfig } from "./lock-config.js";
7
7
  import { sleepSync } from "./timing.js";
8
+ import { realpathSync } from "./realpath.js";
9
+ import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
8
10
  const SYNC_HELD_LOCKS_KEY = Symbol.for("fsSafe.syncSidecarLocks");
9
11
  const SYNC_CLEANUP_REGISTERED_KEY = Symbol.for("fsSafe.syncSidecarLockCleanupRegistered");
10
12
  const SYNC_CLEANUP_HANDLER_KEY = Symbol.for("fsSafe.syncSidecarLockCleanupHandler");
@@ -91,11 +93,11 @@ function createSyncHeldLockHandle(held) {
91
93
  }
92
94
  function canonicalLockParentSync(parent) {
93
95
  // Match Root's async realpath, including Windows short-name expansion.
94
- return process.platform === "win32" ? fs.realpathSync.native(parent) : fs.realpathSync(parent);
96
+ return process.platform === "win32" ? realpathSync.native(parent) : realpathSync(parent);
95
97
  }
96
98
  function normalizeTargetPath(targetPath) {
97
99
  const resolved = path.resolve(targetPath);
98
- fs.mkdirSync(path.dirname(resolved), { recursive: true });
100
+ fs.mkdirSync(recursiveMkdirPath(path.dirname(resolved)), { recursive: true });
99
101
  try {
100
102
  return path.join(canonicalLockParentSync(path.dirname(resolved)), path.basename(resolved));
101
103
  }
@@ -140,6 +142,9 @@ export function acquireFileLockSync(targetPath, options) {
140
142
  const timeoutMs = options.timeoutMs ?? defaults.timeoutMs;
141
143
  validateSidecarLockRetryOptions(retry);
142
144
  validateSidecarLockTimeoutMs(timeoutMs);
145
+ const staleMs = options.staleMs ?? defaults.staleMs ?? 30_000;
146
+ validateSidecarLockStaleMs(staleMs);
147
+ validateSidecarLockCompromiseCheckIntervalMs(options.compromiseCheckIntervalMs);
143
148
  const normalizedTargetPath = normalizeTargetPath(targetPath);
144
149
  const lockPath = boundedLockPath(options.lockPath ?? `${normalizedTargetPath}.lock`, options.lockRoot);
145
150
  const heldLocks = getSyncHeldLocks();
@@ -153,7 +158,6 @@ export function acquireFileLockSync(targetPath, options) {
153
158
  }
154
159
  // Process defaults fill the same fields here as withLockDefaults() fills for
155
160
  // the asynchronous manager, so both acquirers honor configureFsSafeLocks().
156
- const staleMs = options.staleMs ?? defaults.staleMs ?? 30_000;
157
161
  const staleRecovery = options.staleRecovery ?? defaults.staleRecovery;
158
162
  const startedAt = Date.now();
159
163
  let attempt = 0;
@@ -1 +1 @@
1
- {"version":3,"file":"file-store-boundary.d.ts","sourceRoot":"","sources":["../src/file-store-boundary.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAa,QAAQ,EAAE,MAAM,aAAa,CAAC;AAIvD,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAA4C,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAGhF,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAG1D,MAAM,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAOjD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,IAAI,EAChB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhB;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC,CAAC,GACnD,OAAO,CAAC,IAAI,CAAC,CASf;AAsCD,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAwC1D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAWrE;AAUD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,eAAe,CAOlB;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,eAAe,GAAG,OAAO,CAAC;CAC1C,GAAG,eAAe,CAyDlB"}
1
+ {"version":3,"file":"file-store-boundary.d.ts","sourceRoot":"","sources":["../src/file-store-boundary.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAa,QAAQ,EAAE,MAAM,aAAa,CAAC;AAIvD,OAAO,EAGL,KAAK,kBAAkB,EACxB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAA4C,KAAK,IAAI,EAAE,MAAM,WAAW,CAAC;AAGhF,OAAO,EAAE,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAK1D,MAAM,MAAM,eAAe,GAAG,kBAAkB,CAAC;AAOjD,wBAAsB,kBAAkB,CACtC,UAAU,EAAE,IAAI,EAChB,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,GAAG,OAAO,CAAC,IAAI,CAAC,CAKhB;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,EAAE,UAAU,CAAC,OAAO,sBAAsB,CAAC,CAAC,CAAC,CAAC,GACnD,OAAO,CAAC,IAAI,CAAC,CASf;AAsCD,wBAAsB,uBAAuB,CAAC,MAAM,EAAE;IACpD,MAAM,EAAE,QAAQ,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC,CAwC1D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAWrE;AAUD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,eAAe,CAOlB;AAED,wBAAgB,wBAAwB,CAAC,MAAM,EAAE;IAC/C,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,aAAa,EAAE,eAAe,GAAG,OAAO,CAAC;CAC1C,GAAG,eAAe,CAyDlB"}
@@ -13,6 +13,8 @@ import { ensureTrailingSep } from "./root-context.js";
13
13
  import { RootHandle } from "./root-impl.js";
14
14
  import { prepareSecretFileWrite } from "./secret-file.js";
15
15
  import { resolveSecureTempRoot } from "./secure-temp-dir.js";
16
+ import { realpathSync } from "./realpath.js";
17
+ import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
16
18
  function parentRelativePath(relativePath) {
17
19
  const parent = path.posix.dirname(relativePath);
18
20
  return parent === "." ? "" : parent;
@@ -27,7 +29,7 @@ export async function ensureParentInRoot(scopedRoot, relativePath, mode) {
27
29
  }
28
30
  export async function openWritableStoreRoot(params) {
29
31
  const maxBytes = normalizeMaxBytes(params.maxBytes);
30
- await fs.mkdir(params.rootDir, { recursive: true, mode: params.dirMode });
32
+ await fs.mkdir(recursiveMkdirPath(params.rootDir), { recursive: true, mode: params.dirMode });
31
33
  await fs.chmod(params.rootDir, params.dirMode).catch(() => undefined);
32
34
  return await root(params.rootDir, { hardlinks: "reject", maxBytes });
33
35
  }
@@ -149,12 +151,12 @@ export function ensureStoreDirectorySync(params) {
149
151
  if (isPathRelativeEscape(relative)) {
150
152
  throw new FsSafeError("outside-workspace", "file path escapes store root");
151
153
  }
152
- syncFs.mkdirSync(rootDir, { recursive: true, mode: params.mode });
154
+ syncFs.mkdirSync(recursiveMkdirPath(rootDir), { recursive: true, mode: params.mode });
153
155
  const rootStat = syncFs.lstatSync(rootDir);
154
156
  if (rootStat.isSymbolicLink() || !rootStat.isDirectory()) {
155
157
  throw new FsSafeError("not-file", `${params.messagePrefix} root must be a directory: ${rootDir}`);
156
158
  }
157
- const rootReal = syncFs.realpathSync(rootDir);
159
+ const rootReal = realpathSync(rootDir);
158
160
  chmodDirectorySyncBestEffort(rootDir, params.mode);
159
161
  let current = rootDir;
160
162
  for (const segment of path.relative(rootDir, dir).split(path.sep).filter(Boolean)) {
@@ -172,8 +174,8 @@ export function ensureStoreDirectorySync(params) {
172
174
  syncFs.mkdirSync(current, { mode: params.mode });
173
175
  }
174
176
  const currentRootStat = syncFs.lstatSync(rootDir);
175
- const currentRootReal = syncFs.realpathSync(rootDir);
176
- const currentReal = syncFs.realpathSync(current);
177
+ const currentRootReal = realpathSync(rootDir);
178
+ const currentReal = realpathSync(current);
177
179
  if (currentRootStat.isSymbolicLink() ||
178
180
  !currentRootStat.isDirectory() ||
179
181
  !sameFileIdentity(rootStat, currentRootStat) ||
@@ -0,0 +1,3 @@
1
+ export declare function assertRelativePath(relativePath: string): string;
2
+ export declare function resolveStorePath(rootDir: string, relativePath: string): string;
3
+ //# sourceMappingURL=file-store-path.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"file-store-path.d.ts","sourceRoot":"","sources":["../src/file-store-path.ts"],"names":[],"mappings":"AAIA,wBAAgB,kBAAkB,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAe/D;AAED,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAS9E"}
@@ -0,0 +1,27 @@
1
+ import path from "node:path";
2
+ import { FsSafeError } from "./errors.js";
3
+ import { isPathInside, splitSafeRelativePath } from "./path.js";
4
+ export function assertRelativePath(relativePath) {
5
+ const raw = relativePath.trim();
6
+ if (!raw || raw !== relativePath) {
7
+ throw new FsSafeError("invalid-path", "store key must be non-empty and unpadded");
8
+ }
9
+ const segments = splitSafeRelativePath(raw);
10
+ if (segments.length === 0 ||
11
+ segments.join("/") !== raw ||
12
+ raw.normalize("NFC") !== raw ||
13
+ segments.some((segment) => /[ .]$/u.test(segment))) {
14
+ throw new FsSafeError("invalid-path", "store key must use one canonical relative spelling");
15
+ }
16
+ return raw;
17
+ }
18
+ export function resolveStorePath(rootDir, relativePath) {
19
+ const key = assertRelativePath(relativePath);
20
+ const root = path.resolve(rootDir);
21
+ // The immutable key already passed segment validation; keep the containment check.
22
+ const target = path.resolve(root, key);
23
+ if (!isPathInside(root, target)) {
24
+ throw new FsSafeError("outside-workspace", "relative path escapes root");
25
+ }
26
+ return target;
27
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"file-store-prune.d.ts","sourceRoot":"","sources":["../src/file-store-prune.ts"],"names":[],"mappings":"AAQA,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,qBAAqB,CAAC;CAChC,GAAG,OAAO,CAAC,IAAI,CAAC,CAsFhB"}
1
+ {"version":3,"file":"file-store-prune.d.ts","sourceRoot":"","sources":["../src/file-store-prune.ts"],"names":[],"mappings":"AAUA,MAAM,MAAM,qBAAqB,GAAG;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B,CAAC;AAEF,wBAAsB,wBAAwB,CAAC,MAAM,EAAE;IACrD,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,qBAAqB,CAAC;CAChC,GAAG,OAAO,CAAC,IAAI,CAAC,CAsFhB"}
@@ -3,6 +3,8 @@ import fs from "node:fs/promises";
3
3
  import path from "node:path";
4
4
  import { FsSafeError } from "./errors.js";
5
5
  import { isPathInside } from "./path.js";
6
+ import { realpathSync } from "./realpath.js";
7
+ import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
6
8
  import { root } from "./root.js";
7
9
  import { getFsSafeTestHooks } from "./test-hooks.js";
8
10
  export async function pruneExpiredStoreEntries(params) {
@@ -10,8 +12,8 @@ export async function pruneExpiredStoreEntries(params) {
10
12
  const recursive = params.options.recursive ?? false;
11
13
  const maxDepth = params.options.maxDepth;
12
14
  const pruneEmptyDirs = (recursive || maxDepth !== undefined) && (params.options.pruneEmptyDirs ?? false);
13
- await fs.mkdir(params.rootDir, { recursive: true, mode: params.dirMode });
14
- const rootReal = fsSync.realpathSync.native(params.rootDir);
15
+ await fs.mkdir(recursiveMkdirPath(params.rootDir), { recursive: true, mode: params.dirMode });
16
+ const rootReal = realpathSync.native(params.rootDir);
15
17
  const scopedRoot = await root(rootReal);
16
18
  const rootGuard = {
17
19
  dir: rootReal,
@@ -24,7 +26,7 @@ export async function pruneExpiredStoreEntries(params) {
24
26
  !stat.isDirectory() ||
25
27
  stat.dev !== rootGuard.stat.dev ||
26
28
  stat.ino !== rootGuard.stat.ino ||
27
- fsSync.realpathSync.native(rootGuard.dir) !== rootGuard.realPath) {
29
+ realpathSync.native(rootGuard.dir) !== rootGuard.realPath) {
28
30
  throw new FsSafeError("path-mismatch", "store root changed during prune");
29
31
  }
30
32
  }
@@ -33,7 +35,7 @@ export async function pruneExpiredStoreEntries(params) {
33
35
  if (!before || before.isSymbolicLink() || !before.isDirectory()) {
34
36
  return null;
35
37
  }
36
- const real = observeOrNull(() => fsSync.realpathSync.native(dir));
38
+ const real = observeOrNull(() => realpathSync.native(dir));
37
39
  if (!real || !isPathInside(rootReal, real)) {
38
40
  return null;
39
41
  }
@@ -1 +1 @@
1
- {"version":3,"file":"file-store-sync-write.d.ts","sourceRoot":"","sources":["../src/file-store-sync-write.ts"],"names":[],"mappings":"AAwBA,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,MAAM,CA+GT"}
1
+ {"version":3,"file":"file-store-sync-write.d.ts","sourceRoot":"","sources":["../src/file-store-sync-write.ts"],"names":[],"mappings":"AAwDA,wBAAgB,mBAAmB,CAAC,MAAM,EAAE;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,UAAU,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;CACd,GAAG,MAAM,CAwFT"}
@@ -1,11 +1,14 @@
1
1
  import { randomUUID } from "node:crypto";
2
- import fs from "node:fs";
2
+ import fs, {} from "node:fs";
3
3
  import path from "node:path";
4
4
  import { syncDirectorySync } from "./directory-durability.js";
5
5
  import { FsSafeError } from "./errors.js";
6
- import { sameFileIdentity } from "./file-identity.js";
7
6
  import { assertSyncDirectoryGuard, ensureParentSync, ensureStoreDirectorySync, } from "./file-store-boundary.js";
8
7
  import { isPathInside } from "./path.js";
8
+ import { resolveReadOpenFlags } from "./read-open-flags.js";
9
+ import { writeTempFileSync } from "./replace-file-descriptor.js";
10
+ import { SyncAtomicTempOwner } from "./replace-file-temp-owner.js";
11
+ import { inspectFileIdentitySync } from "./strict-file-identity.js";
9
12
  import { getFsSafeTestHooks } from "./test-hooks.js";
10
13
  function ensurePrivateDirectorySync(rootDir, targetDir, mode) {
11
14
  return ensureStoreDirectorySync({
@@ -15,6 +18,35 @@ function ensurePrivateDirectorySync(rootDir, targetDir, mode) {
15
18
  messagePrefix: "private store",
16
19
  });
17
20
  }
21
+ function verifyStoreFile(fd, expected, filePath) {
22
+ const assertFile = (stat, allowUnknown = false) => {
23
+ const unknown = allowUnknown && process.platform === "win32" && (stat.dev === 0n || stat.ino === 0n);
24
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1n ||
25
+ (stat.dev !== expected.dev && !(unknown && stat.dev === 0n)) ||
26
+ (stat.ino !== expected.ino && !(unknown && stat.ino === 0n))) {
27
+ throw new FsSafeError("path-mismatch", "store file changed during write");
28
+ }
29
+ return unknown;
30
+ };
31
+ const assertDescriptor = (descriptor) => {
32
+ assertFile(inspectFileIdentitySync(() => fs.fstatSync(descriptor, { bigint: true }), expected));
33
+ };
34
+ assertDescriptor(fd);
35
+ if (assertFile(fs.lstatSync(filePath, { bigint: true }), true)) {
36
+ // A Windows pathname can have opaque metadata even while the retained writer is known.
37
+ // Reopen for identity only; equal content cannot establish that publication kept its file.
38
+ const reopened = fs.openSync(filePath, resolveReadOpenFlags());
39
+ try {
40
+ assertDescriptor(reopened);
41
+ assertFile(fs.lstatSync(filePath, { bigint: true }), true);
42
+ assertDescriptor(reopened);
43
+ }
44
+ finally {
45
+ fs.closeSync(reopened);
46
+ }
47
+ }
48
+ assertDescriptor(fd);
49
+ }
18
50
  export function writeFileSyncAtomic(params) {
19
51
  const filePath = path.resolve(params.filePath);
20
52
  if (!isPathInside(params.rootDir, filePath)) {
@@ -43,58 +75,41 @@ export function writeFileSyncAtomic(params) {
43
75
  });
44
76
  }
45
77
  const tempPath = path.join(parentGuard?.dir ?? path.dirname(filePath), `.fs-safe-${process.pid}-${randomUUID()}.tmp`);
46
- let tempExists = false;
78
+ const owner = new SyncAtomicTempOwner(tempPath);
79
+ let originalError;
47
80
  try {
48
81
  getFsSafeTestHooks()?.beforeFileStoreSyncPrivateWrite?.(filePath);
49
82
  if (parentGuard) {
50
83
  assertSyncDirectoryGuard(parentGuard);
51
84
  }
52
- const tempStat = (() => {
53
- const descriptor = fs.openSync(tempPath, "wx", params.mode);
54
- tempExists = true;
55
- try {
56
- fs.writeFileSync(descriptor, params.content);
85
+ owner.start();
86
+ const temp = writeTempFileSync({
87
+ fsModule: fs, tempPath, content: params.content, mode: params.mode, sync: false,
88
+ onIdentity: owner.onIdentity,
89
+ fchmodSync: (descriptor, mode) => {
57
90
  try {
58
- fs.fchmodSync(descriptor, params.mode);
91
+ fs.fchmodSync(descriptor, mode);
59
92
  }
60
93
  catch {
61
94
  // Best-effort on platforms that do not enforce POSIX modes.
62
95
  }
63
- const opened = fs.fstatSync(descriptor);
64
- if (!opened.isFile() || opened.nlink > 1) {
65
- throw new FsSafeError("path-mismatch", "store temp is not an owned regular file");
66
- }
67
- if (params.durable)
68
- fs.fsyncSync(descriptor);
69
- return opened;
70
- }
71
- finally {
72
- fs.closeSync(descriptor);
73
- }
74
- })();
75
- const tempPathStat = fs.lstatSync(tempPath);
76
- if (tempPathStat.isSymbolicLink() ||
77
- !tempPathStat.isFile() ||
78
- tempPathStat.nlink > 1 ||
79
- !sameFileIdentity(tempPathStat, tempStat)) {
80
- throw new FsSafeError("path-mismatch", "store temp changed before publication");
81
- }
96
+ },
97
+ });
98
+ owner.adopt(temp);
99
+ // Preserve the store's strict fsync errors; the generic temp helper tolerates EPERM.
100
+ if (params.durable)
101
+ fs.fsyncSync(temp.fd);
102
+ verifyStoreFile(temp.fd, owner.identity, tempPath);
82
103
  if (parentGuard) {
83
104
  assertSyncDirectoryGuard(parentGuard);
84
105
  }
85
106
  fs.renameSync(tempPath, filePath);
86
- tempExists = false;
107
+ owner.markRenamed();
87
108
  if (parentGuard) {
88
109
  assertSyncDirectoryGuard(parentGuard);
89
110
  }
90
111
  try {
91
- const publishedStat = fs.lstatSync(filePath);
92
- if (publishedStat.isSymbolicLink() ||
93
- !publishedStat.isFile() ||
94
- publishedStat.nlink > 1 ||
95
- !sameFileIdentity(tempStat, publishedStat)) {
96
- throw new FsSafeError("path-mismatch", "store target changed after write");
97
- }
112
+ verifyStoreFile(temp.fd, owner.identity, filePath);
98
113
  }
99
114
  catch (error) {
100
115
  if (error instanceof FsSafeError) {
@@ -117,14 +132,11 @@ export function writeFileSyncAtomic(params) {
117
132
  }
118
133
  return filePath;
119
134
  }
135
+ catch (error) {
136
+ originalError = error;
137
+ throw error;
138
+ }
120
139
  finally {
121
- if (tempExists) {
122
- try {
123
- fs.unlinkSync(tempPath);
124
- }
125
- catch {
126
- // Best-effort cleanup after write failure.
127
- }
128
- }
140
+ owner.finish({ fsModule: fs, originalError, throwOnCleanupError: false });
129
141
  }
130
142
  }
@@ -1 +1 @@
1
- {"version":3,"file":"file-store.d.ts","sourceRoot":"","sources":["../src/file-store.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"file-store.d.ts","sourceRoot":"","sources":["../src/file-store.ts"],"names":[],"mappings":"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"}