@openclaw/fs-safe 0.9.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 (326) hide show
  1. package/CHANGELOG.md +134 -0
  2. package/LICENSE +1 -0
  3. package/README.md +52 -4
  4. package/dist/absolute-path.d.ts.map +1 -1
  5. package/dist/absolute-path.js +3 -2
  6. package/dist/advanced.d.ts +5 -0
  7. package/dist/advanced.d.ts.map +1 -1
  8. package/dist/advanced.js +5 -0
  9. package/dist/archive-crc32.d.ts.map +1 -1
  10. package/dist/archive-crc32.js +6 -1
  11. package/dist/archive-deadline.d.ts.map +1 -1
  12. package/dist/archive-deadline.js +3 -4
  13. package/dist/archive-durability.d.ts +6 -6
  14. package/dist/archive-durability.d.ts.map +1 -1
  15. package/dist/archive-durability.js +1 -1
  16. package/dist/archive-entry.d.ts.map +1 -1
  17. package/dist/archive-entry.js +4 -5
  18. package/dist/archive-gzip-tail.d.ts +3 -0
  19. package/dist/archive-gzip-tail.d.ts.map +1 -1
  20. package/dist/archive-gzip-tail.js +23 -3
  21. package/dist/archive-input.d.ts.map +1 -1
  22. package/dist/archive-input.js +4 -2
  23. package/dist/archive-merge.d.ts +5 -1
  24. package/dist/archive-merge.d.ts.map +1 -1
  25. package/dist/archive-merge.js +16 -13
  26. package/dist/archive-native.d.ts.map +1 -1
  27. package/dist/archive-native.js +7 -6
  28. package/dist/archive-parser.wasm +0 -0
  29. package/dist/archive-read.d.ts.map +1 -1
  30. package/dist/archive-read.js +83 -74
  31. package/dist/archive-staging.d.ts +6 -3
  32. package/dist/archive-staging.d.ts.map +1 -1
  33. package/dist/archive-staging.js +42 -22
  34. package/dist/archive-tar-stream.d.ts +11 -4
  35. package/dist/archive-tar-stream.d.ts.map +1 -1
  36. package/dist/archive-tar-stream.js +23 -10
  37. package/dist/archive-tar-wasm.d.ts.map +1 -1
  38. package/dist/archive-tar-wasm.js +18 -14
  39. package/dist/archive-zip-admission.d.ts +1 -1
  40. package/dist/archive-zip-admission.d.ts.map +1 -1
  41. package/dist/archive-zip-admission.js +48 -12
  42. package/dist/archive-zip-loader.d.ts +6 -0
  43. package/dist/archive-zip-loader.d.ts.map +1 -0
  44. package/dist/archive-zip-loader.js +38 -0
  45. package/dist/archive-zip-names.d.ts.map +1 -1
  46. package/dist/archive-zip-names.js +13 -8
  47. package/dist/archive-zip-preflight.d.ts +2 -3
  48. package/dist/archive-zip-preflight.d.ts.map +1 -1
  49. package/dist/archive-zip-preflight.js +2 -34
  50. package/dist/archive.d.ts.map +1 -1
  51. package/dist/archive.js +12 -10
  52. package/dist/bounded-read.d.ts +12 -0
  53. package/dist/bounded-read.d.ts.map +1 -1
  54. package/dist/bounded-read.js +82 -45
  55. package/dist/clone-metadata.d.ts +19 -0
  56. package/dist/clone-metadata.d.ts.map +1 -0
  57. package/dist/clone-metadata.js +32 -0
  58. package/dist/copy-file-input.d.ts +22 -0
  59. package/dist/copy-file-input.d.ts.map +1 -0
  60. package/dist/copy-file-input.js +69 -0
  61. package/dist/copy-policy.d.ts +3 -0
  62. package/dist/copy-policy.d.ts.map +1 -0
  63. package/dist/copy-policy.js +8 -0
  64. package/dist/copy-publication.d.ts +10 -1
  65. package/dist/copy-publication.d.ts.map +1 -1
  66. package/dist/copy-publication.js +27 -0
  67. package/dist/copy-tree-portable.d.ts +9 -0
  68. package/dist/copy-tree-portable.d.ts.map +1 -0
  69. package/dist/copy-tree-portable.js +222 -0
  70. package/dist/copy.d.ts +16 -0
  71. package/dist/copy.d.ts.map +1 -0
  72. package/dist/copy.js +125 -0
  73. package/dist/directory-durability.d.ts.map +1 -1
  74. package/dist/directory-durability.js +5 -4
  75. package/dist/directory-guard.d.ts +11 -1
  76. package/dist/directory-guard.d.ts.map +1 -1
  77. package/dist/directory-guard.js +53 -11
  78. package/dist/durability.d.ts +1 -1
  79. package/dist/durability.d.ts.map +1 -1
  80. package/dist/durability.js +1 -1
  81. package/dist/error-detail.d.ts.map +1 -1
  82. package/dist/error-detail.js +4 -1
  83. package/dist/file-handle-transfer.d.ts +14 -0
  84. package/dist/file-handle-transfer.d.ts.map +1 -0
  85. package/dist/file-handle-transfer.js +64 -0
  86. package/dist/file-hash.d.ts +9 -2
  87. package/dist/file-hash.d.ts.map +1 -1
  88. package/dist/file-hash.js +135 -39
  89. package/dist/file-lock-sync.d.ts.map +1 -1
  90. package/dist/file-lock-sync.js +8 -4
  91. package/dist/file-store-boundary.d.ts.map +1 -1
  92. package/dist/file-store-boundary.js +7 -5
  93. package/dist/file-store-path.d.ts +3 -0
  94. package/dist/file-store-path.d.ts.map +1 -0
  95. package/dist/file-store-path.js +27 -0
  96. package/dist/file-store-prune.d.ts.map +1 -1
  97. package/dist/file-store-prune.js +6 -4
  98. package/dist/file-store-sync-write.d.ts.map +1 -1
  99. package/dist/file-store-sync-write.js +56 -44
  100. package/dist/file-store.d.ts.map +1 -1
  101. package/dist/file-store.js +2 -18
  102. package/dist/filename.d.ts.map +1 -1
  103. package/dist/filename.js +4 -13
  104. package/dist/guarded-mkdir.d.ts +1 -0
  105. package/dist/guarded-mkdir.d.ts.map +1 -1
  106. package/dist/guarded-mkdir.js +4 -2
  107. package/dist/guarded-mutation.d.ts +2 -0
  108. package/dist/guarded-mutation.d.ts.map +1 -1
  109. package/dist/guarded-mutation.js +8 -4
  110. package/dist/guest-dispatch-python.d.ts +2 -0
  111. package/dist/guest-dispatch-python.d.ts.map +1 -0
  112. package/dist/guest-dispatch-python.js +117 -0
  113. package/dist/guest-native-python.d.ts +4 -0
  114. package/dist/guest-native-python.d.ts.map +1 -0
  115. package/dist/guest-native-python.js +135 -0
  116. package/dist/guest.d.ts +9 -0
  117. package/dist/guest.d.ts.map +1 -0
  118. package/dist/guest.js +413 -0
  119. package/dist/home-dir.d.ts.map +1 -1
  120. package/dist/home-dir.js +9 -7
  121. package/dist/index.d.ts +1 -1
  122. package/dist/index.d.ts.map +1 -1
  123. package/dist/install-path.d.ts.map +1 -1
  124. package/dist/install-path.js +5 -8
  125. package/dist/json-durable-queue-directory.js +3 -3
  126. package/dist/json-durable-queue.d.ts.map +1 -1
  127. package/dist/json-durable-queue.js +19 -18
  128. package/dist/json.d.ts.map +1 -1
  129. package/dist/json.js +2 -1
  130. package/dist/local-roots.d.ts.map +1 -1
  131. package/dist/local-roots.js +24 -25
  132. package/dist/move-path-stage.d.ts +8 -0
  133. package/dist/move-path-stage.d.ts.map +1 -0
  134. package/dist/move-path-stage.js +56 -0
  135. package/dist/move-path.d.ts.map +1 -1
  136. package/dist/move-path.js +30 -34
  137. package/dist/mutation-authority.d.ts +9 -0
  138. package/dist/mutation-authority.d.ts.map +1 -0
  139. package/dist/mutation-authority.js +36 -0
  140. package/dist/native-binding.d.ts +29 -1
  141. package/dist/native-binding.d.ts.map +1 -1
  142. package/dist/native-operations.d.ts +1 -1
  143. package/dist/native-operations.d.ts.map +1 -1
  144. package/dist/native-operations.js +11 -5
  145. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  146. package/dist/native-pinned-write-windows.js +19 -7
  147. package/dist/native-pinned-write.d.ts.map +1 -1
  148. package/dist/native-pinned-write.js +3 -1
  149. package/dist/native-staged-file.d.ts +3 -3
  150. package/dist/native-staged-file.d.ts.map +1 -1
  151. package/dist/native-staged-file.js +35 -8
  152. package/dist/opened-realpath.d.ts.map +1 -1
  153. package/dist/opened-realpath.js +5 -4
  154. package/dist/output.d.ts +2 -0
  155. package/dist/output.d.ts.map +1 -1
  156. package/dist/output.js +2 -0
  157. package/dist/overwrite-file-handle.d.ts +8 -0
  158. package/dist/overwrite-file-handle.d.ts.map +1 -0
  159. package/dist/overwrite-file-handle.js +42 -0
  160. package/dist/path-case.d.ts +7 -0
  161. package/dist/path-case.d.ts.map +1 -0
  162. package/dist/path-case.js +136 -0
  163. package/dist/path.d.ts.map +1 -1
  164. package/dist/path.js +5 -2
  165. package/dist/permissions-windows.d.ts +1 -1
  166. package/dist/permissions-windows.d.ts.map +1 -1
  167. package/dist/permissions-windows.js +85 -53
  168. package/dist/pinned-open.d.ts.map +1 -1
  169. package/dist/pinned-open.js +3 -1
  170. package/dist/pinned-operation.js +1 -1
  171. package/dist/pinned-write.d.ts +7 -3
  172. package/dist/pinned-write.d.ts.map +1 -1
  173. package/dist/pinned-write.js +51 -30
  174. package/dist/positional-read.d.ts +9 -0
  175. package/dist/positional-read.d.ts.map +1 -0
  176. package/dist/positional-read.js +36 -0
  177. package/dist/private-temp-workspace.d.ts.map +1 -1
  178. package/dist/private-temp-workspace.js +6 -4
  179. package/dist/publish-copy-stage.d.ts +13 -0
  180. package/dist/publish-copy-stage.d.ts.map +1 -0
  181. package/dist/publish-copy-stage.js +47 -0
  182. package/dist/publish-file.d.ts.map +1 -1
  183. package/dist/publish-file.js +6 -5
  184. package/dist/realpath.d.ts +4 -0
  185. package/dist/realpath.d.ts.map +1 -0
  186. package/dist/realpath.js +43 -0
  187. package/dist/recursive-mkdir-path.d.ts +3 -0
  188. package/dist/recursive-mkdir-path.d.ts.map +1 -0
  189. package/dist/recursive-mkdir-path.js +8 -0
  190. package/dist/replace-directory.d.ts.map +1 -1
  191. package/dist/replace-directory.js +2 -1
  192. package/dist/replace-file-copy-fallback.d.ts.map +1 -1
  193. package/dist/replace-file-copy-fallback.js +23 -31
  194. package/dist/replace-file-copy-source.d.ts.map +1 -1
  195. package/dist/replace-file-copy-source.js +7 -12
  196. package/dist/replace-file-mode.d.ts +3 -0
  197. package/dist/replace-file-mode.d.ts.map +1 -0
  198. package/dist/replace-file-mode.js +10 -0
  199. package/dist/replace-file-temp-owner.d.ts.map +1 -1
  200. package/dist/replace-file-temp-owner.js +6 -2
  201. package/dist/replace-file.d.ts +1 -0
  202. package/dist/replace-file.d.ts.map +1 -1
  203. package/dist/replace-file.js +14 -8
  204. package/dist/root-context.d.ts +3 -0
  205. package/dist/root-context.d.ts.map +1 -1
  206. package/dist/root-context.js +53 -28
  207. package/dist/root-create-input.d.ts +10 -0
  208. package/dist/root-create-input.d.ts.map +1 -0
  209. package/dist/root-create-input.js +80 -0
  210. package/dist/root-directory-list.d.ts +26 -0
  211. package/dist/root-directory-list.d.ts.map +1 -0
  212. package/dist/root-directory-list.js +219 -0
  213. package/dist/root-entries.d.ts +11 -0
  214. package/dist/root-entries.d.ts.map +1 -0
  215. package/dist/root-entries.js +61 -0
  216. package/dist/root-errors.d.ts +6 -5
  217. package/dist/root-errors.d.ts.map +1 -1
  218. package/dist/root-errors.js +16 -12
  219. package/dist/root-file.d.ts +2 -0
  220. package/dist/root-file.d.ts.map +1 -1
  221. package/dist/root-file.js +12 -4
  222. package/dist/root-impl.d.ts +17 -51
  223. package/dist/root-impl.d.ts.map +1 -1
  224. package/dist/root-impl.js +358 -234
  225. package/dist/root-options.d.ts +77 -0
  226. package/dist/root-options.d.ts.map +1 -0
  227. package/dist/root-options.js +18 -0
  228. package/dist/root-path-existing.d.ts +5 -0
  229. package/dist/root-path-existing.d.ts.map +1 -1
  230. package/dist/root-path-existing.js +59 -3
  231. package/dist/root-path-symlink.d.ts.map +1 -1
  232. package/dist/root-path-symlink.js +3 -2
  233. package/dist/root-path.d.ts +1 -0
  234. package/dist/root-path.d.ts.map +1 -1
  235. package/dist/root-path.js +74 -162
  236. package/dist/root-paths.d.ts.map +1 -1
  237. package/dist/root-paths.js +13 -9
  238. package/dist/root-remove.d.ts +5 -0
  239. package/dist/root-remove.d.ts.map +1 -0
  240. package/dist/root-remove.js +286 -0
  241. package/dist/root-symlink-policy.d.ts +14 -0
  242. package/dist/root-symlink-policy.d.ts.map +1 -0
  243. package/dist/root-symlink-policy.js +34 -0
  244. package/dist/root-walk.d.ts +5 -4
  245. package/dist/root-walk.d.ts.map +1 -1
  246. package/dist/root-walk.js +99 -50
  247. package/dist/root-write-mode.d.ts.map +1 -1
  248. package/dist/root-write-mode.js +3 -5
  249. package/dist/root.d.ts +4 -1
  250. package/dist/root.d.ts.map +1 -1
  251. package/dist/secret-file.d.ts.map +1 -1
  252. package/dist/secret-file.js +2 -1
  253. package/dist/secret-read-async.d.ts.map +1 -1
  254. package/dist/secret-read-async.js +2 -1
  255. package/dist/secure-file.d.ts.map +1 -1
  256. package/dist/secure-file.js +25 -8
  257. package/dist/secure-temp-dir.d.ts.map +1 -1
  258. package/dist/secure-temp-dir.js +2 -1
  259. package/dist/sibling-staged-file.d.ts +1 -0
  260. package/dist/sibling-staged-file.d.ts.map +1 -1
  261. package/dist/sibling-staged-file.js +42 -8
  262. package/dist/sibling-temp.d.ts +2 -0
  263. package/dist/sibling-temp.d.ts.map +1 -1
  264. package/dist/sibling-temp.js +6 -4
  265. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  266. package/dist/sidecar-lock-acquire.js +17 -5
  267. package/dist/sidecar-lock-policy.d.ts +2 -0
  268. package/dist/sidecar-lock-policy.d.ts.map +1 -1
  269. package/dist/sidecar-lock-policy.js +20 -1
  270. package/dist/staged-directory.d.ts.map +1 -1
  271. package/dist/staged-directory.js +4 -3
  272. package/dist/temp-cleanup.d.ts.map +1 -1
  273. package/dist/temp-cleanup.js +3 -2
  274. package/dist/temp-target.d.ts +14 -12
  275. package/dist/temp-target.d.ts.map +1 -1
  276. package/dist/temp-target.js +12 -6
  277. package/dist/timing.d.ts +1 -0
  278. package/dist/timing.d.ts.map +1 -1
  279. package/dist/timing.js +25 -6
  280. package/dist/trash.d.ts.map +1 -1
  281. package/dist/trash.js +9 -7
  282. package/dist/unicode-path.d.ts +3 -0
  283. package/dist/unicode-path.d.ts.map +1 -0
  284. package/dist/unicode-path.js +13 -0
  285. package/dist/walk.d.ts.map +1 -1
  286. package/dist/walk.js +9 -28
  287. package/dist/windows-owner.d.ts +9 -12
  288. package/dist/windows-owner.d.ts.map +1 -1
  289. package/dist/windows-owner.js +24 -58
  290. package/dist/write-file-handle.d.ts +7 -0
  291. package/dist/write-file-handle.d.ts.map +1 -0
  292. package/dist/write-file-handle.js +26 -0
  293. package/docs/advanced.md +22 -4
  294. package/docs/archive.md +44 -9
  295. package/docs/atomic.md +24 -1
  296. package/docs/config.md +1 -0
  297. package/docs/contributing.md +36 -1
  298. package/docs/copy.md +155 -0
  299. package/docs/directory-identity.md +85 -0
  300. package/docs/durability.md +53 -3
  301. package/docs/entries.md +109 -0
  302. package/docs/errors.md +4 -4
  303. package/docs/file-store.md +15 -0
  304. package/docs/guest.md +141 -0
  305. package/docs/in-place-write.md +81 -0
  306. package/docs/index.md +2 -0
  307. package/docs/install.md +31 -0
  308. package/docs/local-roots.md +8 -1
  309. package/docs/native-helper.md +10 -3
  310. package/docs/native.md +18 -1
  311. package/docs/output.md +32 -6
  312. package/docs/path-case.md +64 -0
  313. package/docs/path-scope.md +1 -1
  314. package/docs/permissions.md +29 -10
  315. package/docs/positional-read.md +63 -0
  316. package/docs/public-api.md +31 -2
  317. package/docs/root.md +196 -6
  318. package/docs/secure-file.md +2 -0
  319. package/docs/security-model.md +14 -0
  320. package/docs/sidecar-lock.md +12 -3
  321. package/docs/temp.md +35 -6
  322. package/docs/timing.md +2 -0
  323. package/docs/types.md +19 -12
  324. package/docs/walk.md +54 -5
  325. package/docs/writing.md +173 -5
  326. package/package.json +19 -8
@@ -0,0 +1,80 @@
1
+ import { normalizeMaxBytes } from "./byte-budget.js";
2
+ import { FsSafeError } from "./errors.js";
3
+ import { MutationAuthorityError } from "./mutation-authority.js";
4
+ class CreateInputError extends FsSafeError {
5
+ rejection;
6
+ constructor(rejection) {
7
+ super("helper-failed", "create input failed", { cause: rejection });
8
+ this.rejection = rejection;
9
+ }
10
+ }
11
+ export function rethrowCreateInputError(error) {
12
+ if (error instanceof CreateInputError)
13
+ throw error.rejection;
14
+ throw error;
15
+ }
16
+ export function createInputOptions(data, options, defaultMaxBytes) {
17
+ if (typeof data === "string" || Buffer.isBuffer(data))
18
+ return options;
19
+ options.signal?.throwIfAborted();
20
+ if (options.encoding !== undefined || options.renameIdentity !== undefined) {
21
+ throw new TypeError("streamed create does not accept encoding or renameIdentity");
22
+ }
23
+ const maxBytes = normalizeMaxBytes(options.maxBytes, { defaultValue: defaultMaxBytes });
24
+ return {
25
+ ...options,
26
+ maxBytes,
27
+ assertBeforeMutation: () => {
28
+ options.signal?.throwIfAborted();
29
+ return options.assertBeforeMutation?.();
30
+ },
31
+ };
32
+ }
33
+ export function rootWriteInput(params) {
34
+ const data = params.data;
35
+ if (typeof data === "string" || Buffer.isBuffer(data)) {
36
+ return { kind: "buffer", data, encoding: params.encoding };
37
+ }
38
+ return {
39
+ kind: "stream",
40
+ stageBeforePublish: true,
41
+ stream: (async function* () {
42
+ let iterator;
43
+ let completed = false;
44
+ try {
45
+ params.signal?.throwIfAborted();
46
+ iterator = data[Symbol.asyncIterator]();
47
+ while (true) {
48
+ if (params.signal?.aborted)
49
+ throw new MutationAuthorityError(params.signal.reason);
50
+ const next = await iterator.next();
51
+ completed = next.done === true;
52
+ if (params.signal?.aborted)
53
+ throw new MutationAuthorityError(params.signal.reason);
54
+ if (completed)
55
+ return;
56
+ const chunk = next.value;
57
+ if (!(chunk instanceof Uint8Array)) {
58
+ throw new TypeError("streamed create chunks must be Uint8Array values");
59
+ }
60
+ yield chunk;
61
+ }
62
+ }
63
+ catch (error) {
64
+ if (error instanceof MutationAuthorityError)
65
+ throw error;
66
+ throw new CreateInputError(error);
67
+ }
68
+ finally {
69
+ if (iterator && !completed) {
70
+ try {
71
+ await iterator.return?.();
72
+ }
73
+ catch (error) {
74
+ throw new CreateInputError(error);
75
+ }
76
+ }
77
+ }
78
+ })(),
79
+ };
80
+ }
@@ -0,0 +1,26 @@
1
+ import type { Stats } from "node:fs";
2
+ import { type RootContext } from "./root-context.js";
3
+ import type { DirEntry, PathStat } from "./types.js";
4
+ export declare function pathStatFromStats(stat: Stats): PathStat;
5
+ export declare function listDirectoryPath(root: RootContext, directory: string, withFileTypes: true): Promise<DirEntry[]>;
6
+ export declare function listDirectoryPath(root: RootContext, directory: string, withFileTypes: boolean): Promise<string[] | DirEntry[]>;
7
+ export type RootDirectoryListing = {
8
+ next(): Promise<{
9
+ kind: "entry";
10
+ entry: DirEntry;
11
+ } | {
12
+ kind: "limit";
13
+ name: string;
14
+ } | undefined>;
15
+ [Symbol.asyncDispose](): Promise<void>;
16
+ };
17
+ export type RootDirectoryListingOptions = {
18
+ order: "sorted" | "filesystem";
19
+ signal?: AbortSignal;
20
+ snapshot: boolean;
21
+ maxNames?: number;
22
+ metadataBatchSize?: number;
23
+ admitEntry(): boolean;
24
+ };
25
+ export declare function openRootDirectoryListing(root: RootContext, directory: string, options: RootDirectoryListingOptions): Promise<RootDirectoryListing>;
26
+ //# sourceMappingURL=root-directory-list.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root-directory-list.d.ts","sourceRoot":"","sources":["../src/root-directory-list.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAQ1C,OAAO,EAA6B,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAGhF,OAAO,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAIrD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,KAAK,GAAG,QAAQ,CAcvD;AAWD,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC;AAClH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,aAAa,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,QAAQ,EAAE,CAAC,CAAC;AAqBhI,MAAM,MAAM,oBAAoB,GAAG;IACjC,IAAI,IAAI,OAAO,CAAC;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,KAAK,EAAE,QAAQ,CAAA;KAAE,GAAG;QAAE,IAAI,EAAE,OAAO,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC;IAClG,CAAC,MAAM,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACxC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,KAAK,EAAE,QAAQ,GAAG,YAAY,CAAC;IAC/B,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,IAAI,OAAO,CAAC;CACvB,CAAC;AAEF,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE,WAAW,EACjB,SAAS,EAAE,MAAM,EACjB,OAAO,EAAE,2BAA2B,GACnC,OAAO,CAAC,oBAAoB,CAAC,CAsJ/B"}
@@ -0,0 +1,219 @@
1
+ import fsSync from "node:fs";
2
+ import fs from "node:fs/promises";
3
+ import path from "node:path";
4
+ import { setImmediate as yieldToEventLoop } from "node:timers/promises";
5
+ import { assertAsyncDirectoryGuard, createAsyncDirectoryGuard } from "./directory-guard.js";
6
+ import { FsSafeError } from "./errors.js";
7
+ import { isNotFoundPathError, isPathInside } from "./path.js";
8
+ import { assertRootIdentityCurrent } from "./root-context.js";
9
+ import { rootPathChangedError } from "./root-errors.js";
10
+ import { createSuppressedError } from "./suppressed-error.js";
11
+ const METADATA_BATCH_SIZE = 32;
12
+ export function pathStatFromStats(stat) {
13
+ return {
14
+ dev: Number(stat.dev),
15
+ gid: Number(stat.gid),
16
+ ino: Number(stat.ino),
17
+ isDirectory: stat.isDirectory(),
18
+ isFile: stat.isFile(),
19
+ isSymbolicLink: stat.isSymbolicLink(),
20
+ mode: stat.mode,
21
+ mtimeMs: stat.mtimeMs,
22
+ nlink: stat.nlink,
23
+ size: stat.size,
24
+ uid: stat.uid,
25
+ };
26
+ }
27
+ function normalizeDirectoryError(error) {
28
+ if (isNotFoundPathError(error)) {
29
+ return new FsSafeError("not-found", "directory not found", {
30
+ cause: error instanceof Error ? error : undefined,
31
+ });
32
+ }
33
+ return error;
34
+ }
35
+ export async function listDirectoryPath(root, directory, withFileTypes) {
36
+ try {
37
+ const names = (await fs.readdir(directory)).sort();
38
+ const entries = withFileTypes
39
+ ? names.map(name => ({
40
+ name,
41
+ ...pathStatFromStats(fsSync.lstatSync(path.join(directory, name))),
42
+ }))
43
+ : names;
44
+ await assertRootIdentityCurrent(root);
45
+ return entries;
46
+ }
47
+ catch (error) {
48
+ throw normalizeDirectoryError(error);
49
+ }
50
+ }
51
+ export async function openRootDirectoryListing(root, directory, options) {
52
+ const guard = await createAsyncDirectoryGuard(directory, { bigint: true }).catch((error) => {
53
+ throw normalizeDirectoryError(error);
54
+ });
55
+ if (!isPathInside(root.rootReal, guard.realPath)) {
56
+ throw new FsSafeError("outside-workspace", "directory is outside workspace root");
57
+ }
58
+ const guardPinsRoot = guard.dir === root.rootReal &&
59
+ guard.stat.dev === root.rootIdentity.dev && guard.stat.ino === root.rootIdentity.ino;
60
+ const assertCurrent = async () => {
61
+ options.signal?.throwIfAborted();
62
+ if (guardPinsRoot) {
63
+ // One exact receipt proves both identities without dropping the canonical-path check.
64
+ try {
65
+ await assertAsyncDirectoryGuard(guard);
66
+ }
67
+ catch (error) {
68
+ throw rootPathChangedError(error instanceof Error ? error : undefined);
69
+ }
70
+ }
71
+ else {
72
+ await assertRootIdentityCurrent(root);
73
+ await assertAsyncDirectoryGuard(guard);
74
+ }
75
+ options.signal?.throwIfAborted();
76
+ };
77
+ let handle;
78
+ let names;
79
+ let snapshot;
80
+ let index = 0;
81
+ let prepared = [];
82
+ let preparedIndex = 0;
83
+ let pendingFailure;
84
+ let pendingLimit;
85
+ let preparedBatch = false;
86
+ const close = async () => {
87
+ const owned = handle;
88
+ handle = undefined;
89
+ await owned?.close();
90
+ };
91
+ try {
92
+ await assertCurrent();
93
+ if (options.order === "filesystem") {
94
+ // A one-entry buffer keeps the truncation lookahead independent of width.
95
+ handle = await fs.opendir(guard.realPath, { bufferSize: 1 });
96
+ }
97
+ else if (options.snapshot) {
98
+ snapshot = await listDirectoryPath(root, guard.realPath, true);
99
+ }
100
+ else if (options.maxNames !== undefined) {
101
+ names = [];
102
+ handle = await fs.opendir(guard.realPath, { bufferSize: 1 });
103
+ while (true) {
104
+ await assertCurrent();
105
+ const name = (await handle.read())?.name;
106
+ await assertCurrent();
107
+ if (name === undefined)
108
+ break;
109
+ if (names.length >= options.maxNames) {
110
+ throw new FsSafeError("too-large", "directory entry budget exceeded");
111
+ }
112
+ names.push(name);
113
+ }
114
+ await close();
115
+ }
116
+ else {
117
+ names = await fs.readdir(guard.realPath);
118
+ }
119
+ await assertCurrent();
120
+ names?.sort();
121
+ }
122
+ catch (error) {
123
+ const operationError = normalizeDirectoryError(error);
124
+ try {
125
+ await close();
126
+ }
127
+ catch (closeError) {
128
+ throw createSuppressedError(closeError, operationError, "directory setup and close both failed");
129
+ }
130
+ throw operationError;
131
+ }
132
+ const prepareBatch = async (sortedNames) => {
133
+ let name = sortedNames[index++];
134
+ if (name === undefined) {
135
+ await assertCurrent();
136
+ return;
137
+ }
138
+ if (!options.admitEntry()) {
139
+ pendingLimit = name;
140
+ return;
141
+ }
142
+ if (preparedBatch)
143
+ await yieldToEventLoop();
144
+ await assertCurrent();
145
+ preparedBatch = true;
146
+ prepared = [];
147
+ preparedIndex = 0;
148
+ while (true) {
149
+ try {
150
+ const entry = { name, ...pathStatFromStats(fsSync.lstatSync(path.join(guard.realPath, name))) };
151
+ prepared.push(entry);
152
+ // No later sibling can be observed before a possible recursive descent.
153
+ if (entry.isDirectory || entry.isSymbolicLink || prepared.length >= (options.metadataBatchSize ?? METADATA_BATCH_SIZE))
154
+ break;
155
+ }
156
+ catch (error) {
157
+ pendingFailure = { error: normalizeDirectoryError(error) };
158
+ break;
159
+ }
160
+ name = sortedNames[index++];
161
+ if (name === undefined)
162
+ break;
163
+ if (!options.admitEntry()) {
164
+ pendingLimit = name;
165
+ break;
166
+ }
167
+ }
168
+ try {
169
+ await assertCurrent();
170
+ }
171
+ catch (error) {
172
+ if (pendingFailure) {
173
+ throw createSuppressedError(error, pendingFailure.error, "directory observation and identity checks both failed");
174
+ }
175
+ throw error;
176
+ }
177
+ };
178
+ return {
179
+ async next() {
180
+ try {
181
+ options.signal?.throwIfAborted();
182
+ if (snapshot) {
183
+ const entry = snapshot[index++];
184
+ if (!entry)
185
+ return;
186
+ return options.admitEntry() ? { kind: "entry", entry } : { kind: "limit", name: entry.name };
187
+ }
188
+ if (names) {
189
+ if (preparedIndex >= prepared.length && !pendingFailure && pendingLimit === undefined) {
190
+ await prepareBatch(names);
191
+ }
192
+ const entry = prepared[preparedIndex++];
193
+ if (entry)
194
+ return { kind: "entry", entry };
195
+ if (pendingFailure)
196
+ throw pendingFailure.error;
197
+ if (pendingLimit !== undefined)
198
+ return { kind: "limit", name: pendingLimit };
199
+ return;
200
+ }
201
+ await assertCurrent();
202
+ const name = (await handle.read())?.name;
203
+ await assertCurrent();
204
+ if (name === undefined)
205
+ return;
206
+ if (!options.admitEntry())
207
+ return { kind: "limit", name };
208
+ // The stream's post-read fence is also the pre-stat fence in this owned operation.
209
+ const stat = fsSync.lstatSync(path.join(guard.realPath, name));
210
+ await assertCurrent();
211
+ return { kind: "entry", entry: { name, ...pathStatFromStats(stat) } };
212
+ }
213
+ catch (error) {
214
+ throw normalizeDirectoryError(error);
215
+ }
216
+ },
217
+ [Symbol.asyncDispose]: close,
218
+ };
219
+ }
@@ -0,0 +1,11 @@
1
+ import { type RootContext } from "./root-context.js";
2
+ import { type SymlinkPolicy } from "./root-symlink-policy.js";
3
+ import type { DirEntry } from "./types.js";
4
+ export type RootEntriesOptions = {
5
+ maxEntries?: number;
6
+ order?: "filesystem" | "sorted";
7
+ signal?: AbortSignal;
8
+ symlinks?: SymlinkPolicy;
9
+ };
10
+ export declare function entriesInRoot(root: RootContext, relativePath: string, options: RootEntriesOptions): AsyncGenerator<DirEntry>;
11
+ //# sourceMappingURL=root-entries.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"root-entries.d.ts","sourceRoot":"","sources":["../src/root-entries.ts"],"names":[],"mappings":"AAEA,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAExE,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AAErF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAE3C,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,YAAY,GAAG,QAAQ,CAAC;IAChC,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B,CAAC;AAEF,wBAAuB,aAAa,CAClC,IAAI,EAAE,WAAW,EACjB,YAAY,EAAE,MAAM,EACpB,OAAO,EAAE,kBAAkB,GAC1B,cAAc,CAAC,QAAQ,CAAC,CAkD1B"}
@@ -0,0 +1,61 @@
1
+ import { FsSafeError } from "./errors.js";
2
+ import { validatePinnedOperationPayload } from "./pinned-operation.js";
3
+ import { resolvePathInRoot } from "./root-context.js";
4
+ import { openRootDirectoryListing } from "./root-directory-list.js";
5
+ import { readSymlinkResolution } from "./root-symlink-policy.js";
6
+ import { createSuppressedError } from "./suppressed-error.js";
7
+ export async function* entriesInRoot(root, relativePath, options) {
8
+ const maxEntries = options.maxEntries;
9
+ if (maxEntries !== undefined && (!Number.isSafeInteger(maxEntries) || maxEntries < 0)) {
10
+ throw new RangeError("maxEntries must be a non-negative safe integer");
11
+ }
12
+ if (options.order !== undefined && options.order !== "filesystem" && options.order !== "sorted") {
13
+ throw new TypeError(`invalid root entries order: ${String(options.order)}`);
14
+ }
15
+ options.signal?.throwIfAborted();
16
+ validatePinnedOperationPayload({ relativePath });
17
+ const resolved = await resolvePathInRoot(root, relativePath, {
18
+ ...readSymlinkResolution(options.symlinks),
19
+ resolveCanonical: true,
20
+ });
21
+ options.signal?.throwIfAborted();
22
+ let examined = 0;
23
+ const listing = await openRootDirectoryListing(root, resolved.resolved, {
24
+ order: options.order ?? "filesystem",
25
+ signal: options.signal,
26
+ snapshot: false,
27
+ maxNames: maxEntries,
28
+ metadataBatchSize: 1,
29
+ admitEntry: () => maxEntries === undefined || examined++ < maxEntries,
30
+ });
31
+ let failed = false;
32
+ let operationError;
33
+ try {
34
+ while (true) {
35
+ const next = await listing.next();
36
+ options.signal?.throwIfAborted();
37
+ if (next === undefined)
38
+ return;
39
+ if (next.kind === "limit") {
40
+ throw new FsSafeError("too-large", "directory entry budget exceeded");
41
+ }
42
+ yield next.entry;
43
+ }
44
+ }
45
+ catch (error) {
46
+ failed = true;
47
+ operationError = error;
48
+ throw error;
49
+ }
50
+ finally {
51
+ try {
52
+ await listing[Symbol.asyncDispose]();
53
+ }
54
+ catch (closeError) {
55
+ if (failed) {
56
+ throw createSuppressedError(closeError, operationError, "directory iteration and close both failed");
57
+ }
58
+ throw closeError;
59
+ }
60
+ }
61
+ }
@@ -1,12 +1,13 @@
1
- import { FsSafeError } from "./errors.js";
2
- export declare function fileNotFoundError(cause?: unknown): FsSafeError;
1
+ import { FsSafeError, type FsSafeErrorDetails } from "./errors.js";
2
+ export declare function fileNotFoundError(cause?: unknown, details?: FsSafeErrorDetails): FsSafeError;
3
3
  export declare function outsideWorkspaceError(): FsSafeError;
4
+ export declare function rootPathChangedError(cause?: Error): FsSafeError;
4
5
  export declare function directoryComponentNotDirectoryError(cause?: unknown): FsSafeError;
5
6
  export declare function hardlinkedPathNotAllowedError(): FsSafeError;
6
7
  export declare function isAlreadyExistsError(error: unknown): boolean;
7
8
  export declare function normalizePinnedWriteError(error: unknown): Error;
8
- export declare function normalizePinnedPathError(error: unknown): Error;
9
- export declare function normalizeRemoveGuardError(error: unknown): Error;
10
- export declare function normalizeRemovePathError(error: unknown): Error;
9
+ export declare function normalizePinnedPathError(error: unknown, details?: FsSafeErrorDetails): Error;
10
+ export declare function normalizeRemoveGuardError(error: unknown, details?: FsSafeErrorDetails): Error;
11
+ export declare function normalizeRemovePathError(error: unknown, details?: FsSafeErrorDetails): Error;
11
12
  export declare function throwFsSafeReadError(error: unknown, label: string): never;
12
13
  //# sourceMappingURL=root-errors.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"root-errors.d.ts","sourceRoot":"","sources":["../src/root-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAK1C,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,WAAW,CAI9D;AAED,wBAAgB,qBAAqB,IAAI,WAAW,CAEnD;AAED,wBAAgB,mCAAmC,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,WAAW,CAIhF;AAED,wBAAgB,6BAA6B,IAAI,WAAW,CAE3D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAU/D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAO9D;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAQ/D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAe9D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAQzE"}
1
+ {"version":3,"file":"root-errors.d.ts","sourceRoot":"","sources":["../src/root-errors.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,KAAK,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAKnE,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,WAAW,CAI5F;AAED,wBAAgB,qBAAqB,IAAI,WAAW,CAEnD;AAED,wBAAgB,oBAAoB,CAAC,KAAK,CAAC,EAAE,KAAK,GAAG,WAAW,CAE/D;AAED,wBAAgB,mCAAmC,CAAC,KAAK,CAAC,EAAE,OAAO,GAAG,WAAW,CAIhF;AAED,wBAAgB,6BAA6B,IAAI,WAAW,CAE3D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE5D;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,CAU/D;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,KAAK,CAQ5F;AAED,wBAAgB,yBAAyB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,KAAK,CAQ7F;AAED,wBAAgB,wBAAwB,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,kBAAkB,GAAG,KAAK,CAe5F;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAQzE"}
@@ -1,14 +1,17 @@
1
1
  import { FsSafeError } from "./errors.js";
2
2
  import { hasNodeErrorCode, isNodeError, isNotFoundPathError } from "./path.js";
3
3
  const REMOVE_NOT_EMPTY_CODES = new Set(["ENOTEMPTY", "EEXIST"]);
4
- export function fileNotFoundError(cause) {
4
+ export function fileNotFoundError(cause, details) {
5
5
  return cause === undefined
6
- ? new FsSafeError("not-found", "file not found")
7
- : new FsSafeError("not-found", "file not found", { cause });
6
+ ? new FsSafeError("not-found", "file not found", { details })
7
+ : new FsSafeError("not-found", "file not found", { cause, details });
8
8
  }
9
9
  export function outsideWorkspaceError() {
10
10
  return new FsSafeError("outside-workspace", "file is outside workspace root");
11
11
  }
12
+ export function rootPathChangedError(cause) {
13
+ return new FsSafeError("path-mismatch", "root path changed during operation", { cause });
14
+ }
12
15
  export function directoryComponentNotDirectoryError(cause) {
13
16
  return cause === undefined
14
17
  ? new FsSafeError("not-file", "directory component must be a directory")
@@ -31,38 +34,39 @@ export function normalizePinnedWriteError(error) {
31
34
  cause: error instanceof Error ? error : undefined,
32
35
  });
33
36
  }
34
- export function normalizePinnedPathError(error) {
37
+ export function normalizePinnedPathError(error, details) {
35
38
  if (error instanceof FsSafeError) {
36
39
  return error;
37
40
  }
38
41
  return new FsSafeError("path-alias", "path is not under root", {
39
42
  cause: error instanceof Error ? error : undefined,
43
+ details,
40
44
  });
41
45
  }
42
- export function normalizeRemoveGuardError(error) {
46
+ export function normalizeRemoveGuardError(error, details) {
43
47
  if (error instanceof FsSafeError) {
44
48
  return error;
45
49
  }
46
50
  if (isNotFoundPathError(error)) {
47
- return fileNotFoundError(error instanceof Error ? error : undefined);
51
+ return fileNotFoundError(error instanceof Error ? error : undefined, details);
48
52
  }
49
- return normalizePinnedPathError(error);
53
+ return normalizePinnedPathError(error, details);
50
54
  }
51
- export function normalizeRemovePathError(error) {
55
+ export function normalizeRemovePathError(error, details) {
52
56
  if (error instanceof FsSafeError) {
53
57
  return error;
54
58
  }
55
59
  if (!isNodeError(error) || typeof error.code !== "string") {
56
- return normalizePinnedPathError(error);
60
+ return normalizePinnedPathError(error, details);
57
61
  }
58
62
  const cause = error instanceof Error ? error : undefined;
59
63
  if (isNotFoundPathError(error)) {
60
- return fileNotFoundError(cause);
64
+ return fileNotFoundError(cause, details);
61
65
  }
62
66
  if (REMOVE_NOT_EMPTY_CODES.has(error.code)) {
63
- return new FsSafeError("not-empty", "directory is not empty", { cause });
67
+ return new FsSafeError("not-empty", "directory is not empty", { cause, details });
64
68
  }
65
- return new FsSafeError("not-removable", "path could not be removed", { cause });
69
+ return new FsSafeError("not-removable", "path could not be removed", { cause, details });
66
70
  }
67
71
  export function throwFsSafeReadError(error, label) {
68
72
  if (error instanceof FsSafeError) {
@@ -1,5 +1,6 @@
1
1
  import fs from "node:fs";
2
2
  import type { PathAliasPolicy } from "./path-policy.js";
3
+ import { type SymlinkPolicy } from "./root-symlink-policy.js";
3
4
  import { type PinnedOpenSyncAllowedType, type PinnedOpenSyncFailureReason } from "./pinned-open.js";
4
5
  type BoundaryReadFs = Pick<typeof fs, "closeSync" | "constants" | "fstatSync" | "lstatSync" | "openSync" | "readFileSync" | "realpathSync">;
5
6
  export type RootFileOpenFailureReason = PinnedOpenSyncFailureReason | "validation";
@@ -25,6 +26,7 @@ export type OpenRootFileSyncParams = {
25
26
  maxBytes?: number;
26
27
  rejectHardlinks?: boolean;
27
28
  rejectSymlinks?: boolean;
29
+ symlinks?: SymlinkPolicy;
28
30
  allowedType?: PinnedOpenSyncAllowedType;
29
31
  skipLexicalRootCheck?: boolean;
30
32
  ioFs?: BoundaryReadFs;
@@ -1 +1 @@
1
- {"version":3,"file":"root-file.d.ts","sourceRoot":"","sources":["../src/root-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAOzB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EACjC,MAAM,kBAAkB,CAAC;AAE1B,KAAK,cAAc,GAAG,IAAI,CACxB,OAAO,EAAE,EACP,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,UAAU,GACV,cAAc,GACd,cAAc,CACjB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,2BAA2B,GAAG,YAAY,CAAC;AAEnF,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC5E;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,yBAAyB,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtE,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,EAAE;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAE7E,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,WAAW,CAAC,EAAE,yBAAyB,CAAC;IACxC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,sBAAsB,GAAG;IACxD,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B,CAAC;AAQF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAW3D;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,kBAAkB,CAuBnF;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE;IACR,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;IAC3C,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;IACjD,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;IACzC,QAAQ,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;CAC/C,GACA,CAAC,CAUH;AAoDD,wBAAsB,YAAY,CAChC,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,kBAAkB,CAAC,CAwB7B"}
1
+ {"version":3,"file":"root-file.d.ts","sourceRoot":"","sources":["../src/root-file.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAOzB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACxD,OAAO,EAAyB,KAAK,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACrF,OAAO,EAEL,KAAK,yBAAyB,EAC9B,KAAK,2BAA2B,EACjC,MAAM,kBAAkB,CAAC;AAE1B,KAAK,cAAc,GAAG,IAAI,CACxB,OAAO,EAAE,EACP,WAAW,GACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,UAAU,GACV,cAAc,GACd,cAAc,CACjB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG,2BAA2B,GAAG,YAAY,CAAC;AAEnF,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAC;IAAC,YAAY,EAAE,MAAM,CAAA;CAAE,GAC5E;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,yBAAyB,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtE,MAAM,MAAM,mBAAmB,GAAG,OAAO,CAAC,kBAAkB,EAAE;IAAE,EAAE,EAAE,KAAK,CAAA;CAAE,CAAC,CAAC;AAE7E,MAAM,MAAM,sBAAsB,GAAG;IACnC,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,WAAW,CAAC,EAAE,yBAAyB,CAAC;IACxC,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG,sBAAsB,GAAG;IACxD,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B,CAAC;AAQF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,GAAG,OAAO,CAW3D;AASD,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,sBAAsB,GAAG,kBAAkB,CAuBnF;AAED,wBAAgB,wBAAwB,CAAC,CAAC,EACxC,OAAO,EAAE,mBAAmB,EAC5B,QAAQ,EAAE;IACR,IAAI,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;IAC3C,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;IACjD,EAAE,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;IACzC,QAAQ,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,CAAC,CAAC;CAC/C,GACA,CAAC,CAUH;AAoDD,wBAAsB,YAAY,CAChC,MAAM,EAAE,kBAAkB,GACzB,OAAO,CAAC,kBAAkB,CAAC,CAwB7B"}
package/dist/root-file.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { resolveRootPath, resolveRootPathSync, } from "./root-path.js";
4
+ import { readSymlinkResolution } from "./root-symlink-policy.js";
4
5
  import { openPinnedFileSync, } from "./pinned-open.js";
5
6
  export function canUseRootFileOpen(ioFs) {
6
7
  return (typeof ioFs.openSync === "function" &&
@@ -12,9 +13,16 @@ export function canUseRootFileOpen(ioFs) {
12
13
  typeof ioFs.constants === "object" &&
13
14
  ioFs.constants !== null);
14
15
  }
16
+ function absoluteRootFilePath(filePath) {
17
+ if (path.isAbsolute(filePath))
18
+ return filePath;
19
+ const drive = path.parse(filePath).root;
20
+ const base = drive ? path.resolve(drive) : process.cwd();
21
+ return `${base}${path.sep}${filePath.slice(drive.length)}`;
22
+ }
15
23
  export function openRootFileSync(params) {
16
24
  const ioFs = params.ioFs ?? fs;
17
- const absolutePath = path.resolve(params.absolutePath);
25
+ const absolutePath = absoluteRootFilePath(params.absolutePath);
18
26
  let resolved;
19
27
  try {
20
28
  resolved = mapResolvedRootPath(absolutePath, resolveRootPathSync({
@@ -22,7 +30,7 @@ export function openRootFileSync(params) {
22
30
  rootPath: params.rootPath,
23
31
  rootCanonicalPath: params.rootRealPath,
24
32
  boundaryLabel: params.boundaryLabel,
25
- rejectSymlinks: params.rejectSymlinks ?? true,
33
+ ...readSymlinkResolution(params.symlinks ?? (params.rejectSymlinks === false ? "follow-within-root" : "reject")),
26
34
  skipLexicalRootCheck: params.skipLexicalRootCheck,
27
35
  }));
28
36
  }
@@ -84,7 +92,7 @@ function finalizeRootFileOpen(params) {
84
92
  }
85
93
  export async function openRootFile(params) {
86
94
  const ioFs = params.ioFs ?? fs;
87
- const absolutePath = path.resolve(params.absolutePath);
95
+ const absolutePath = absoluteRootFilePath(params.absolutePath);
88
96
  let resolved;
89
97
  try {
90
98
  resolved = mapResolvedRootPath(absolutePath, await resolveRootPath({
@@ -93,7 +101,7 @@ export async function openRootFile(params) {
93
101
  rootCanonicalPath: params.rootRealPath,
94
102
  boundaryLabel: params.boundaryLabel,
95
103
  policy: params.aliasPolicy,
96
- rejectSymlinks: params.rejectSymlinks ?? true,
104
+ ...readSymlinkResolution(params.symlinks ?? (params.rejectSymlinks === false ? "follow-within-root" : "reject")),
97
105
  skipLexicalRootCheck: params.skipLexicalRootCheck,
98
106
  }));
99
107
  }