@openclaw/fs-safe 0.8.6 → 0.10.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 (246) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +17 -0
  3. package/dist/advanced.d.ts +1 -0
  4. package/dist/advanced.d.ts.map +1 -1
  5. package/dist/advanced.js +1 -0
  6. package/dist/archive-crc32.d.ts.map +1 -1
  7. package/dist/archive-crc32.js +6 -1
  8. package/dist/archive-deadline.d.ts.map +1 -1
  9. package/dist/archive-deadline.js +3 -4
  10. package/dist/archive-durability.d.ts +24 -0
  11. package/dist/archive-durability.d.ts.map +1 -0
  12. package/dist/archive-durability.js +180 -0
  13. package/dist/archive-gzip-tail.d.ts +2 -0
  14. package/dist/archive-gzip-tail.d.ts.map +1 -1
  15. package/dist/archive-gzip-tail.js +22 -4
  16. package/dist/archive-input.js +4 -4
  17. package/dist/archive-kind.d.ts.map +1 -1
  18. package/dist/archive-kind.js +3 -2
  19. package/dist/archive-merge.d.ts +1 -0
  20. package/dist/archive-merge.d.ts.map +1 -1
  21. package/dist/archive-merge.js +42 -9
  22. package/dist/archive-native.d.ts +1 -0
  23. package/dist/archive-native.d.ts.map +1 -1
  24. package/dist/archive-native.js +4 -2
  25. package/dist/archive-options.d.ts +2 -0
  26. package/dist/archive-options.d.ts.map +1 -1
  27. package/dist/archive-parser.wasm +0 -0
  28. package/dist/archive-read.d.ts.map +1 -1
  29. package/dist/archive-read.js +72 -70
  30. package/dist/archive-staging.d.ts +1 -1
  31. package/dist/archive-staging.d.ts.map +1 -1
  32. package/dist/archive-staging.js +24 -19
  33. package/dist/archive-tar-stream.d.ts +11 -4
  34. package/dist/archive-tar-stream.d.ts.map +1 -1
  35. package/dist/archive-tar-stream.js +20 -8
  36. package/dist/archive-tar-wasm.d.ts.map +1 -1
  37. package/dist/archive-tar-wasm.js +2 -1
  38. package/dist/archive.d.ts.map +1 -1
  39. package/dist/archive.js +9 -4
  40. package/dist/bounded-read.d.ts +7 -0
  41. package/dist/bounded-read.d.ts.map +1 -1
  42. package/dist/bounded-read.js +73 -43
  43. package/dist/clone-metadata.d.ts +19 -0
  44. package/dist/clone-metadata.d.ts.map +1 -0
  45. package/dist/clone-metadata.js +32 -0
  46. package/dist/copy-file-input.d.ts +23 -0
  47. package/dist/copy-file-input.d.ts.map +1 -0
  48. package/dist/copy-file-input.js +91 -0
  49. package/dist/copy-policy.d.ts +3 -0
  50. package/dist/copy-policy.d.ts.map +1 -0
  51. package/dist/copy-policy.js +8 -0
  52. package/dist/copy-publication.d.ts +15 -0
  53. package/dist/copy-publication.d.ts.map +1 -0
  54. package/dist/copy-publication.js +30 -0
  55. package/dist/copy-tree-portable.d.ts +9 -0
  56. package/dist/copy-tree-portable.d.ts.map +1 -0
  57. package/dist/copy-tree-portable.js +191 -0
  58. package/dist/copy.d.ts +16 -0
  59. package/dist/copy.d.ts.map +1 -0
  60. package/dist/copy.js +123 -0
  61. package/dist/directory-mode-node.d.ts.map +1 -1
  62. package/dist/directory-mode-node.js +4 -4
  63. package/dist/durability.d.ts +1 -1
  64. package/dist/durability.d.ts.map +1 -1
  65. package/dist/error-detail.d.ts.map +1 -1
  66. package/dist/error-detail.js +4 -1
  67. package/dist/file-hash.d.ts +6 -2
  68. package/dist/file-hash.d.ts.map +1 -1
  69. package/dist/file-hash.js +49 -12
  70. package/dist/file-store-sync-write.d.ts +1 -0
  71. package/dist/file-store-sync-write.d.ts.map +1 -1
  72. package/dist/file-store-sync-write.js +9 -6
  73. package/dist/file-store.d.ts +4 -0
  74. package/dist/file-store.d.ts.map +1 -1
  75. package/dist/file-store.js +10 -1
  76. package/dist/filename.d.ts.map +1 -1
  77. package/dist/filename.js +2 -12
  78. package/dist/fs.d.ts.map +1 -1
  79. package/dist/fs.js +1 -2
  80. package/dist/guarded-mkdir.d.ts +1 -0
  81. package/dist/guarded-mkdir.d.ts.map +1 -1
  82. package/dist/guarded-mkdir.js +1 -0
  83. package/dist/guarded-mutation.d.ts +2 -0
  84. package/dist/guarded-mutation.d.ts.map +1 -1
  85. package/dist/guarded-mutation.js +9 -5
  86. package/dist/home-dir.d.ts.map +1 -1
  87. package/dist/home-dir.js +9 -7
  88. package/dist/index.d.ts +1 -1
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/install-path.d.ts.map +1 -1
  91. package/dist/install-path.js +9 -13
  92. package/dist/json-document-store.d.ts +3 -0
  93. package/dist/json-document-store.d.ts.map +1 -1
  94. package/dist/json-document-store.js +1 -0
  95. package/dist/json-durable-queue-directory.js +3 -3
  96. package/dist/json-durable-queue-ownership.js +3 -2
  97. package/dist/json-durable-queue.d.ts.map +1 -1
  98. package/dist/json-durable-queue.js +31 -27
  99. package/dist/local-roots.d.ts.map +1 -1
  100. package/dist/local-roots.js +22 -24
  101. package/dist/move-path-cleanup.d.ts.map +1 -1
  102. package/dist/move-path-cleanup.js +4 -3
  103. package/dist/move-path-stage.d.ts +8 -0
  104. package/dist/move-path-stage.d.ts.map +1 -0
  105. package/dist/move-path-stage.js +55 -0
  106. package/dist/move-path.d.ts.map +1 -1
  107. package/dist/move-path.js +55 -47
  108. package/dist/mutation-authority.d.ts +8 -0
  109. package/dist/mutation-authority.d.ts.map +1 -0
  110. package/dist/mutation-authority.js +36 -0
  111. package/dist/native-binding.d.ts +24 -1
  112. package/dist/native-binding.d.ts.map +1 -1
  113. package/dist/native-operations.d.ts +1 -1
  114. package/dist/native-operations.d.ts.map +1 -1
  115. package/dist/native-operations.js +11 -5
  116. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  117. package/dist/native-pinned-write-windows.js +18 -7
  118. package/dist/native-pinned-write.d.ts.map +1 -1
  119. package/dist/native-pinned-write.js +1 -0
  120. package/dist/native-staged-file.d.ts +3 -3
  121. package/dist/native-staged-file.d.ts.map +1 -1
  122. package/dist/native-staged-file.js +35 -8
  123. package/dist/opened-file-failure.d.ts +1 -1
  124. package/dist/opened-file-failure.d.ts.map +1 -1
  125. package/dist/opened-file-failure.js +3 -2
  126. package/dist/path.d.ts.map +1 -1
  127. package/dist/path.js +3 -1
  128. package/dist/permissions-windows.d.ts.map +1 -1
  129. package/dist/permissions-windows.js +38 -48
  130. package/dist/permissions.js +3 -3
  131. package/dist/pinned-operation.js +1 -1
  132. package/dist/pinned-write.d.ts +5 -1
  133. package/dist/pinned-write.d.ts.map +1 -1
  134. package/dist/pinned-write.js +50 -30
  135. package/dist/positional-read.d.ts +9 -0
  136. package/dist/positional-read.d.ts.map +1 -0
  137. package/dist/positional-read.js +36 -0
  138. package/dist/private-temp-workspace.d.ts.map +1 -1
  139. package/dist/private-temp-workspace.js +9 -3
  140. package/dist/publish-copy-stage.d.ts +13 -0
  141. package/dist/publish-copy-stage.d.ts.map +1 -0
  142. package/dist/publish-copy-stage.js +47 -0
  143. package/dist/publish-file.d.ts.map +1 -1
  144. package/dist/publish-file.js +26 -25
  145. package/dist/replace-file-copy-fallback.d.ts.map +1 -1
  146. package/dist/replace-file-copy-fallback.js +30 -17
  147. package/dist/replace-file-copy-source.d.ts.map +1 -1
  148. package/dist/replace-file-copy-source.js +10 -5
  149. package/dist/replace-file-temp-owner.d.ts.map +1 -1
  150. package/dist/replace-file-temp-owner.js +6 -2
  151. package/dist/replace-file.d.ts.map +1 -1
  152. package/dist/replace-file.js +8 -3
  153. package/dist/root-context.d.ts +3 -0
  154. package/dist/root-context.d.ts.map +1 -1
  155. package/dist/root-context.js +51 -26
  156. package/dist/root-directory-list.d.ts +24 -0
  157. package/dist/root-directory-list.d.ts.map +1 -0
  158. package/dist/root-directory-list.js +201 -0
  159. package/dist/root-errors.d.ts +1 -0
  160. package/dist/root-errors.d.ts.map +1 -1
  161. package/dist/root-errors.js +3 -0
  162. package/dist/root-file.d.ts +2 -0
  163. package/dist/root-file.d.ts.map +1 -1
  164. package/dist/root-file.js +12 -4
  165. package/dist/root-impl.d.ts +6 -50
  166. package/dist/root-impl.d.ts.map +1 -1
  167. package/dist/root-impl.js +342 -208
  168. package/dist/root-options.d.ts +66 -0
  169. package/dist/root-options.d.ts.map +1 -0
  170. package/dist/root-options.js +18 -0
  171. package/dist/root-path-existing.d.ts +5 -0
  172. package/dist/root-path-existing.d.ts.map +1 -1
  173. package/dist/root-path-existing.js +56 -1
  174. package/dist/root-path.d.ts +1 -0
  175. package/dist/root-path.d.ts.map +1 -1
  176. package/dist/root-path.js +74 -162
  177. package/dist/root-symlink-policy.d.ts +13 -0
  178. package/dist/root-symlink-policy.d.ts.map +1 -0
  179. package/dist/root-symlink-policy.js +34 -0
  180. package/dist/root-walk.d.ts +5 -4
  181. package/dist/root-walk.d.ts.map +1 -1
  182. package/dist/root-walk.js +99 -50
  183. package/dist/root-write-mode.d.ts.map +1 -1
  184. package/dist/root-write-mode.js +1 -4
  185. package/dist/root.d.ts +3 -1
  186. package/dist/root.d.ts.map +1 -1
  187. package/dist/secret-file.d.ts +1 -0
  188. package/dist/secret-file.d.ts.map +1 -1
  189. package/dist/secret-file.js +1 -0
  190. package/dist/secret-read-async.js +5 -5
  191. package/dist/secure-file.d.ts +1 -1
  192. package/dist/secure-file.d.ts.map +1 -1
  193. package/dist/secure-file.js +23 -13
  194. package/dist/sibling-staged-file.js +7 -7
  195. package/dist/sibling-temp.d.ts.map +1 -1
  196. package/dist/sibling-temp.js +16 -7
  197. package/dist/sidecar-lock-acquire.d.ts +1 -1
  198. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  199. package/dist/sidecar-lock-acquire.js +6 -4
  200. package/dist/sidecar-lock-policy.d.ts.map +1 -1
  201. package/dist/sidecar-lock-policy.js +5 -3
  202. package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
  203. package/dist/sidecar-lock-reclaim.js +16 -4
  204. package/dist/sidecar-lock.d.ts.map +1 -1
  205. package/dist/sidecar-lock.js +2 -1
  206. package/dist/temp-cleanup.d.ts.map +1 -1
  207. package/dist/temp-cleanup.js +3 -2
  208. package/dist/temp-target.d.ts.map +1 -1
  209. package/dist/temp-target.js +9 -4
  210. package/dist/timing.d.ts +1 -0
  211. package/dist/timing.d.ts.map +1 -1
  212. package/dist/timing.js +25 -6
  213. package/dist/trash.js +2 -2
  214. package/dist/walk.d.ts.map +1 -1
  215. package/dist/walk.js +7 -27
  216. package/dist/windows-owner.d.ts +9 -12
  217. package/dist/windows-owner.d.ts.map +1 -1
  218. package/dist/windows-owner.js +24 -58
  219. package/dist/write-file-handle.d.ts +6 -0
  220. package/dist/write-file-handle.d.ts.map +1 -0
  221. package/dist/write-file-handle.js +25 -0
  222. package/dist/write-open-flags.d.ts.map +1 -1
  223. package/dist/write-open-flags.js +1 -2
  224. package/docs/advanced.md +18 -4
  225. package/docs/archive.md +57 -9
  226. package/docs/atomic.md +7 -0
  227. package/docs/contributing.md +7 -0
  228. package/docs/copy.md +86 -0
  229. package/docs/durability.md +17 -2
  230. package/docs/errors.md +1 -1
  231. package/docs/file-store.md +48 -3
  232. package/docs/json-store.md +10 -0
  233. package/docs/local-roots.md +8 -1
  234. package/docs/native.md +14 -1
  235. package/docs/permissions.md +20 -10
  236. package/docs/positional-read.md +63 -0
  237. package/docs/reading.md +6 -0
  238. package/docs/root.md +168 -6
  239. package/docs/secret-file.md +6 -0
  240. package/docs/security-model.md +14 -0
  241. package/docs/sidecar-lock.md +2 -0
  242. package/docs/timing.md +2 -0
  243. package/docs/types.md +18 -11
  244. package/docs/walk.md +54 -5
  245. package/docs/writing.md +25 -5
  246. package/package.json +16 -11
@@ -17,6 +17,10 @@ import { resolveReadOpenFlags } from "./read-open-flags.js";
17
17
  import { cleanupPinnedFilePath } from "./replace-file-temp-owner.js";
18
18
  import { withSidecarLock } from "./sidecar-lock.js";
19
19
  import { getFsSafeTestHooks } from "./test-hooks.js";
20
+ import { writeAllToFile } from "./write-file-handle.js";
21
+ import { assertFinalSymlinkRejected } from "./root-symlink-policy.js";
22
+ import { writeCopyFileToFd } from "./copy-file-input.js";
23
+ import { publishCopyStage } from "./publish-copy-stage.js";
20
24
  function byteLength(input, encoding) {
21
25
  return typeof input === "string"
22
26
  ? Buffer.byteLength(input, encoding ?? "utf8")
@@ -36,20 +40,13 @@ function assertWithinMaxBytes(bytes, maxBytes) {
36
40
  throw new FsSafeError("too-large", `file exceeds limit of ${maxBytes} bytes (got at least ${bytes})`);
37
41
  }
38
42
  }
39
- async function writeStreamToHandle(stream, handle, maxBytes) {
43
+ async function writeStreamToHandle(stream, handle, maxBytes, assertBeforeMutation) {
40
44
  let bytes = 0;
41
45
  for await (const chunk of stream) {
42
46
  const buffer = Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk);
43
47
  bytes += buffer.byteLength;
44
48
  assertWithinMaxBytes(bytes, maxBytes);
45
- let offset = 0;
46
- while (offset < buffer.byteLength) {
47
- const { bytesWritten } = await handle.write(buffer, offset, buffer.byteLength - offset);
48
- if (bytesWritten <= 0) {
49
- throw new FsSafeError("helper-failed", "fallback stream write made no progress");
50
- }
51
- offset += bytesWritten;
52
- }
49
+ await writeAllToFile(handle, buffer, { assertBeforeMutation });
53
50
  }
54
51
  }
55
52
  export async function runPinnedWriteHelper(params) {
@@ -95,7 +92,6 @@ async function runPinnedWriteFallback(params) {
95
92
  ? { dev: params.rootIdentity.dev, ino: params.rootIdentity.ino } : undefined;
96
93
  if (exactRoot)
97
94
  await inspectDirectoryIdentity(params.rootPath, exactRoot);
98
- const guardOptions = { bigint: exactRoot !== undefined };
99
95
  let parentPath = params.relativeParentPath
100
96
  ? path.join(params.rootPath, ...params.relativeParentPath.split("/"))
101
97
  : params.rootPath;
@@ -107,15 +103,23 @@ async function runPinnedWriteFallback(params) {
107
103
  parentPath = await mkdirPathComponentsWithGuards({
108
104
  rootReal: params.rootPath,
109
105
  targetPath: parentPath,
106
+ assertBeforeMutation: params.assertBeforeMutation,
110
107
  beforeComponent: async (componentPath) => await getFsSafeTestHooks()?.beforeRootFallbackMutation?.("mkdir", componentPath),
111
108
  });
112
109
  }
113
110
  const parentGuard = params.mkdir
114
- ? await createAsyncDirectoryGuard(parentPath, guardOptions)
115
- : await createNearestExistingDirectoryGuard(params.rootPath, parentPath, guardOptions);
111
+ ? await createAsyncDirectoryGuard(parentPath, { bigint: true })
112
+ : await createNearestExistingDirectoryGuard(params.rootPath, parentPath, { bigint: true });
116
113
  const targetPath = path.join(parentPath, params.basename);
117
- if (params.overwrite === false) {
118
- const handle = await withAsyncDirectoryGuards([parentGuard], async () => await fs.open(targetPath, fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_EXCL, params.mode), {
114
+ if (params.overwrite === false && params.input.kind !== "file") {
115
+ const assertBeforeMutation = () => {
116
+ assertFinalSymlinkRejected(targetPath, params.rejectFinalSymlink);
117
+ params.assertBeforeMutation?.();
118
+ };
119
+ const handle = await withAsyncDirectoryGuards([parentGuard], async () => {
120
+ assertBeforeMutation();
121
+ return await fs.open(targetPath, fsSync.constants.O_WRONLY | fsSync.constants.O_CREAT | fsSync.constants.O_EXCL, params.mode);
122
+ }, {
119
123
  onPostGuardFailure: async (openedHandle) => {
120
124
  // The parent failed verification, so targetPath may now resolve
121
125
  // somewhere else. Close the fd, but do not clean up by path.
@@ -129,15 +133,12 @@ async function runPinnedWriteFallback(params) {
129
133
  createdIdentity = verificationIdentity;
130
134
  if (params.input.kind === "buffer") {
131
135
  assertWithinMaxBytes(byteLength(params.input.data, params.input.encoding), params.maxBytes);
132
- if (typeof params.input.data === "string") {
133
- await handle.writeFile(params.input.data, params.input.encoding ?? "utf8");
134
- }
135
- else {
136
- await handle.writeFile(params.input.data);
137
- }
136
+ await writeAllToFile(handle, params.input.data, {
137
+ encoding: params.input.encoding, assertBeforeMutation,
138
+ });
138
139
  }
139
140
  else {
140
- await writeStreamToHandle(params.input.stream, handle, params.maxBytes);
141
+ await writeStreamToHandle(params.input.stream, handle, params.maxBytes, assertBeforeMutation);
141
142
  }
142
143
  // Content writes may clear set-ID bits; finalize them through the owned fd.
143
144
  await handle.chmod(params.mode);
@@ -176,20 +177,21 @@ async function runPinnedWriteFallback(params) {
176
177
  let readHandle;
177
178
  let renamed = false;
178
179
  try {
180
+ params.assertBeforeMutation?.();
179
181
  handle = await fs.open(tempPath, tempFlags, params.mode);
180
182
  let verificationIdentity = fsSync.fstatSync(handle.fd, { bigint: true });
181
183
  tempIdentity = verificationIdentity;
182
184
  if (params.input.kind === "buffer") {
183
185
  assertWithinMaxBytes(byteLength(params.input.data, params.input.encoding), params.maxBytes);
184
- if (typeof params.input.data === "string") {
185
- await handle.writeFile(params.input.data, params.input.encoding ?? "utf8");
186
- }
187
- else {
188
- await handle.writeFile(params.input.data);
189
- }
186
+ await writeAllToFile(handle, params.input.data, {
187
+ encoding: params.input.encoding, assertBeforeMutation: params.assertBeforeMutation,
188
+ });
189
+ }
190
+ else if (params.input.kind === "file") {
191
+ await writeCopyFileToFd(handle.fd, params.input, params.maxBytes, params.assertBeforeMutation);
190
192
  }
191
193
  else {
192
- await writeStreamToHandle(params.input.stream, handle, params.maxBytes);
194
+ await writeStreamToHandle(params.input.stream, handle, params.maxBytes, params.assertBeforeMutation);
193
195
  }
194
196
  tempStat = fsSync.fstatSync(handle.fd);
195
197
  const tempPathStat = fsSync.lstatSync(tempPath);
@@ -200,10 +202,28 @@ async function runPinnedWriteFallback(params) {
200
202
  await handle.chmod(params.mode);
201
203
  if (params.sync !== false)
202
204
  await syncFileBestEffort(handle);
205
+ if (params.input.kind === "file")
206
+ await params.input.verifySource();
203
207
  let verifiedIdentity = expectedTempStat;
204
208
  await withAsyncDirectoryGuards([parentGuard], async () => {
205
- await fs.rename(tempPath, targetPath);
206
- renamed = true;
209
+ assertFinalSymlinkRejected(targetPath, params.rejectFinalSymlink);
210
+ if (params.overwrite === false) {
211
+ publishCopyStage({
212
+ temporaryPath: tempPath, targetPath, fd: handle.fd,
213
+ identity: tempIdentity, parentGuard,
214
+ assertBeforeMutation: params.assertBeforeMutation,
215
+ onPublished: (identity) => {
216
+ renamed = true;
217
+ params.onPublished?.(identity);
218
+ },
219
+ });
220
+ }
221
+ else {
222
+ params.assertBeforeMutation?.();
223
+ await fs.rename(tempPath, targetPath);
224
+ renamed = true;
225
+ params.onPublished?.(verificationIdentity);
226
+ }
207
227
  await getFsSafeTestHooks()?.afterPinnedWriteFallbackRename?.(targetPath);
208
228
  if (params.sync !== false)
209
229
  await syncDirectoryBestEffort(parentPath);
@@ -0,0 +1,9 @@
1
+ import type { FileHandle } from "node:fs/promises";
2
+ export type ReadFileWindowOptions = {
3
+ signal?: AbortSignal;
4
+ };
5
+ /** Fills a caller-owned buffer from an explicit position, stopping at EOF. */
6
+ export declare function readFileWindowFully(handle: FileHandle, buffer: Buffer, position: number, { signal }?: ReadFileWindowOptions): Promise<number>;
7
+ /** Synchronous positional read; neither variant advances or closes its descriptor. */
8
+ export declare function readFileWindowFullySync(fd: number, buffer: Buffer, position: number): number;
9
+ //# sourceMappingURL=positional-read.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"positional-read.d.ts","sourceRoot":"","sources":["../src/positional-read.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAKnD,MAAM,MAAM,qBAAqB,GAAG;IAClC,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,CAAC;AASF,8EAA8E;AAC9E,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,UAAU,EAClB,MAAM,EAAE,MAAM,EACd,QAAQ,EAAE,MAAM,EAChB,EAAE,MAAM,EAAE,GAAE,qBAA0B,GACrC,OAAO,CAAC,MAAM,CAAC,CAcjB;AAED,sFAAsF;AACtF,wBAAgB,uBAAuB,CAAC,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAW5F"}
@@ -0,0 +1,36 @@
1
+ import fs from "node:fs";
2
+ // Node's synchronous read length is a signed 32-bit value on supported runtimes.
3
+ const MAX_READ_BYTES = 0x7fffffff;
4
+ function validateWindow(buffer, position) {
5
+ if (!Number.isSafeInteger(position) || position < 0 ||
6
+ !Number.isSafeInteger(position + buffer.length)) {
7
+ throw new RangeError("read position and window end must be non-negative safe integers");
8
+ }
9
+ }
10
+ /** Fills a caller-owned buffer from an explicit position, stopping at EOF. */
11
+ export async function readFileWindowFully(handle, buffer, position, { signal } = {}) {
12
+ validateWindow(buffer, position);
13
+ signal?.throwIfAborted();
14
+ let total = 0;
15
+ while (total < buffer.length) {
16
+ const { bytesRead } = await handle.read(buffer, total, Math.min(buffer.length - total, MAX_READ_BYTES), position + total);
17
+ // Settle the pending read before cancellation lets the caller reuse its buffer or handle.
18
+ signal?.throwIfAborted();
19
+ if (bytesRead === 0)
20
+ break;
21
+ total += bytesRead;
22
+ }
23
+ return total;
24
+ }
25
+ /** Synchronous positional read; neither variant advances or closes its descriptor. */
26
+ export function readFileWindowFullySync(fd, buffer, position) {
27
+ validateWindow(buffer, position);
28
+ let total = 0;
29
+ while (total < buffer.length) {
30
+ const bytesRead = fs.readSync(fd, buffer, total, Math.min(buffer.length - total, MAX_READ_BYTES), position + total);
31
+ if (bytesRead === 0)
32
+ break;
33
+ total += bytesRead;
34
+ }
35
+ return total;
36
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"private-temp-workspace.d.ts","sourceRoot":"","sources":["../src/private-temp-workspace.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"private-temp-workspace.d.ts","sourceRoot":"","sources":["../src/private-temp-workspace.ts"],"names":[],"mappings":"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"}
@@ -67,7 +67,7 @@ function throwTempWorkspaceOpenFailure(failure) {
67
67
  }
68
68
  async function ensurePrivateDirectory(dir, mode) {
69
69
  await fs.mkdir(dir, { recursive: true, mode });
70
- const stat = await fs.stat(dir);
70
+ const stat = fsSync.statSync(dir);
71
71
  if (!stat.isDirectory()) {
72
72
  throw new Error(`Temp root must be a directory: ${dir}`);
73
73
  }
@@ -91,7 +91,13 @@ async function createTempWorkspace(options) {
91
91
  const mode = options.mode ?? 0o600;
92
92
  const cleanupSafety = resolveTempWorkspaceCleanupSafety(options.cleanupSafety);
93
93
  const requestedRoot = path.resolve(options.rootDir);
94
- const root = await fs.realpath(requestedRoot).catch(() => requestedRoot);
94
+ let root = requestedRoot;
95
+ try {
96
+ root = fsSync.realpathSync.native(requestedRoot);
97
+ }
98
+ catch {
99
+ root = requestedRoot;
100
+ }
95
101
  await ensurePrivateDirectory(root, dirMode);
96
102
  const capability = new TempWorkspaceCleanupCapability(root, cleanupSafety);
97
103
  let dir;
@@ -101,7 +107,7 @@ async function createTempWorkspace(options) {
101
107
  try {
102
108
  dir = await fs.mkdtemp(path.join(root, sanitizeTempPrefix(options.prefix)));
103
109
  await fs.chmod(dir, dirMode).catch(() => undefined);
104
- stat = await fs.lstat(dir, { bigint: true });
110
+ stat = fsSync.lstatSync(dir, { bigint: true });
105
111
  if (stat.isSymbolicLink() || !stat.isDirectory()) {
106
112
  throw new Error(`Temp workspace must be a directory: ${dir}`);
107
113
  }
@@ -0,0 +1,13 @@
1
+ import { type BigIntStats } from "node:fs";
2
+ import type { AnyAsyncDirectoryGuard } from "./directory-guard.js";
3
+ import type { PublishedWriteIdentity } from "./pinned-write.js";
4
+ export declare function publishCopyStage(params: {
5
+ temporaryPath: string;
6
+ targetPath: string;
7
+ fd: number;
8
+ identity: BigIntStats;
9
+ parentGuard: AnyAsyncDirectoryGuard;
10
+ assertBeforeMutation?: () => void;
11
+ onPublished?: (identity: PublishedWriteIdentity) => void;
12
+ }): void;
13
+ //# sourceMappingURL=publish-copy-stage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"publish-copy-stage.d.ts","sourceRoot":"","sources":["../src/publish-copy-stage.ts"],"names":[],"mappings":"AAAA,OAAW,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAE/C,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAGnE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AAIhE,wBAAgB,gBAAgB,CAAC,MAAM,EAAE;IACvC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,WAAW,CAAC;IACtB,WAAW,EAAE,sBAAsB,CAAC;IACpC,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,sBAAsB,KAAK,IAAI,CAAC;CAC1D,GAAG,IAAI,CAqCP"}
@@ -0,0 +1,47 @@
1
+ import fs, {} from "node:fs";
2
+ import path from "node:path";
3
+ import { FsSafeError } from "./errors.js";
4
+ import { sameFileIdentityForCleanup } from "./file-identity.js";
5
+ // Node has no portable no-replace rename. Link only our completed private stage,
6
+ // then unlink it in the same JS turn so local readers never observe two names.
7
+ export function publishCopyStage(params) {
8
+ const { identity, temporaryPath, targetPath } = params;
9
+ const staged = fs.lstatSync(temporaryPath, { bigint: true });
10
+ const opened = fs.fstatSync(params.fd, { bigint: true });
11
+ if (!staged.isFile() || staged.isSymbolicLink() || staged.nlink !== 1n ||
12
+ !sameFileIdentityForCleanup(staged, identity) ||
13
+ !sameFileIdentityForCleanup(opened, identity)) {
14
+ throw new FsSafeError("path-mismatch", "exclusive copy stage changed before publication");
15
+ }
16
+ params.assertBeforeMutation?.();
17
+ fs.linkSync(temporaryPath, targetPath);
18
+ let observerRejected = false;
19
+ let observerError;
20
+ try {
21
+ params.onPublished?.(identity);
22
+ }
23
+ catch (error) {
24
+ observerRejected = true;
25
+ observerError = error;
26
+ }
27
+ try {
28
+ const parent = fs.lstatSync(path.dirname(temporaryPath), { bigint: true });
29
+ const current = fs.lstatSync(temporaryPath, { bigint: true });
30
+ if (parent.isSymbolicLink() || !parent.isDirectory() ||
31
+ !sameFileIdentityForCleanup(parent, params.parentGuard.stat) ||
32
+ current.isSymbolicLink() || !current.isFile() ||
33
+ !sameFileIdentityForCleanup(current, identity)) {
34
+ throw new FsSafeError("path-mismatch", "published copy temporary name changed before cleanup");
35
+ }
36
+ // This removes the stage's name, never the published destination or source.
37
+ fs.unlinkSync(temporaryPath);
38
+ }
39
+ catch (error) {
40
+ throw new FsSafeError("helper-failed", "published copy temporary cleanup failed", {
41
+ cause: observerRejected ? new AggregateError([observerError, error], "observer and cleanup failed") : error,
42
+ details: { publication: "published", path: targetPath, dev: identity.dev, ino: identity.ino, cleanup: "failed" },
43
+ });
44
+ }
45
+ if (observerRejected)
46
+ throw observerError;
47
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"publish-file.d.ts","sourceRoot":"","sources":["../src/publish-file.ts"],"names":[],"mappings":"AACA,OAAe,EAAoB,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAI/D,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EAC1B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAML,KAAK,qCAAqC,EAC3C,MAAM,2BAA2B,CAAC;AAGnC,YAAY,EACV,2BAA2B,EAC3B,wCAAwC,EACxC,kCAAkC,EAClC,gCAAgC,EAChC,qCAAqC,GACtC,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,4BAA4B,GACpC,cAAc,GACd,eAAe,GACf,kBAAkB,CAAC;AAEvB,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;IAC3D,QAAQ,EAAE,KAAK,CAAC;IAChB,aAAa,EAAE,oBAAoB,CAAC;CACrC,CAAC;AAkBF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE/D;AA0ND,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB,CAAC,EAAE,gBAAgB,CAAC;IAC1C,QAAQ,EAAE,4BAA4B,CAAC;IACvC,aAAa,CAAC,EAAE,qCAAqC,CAAC;IACtD,aAAa,CAAC,EAAE,gBAAgB,CAAC;CAClC,GAAG,OAAO,CAAC,0BAA0B,CAAC,CA4MtC"}
1
+ {"version":3,"file":"publish-file.d.ts","sourceRoot":"","sources":["../src/publish-file.ts"],"names":[],"mappings":"AACA,OAAe,EAAoB,KAAK,KAAK,EAAE,MAAM,SAAS,CAAC;AAI/D,OAAO,EAEL,KAAK,gBAAgB,EACrB,KAAK,oBAAoB,EAC1B,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAGL,KAAK,gBAAgB,EACtB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EAML,KAAK,qCAAqC,EAC3C,MAAM,2BAA2B,CAAC;AAGnC,YAAY,EACV,2BAA2B,EAC3B,wCAAwC,EACxC,kCAAkC,EAClC,gCAAgC,EAChC,qCAAqC,GACtC,MAAM,2BAA2B,CAAC;AAEnC,MAAM,MAAM,4BAA4B,GACpC,cAAc,GACd,eAAe,GACf,kBAAkB,CAAC;AAEvB,MAAM,MAAM,0BAA0B,GAAG;IACvC,MAAM,EAAE,UAAU,GAAG,gBAAgB,GAAG,kBAAkB,CAAC;IAC3D,QAAQ,EAAE,KAAK,CAAC;IAChB,aAAa,EAAE,oBAAoB,CAAC;CACrC,CAAC;AAkBF,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAE/D;AA2ND,wBAAsB,oBAAoB,CAAC,MAAM,EAAE;IACjD,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,sBAAsB,CAAC,EAAE,gBAAgB,CAAC;IAC1C,QAAQ,EAAE,4BAA4B,CAAC;IACvC,aAAa,CAAC,EAAE,qCAAqC,CAAC;IACtD,aAAa,CAAC,EAAE,gBAAgB,CAAC;CAClC,GAAG,OAAO,CAAC,0BAA0B,CAAC,CA4MtC"}
@@ -40,8 +40,8 @@ async function openNativeParent(filePath) {
40
40
  const parentPath = path.dirname(filePath);
41
41
  const handle = await fs.open(parentPath, directoryOpenFlags());
42
42
  try {
43
- const pathname = await fs.lstat(parentPath, { bigint: true });
44
- const opened = await handle.stat({ bigint: true });
43
+ const pathname = fsSync.lstatSync(parentPath, { bigint: true });
44
+ const opened = fsSync.fstatSync(handle.fd, { bigint: true });
45
45
  if (pathname.isSymbolicLink() || !sameFileIdentity(pathname, opened)) {
46
46
  throw new FsSafeError("path-mismatch", "publication parent changed while opening");
47
47
  }
@@ -55,8 +55,8 @@ async function openNativeParent(filePath) {
55
55
  async function assertPinnedSourceCurrent(params) {
56
56
  // Windows file indexes can exceed Number.MAX_SAFE_INTEGER, so publication
57
57
  // fences must compare bigint stats instead of rounded numeric identities.
58
- const opened = await params.handle.stat({ bigint: true });
59
- const current = await fs.lstat(params.sourcePath, { bigint: true });
58
+ const opened = fsSync.fstatSync(params.handle.fd, { bigint: true });
59
+ const current = fsSync.lstatSync(params.sourcePath, { bigint: true });
60
60
  if (!opened.isFile() ||
61
61
  current.isSymbolicLink() ||
62
62
  !current.isFile() ||
@@ -90,15 +90,15 @@ async function copyPinnedSource(params) {
90
90
  throw error;
91
91
  }
92
92
  let target;
93
- let createdIdentity = fsSync.fstatSync(nativeFd, { bigint: true });
94
- rememberCreatedTarget(params.failure, createdIdentity, "copy-verify");
95
93
  try {
94
+ let createdIdentity = fsSync.fstatSync(nativeFd, { bigint: true });
95
+ rememberCreatedTarget(params.failure, createdIdentity, "copy-verify");
96
96
  fsSync.fchmodSync(nativeFd, 0o600);
97
97
  createdIdentity = fsSync.fstatSync(nativeFd, { bigint: true });
98
98
  await getFsSafeTestHooks()?.afterPublishTargetCreated?.("exclusive-copy", params.targetPath, createdIdentity);
99
- const identity = await fs.lstat(params.targetPath, { bigint: true });
99
+ const identity = fsSync.lstatSync(params.targetPath, { bigint: true });
100
100
  target = await fs.open(params.targetPath, sourceOpenFlags());
101
- const opened = await target.stat({ bigint: true });
101
+ const opened = fsSync.fstatSync(target.fd, { bigint: true });
102
102
  if (identity.isSymbolicLink() ||
103
103
  !identity.isFile() ||
104
104
  !sameFileIdentity(createdIdentity, identity) ||
@@ -106,8 +106,9 @@ async function copyPinnedSource(params) {
106
106
  throw new FsSafeError("path-mismatch", "native publication target changed after copy");
107
107
  }
108
108
  const hashed = await hashFileHandle(target, params.native);
109
- fsSync.closeSync(nativeFd);
109
+ const completedFd = nativeFd;
110
110
  nativeFd = undefined;
111
+ fsSync.closeSync(completedFd);
111
112
  return {
112
113
  handle: target,
113
114
  exactIdentity: opened,
@@ -126,11 +127,11 @@ async function copyPinnedSource(params) {
126
127
  }
127
128
  }
128
129
  const target = await fs.open(params.targetPath, "wx+", 0o600);
129
- const createdIdentity = await target.stat({ bigint: true });
130
- rememberCreatedTarget(params.failure, createdIdentity, "copy-verify");
131
130
  try {
131
+ const createdIdentity = fsSync.fstatSync(target.fd, { bigint: true });
132
+ rememberCreatedTarget(params.failure, createdIdentity, "copy-verify");
132
133
  await target.chmod(0o600);
133
- const exactIdentity = await target.stat({ bigint: true });
134
+ const exactIdentity = fsSync.fstatSync(target.fd, { bigint: true });
134
135
  await getFsSafeTestHooks()?.afterPublishTargetCreated?.("exclusive-copy", params.targetPath, exactIdentity);
135
136
  const hash = createHash("sha256");
136
137
  const buffer = Buffer.allocUnsafe(64 * 1024);
@@ -164,7 +165,7 @@ async function removeCreatedTargetIfUnchanged(targetPath, identity) {
164
165
  return "unknown";
165
166
  }
166
167
  try {
167
- const current = await fs.lstat(targetPath, { bigint: true });
168
+ const current = fsSync.lstatSync(targetPath, { bigint: true });
168
169
  if (!current.isSymbolicLink() && sameFileIdentityForCleanup(current, identity)) {
169
170
  await fs.rm(targetPath);
170
171
  return "removed";
@@ -195,7 +196,7 @@ export async function publishFileExclusive(params) {
195
196
  if (params.parentReceipt && path.resolve(params.parentReceipt.path) !== parentPath) {
196
197
  throw new FsSafeError("path-mismatch", "publication parent receipt does not match target parent");
197
198
  }
198
- const sourcePathStat = await fs.lstat(sourcePath);
199
+ const sourcePathStat = fsSync.lstatSync(sourcePath);
199
200
  if (sourcePathStat.isSymbolicLink() || !sourcePathStat.isFile()) {
200
201
  throw new FsSafeError("not-file", "publication source must be a regular file");
201
202
  }
@@ -212,9 +213,9 @@ export async function publishFileExclusive(params) {
212
213
  parent = await pinDirectory(params.parentReceipt ?? parentPath, {
213
214
  label: "publication parent",
214
215
  });
215
- const sourceIdentity = await source.stat();
216
- const sourceExactIdentity = await source.stat({ bigint: true });
217
- const sourcePathExactIdentity = await fs.lstat(sourcePath, { bigint: true });
216
+ const sourceIdentity = fsSync.fstatSync(source.fd);
217
+ const sourceExactIdentity = fsSync.fstatSync(source.fd, { bigint: true });
218
+ const sourcePathExactIdentity = fsSync.lstatSync(sourcePath, { bigint: true });
218
219
  if (sourcePathExactIdentity.isSymbolicLink() ||
219
220
  !sourcePathExactIdentity.isFile() ||
220
221
  !sameFileIdentity(sourcePathExactIdentity, sourceExactIdentity) ||
@@ -241,14 +242,14 @@ export async function publishFileExclusive(params) {
241
242
  // A failed post-rename fence must not delete the only remaining name.
242
243
  failure.preserveTarget = true;
243
244
  await getFsSafeTestHooks()?.afterPublishTargetCreated?.("rename-noreplace", targetPath, sourceExactIdentity);
244
- const targetExactIdentity = await fs.lstat(targetPath, { bigint: true });
245
+ const targetExactIdentity = fsSync.lstatSync(targetPath, { bigint: true });
245
246
  if (targetExactIdentity.isSymbolicLink() ||
246
247
  !targetExactIdentity.isFile() ||
247
248
  !sameFileIdentity(targetExactIdentity, sourceExactIdentity)) {
248
249
  throw new FsSafeError("path-mismatch", "no-replace publication target changed");
249
250
  }
250
251
  try {
251
- await fs.lstat(sourcePath);
252
+ fsSync.lstatSync(sourcePath);
252
253
  throw new FsSafeError("path-mismatch", "no-replace publication source still exists");
253
254
  }
254
255
  catch (error) {
@@ -257,7 +258,7 @@ export async function publishFileExclusive(params) {
257
258
  }
258
259
  }
259
260
  syncFileBestEffortSync(sourceNativeParent.handle.fd);
260
- const targetIdentity = await fs.lstat(targetPath);
261
+ const targetIdentity = fsSync.lstatSync(targetPath);
261
262
  return {
262
263
  method: "rename-noreplace",
263
264
  identity: targetIdentity,
@@ -278,14 +279,14 @@ export async function publishFileExclusive(params) {
278
279
  }
279
280
  rememberCreatedTarget(failure, sourceExactIdentity, "hardlink-verify");
280
281
  await getFsSafeTestHooks()?.afterPublishTargetCreated?.("hardlink", targetPath, sourceExactIdentity);
281
- const targetExactIdentity = await fs.lstat(targetPath, { bigint: true });
282
+ const targetExactIdentity = fsSync.lstatSync(targetPath, { bigint: true });
282
283
  if (targetExactIdentity.isSymbolicLink() ||
283
284
  !targetExactIdentity.isFile() ||
284
285
  !sameFileIdentity(targetExactIdentity, sourceExactIdentity)) {
285
286
  throw new FsSafeError("path-mismatch", "hardlink publication target changed");
286
287
  }
287
288
  await assertPinnedSourceCurrent({ sourcePath, handle: source, identity: sourceExactIdentity });
288
- const targetIdentity = await fs.lstat(targetPath);
289
+ const targetIdentity = fsSync.lstatSync(targetPath);
289
290
  return {
290
291
  method: "hardlink",
291
292
  identity: targetIdentity,
@@ -316,9 +317,9 @@ export async function publishFileExclusive(params) {
316
317
  failure,
317
318
  });
318
319
  target = copied.handle;
319
- targetIdentity = await target.stat();
320
- const targetPathStat = await fs.lstat(targetPath);
321
- const targetPathExactStat = await fs.lstat(targetPath, { bigint: true });
320
+ targetIdentity = fsSync.fstatSync(target.fd);
321
+ const targetPathStat = fsSync.lstatSync(targetPath);
322
+ const targetPathExactStat = fsSync.lstatSync(targetPath, { bigint: true });
322
323
  const copiedBack = await hashFileHandle(target, native);
323
324
  const sourceAfter = await hashFileHandle(source, native);
324
325
  if (targetPathStat.isSymbolicLink() ||
@@ -1 +1 @@
1
- {"version":3,"file":"replace-file-copy-fallback.d.ts","sourceRoot":"","sources":["../src/replace-file-copy-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAc,MAAM,SAAS,CAAC;AAO/D,MAAM,MAAM,oCAAoC,GAAG,QAAQ,CAAC;AAC5D,MAAM,MAAM,oCAAoC,GAAG,kBAAkB,GAAG,MAAM,CAAC;AAC/E,MAAM,MAAM,+BAA+B,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAC5E,MAAM,MAAM,sCAAsC,GAAG;IACnD,OAAO,EAAE,+BAA+B,CAAC;CAC1C,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,KAAK,EAAE,cAAc,kBAAkB,EAAE,KAAK,CAAC;IAC/C,IAAI,EAAE,cAAc,kBAAkB,EAAE,IAAI,CAAC;IAC7C,EAAE,EAAE,cAAc,kBAAkB,EAAE,EAAE,CAAC;IACzC,MAAM,EAAE,cAAc,kBAAkB,EAAE,MAAM,CAAC;CAClD,CAAC;AAEF,KAAK,cAAc,GAAG,IAAI,CACxB,OAAO,MAAM,EACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,eAAe,GACf,WAAW,GACX,UAAU,GACV,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,WAAW,CACd,CAAC;AAuFF,wBAAsB,+BAA+B,CACnD,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,oCAAoC,GAC5C,OAAO,CAAC,IAAI,CAAC,CAqBf;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,oCAAoC,GAC5C,IAAI,CAwBN;AAmID,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,QAAQ,EAAE,eAAe,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,oCAAoC,CAAC;IAC5D,OAAO,EAAE,oCAAoC,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,WAAW,CAAC;IACrC,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,OAAO,CAAC,IAAI,CAAC,CAwDhB;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,QAAQ,EAAE,cAAc,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,oCAAoC,CAAC;IAC5D,OAAO,EAAE,oCAAoC,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,WAAW,CAAC;IACrC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,IAAI,CAkEP"}
1
+ {"version":3,"file":"replace-file-copy-fallback.d.ts","sourceRoot":"","sources":["../src/replace-file-copy-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAc,MAAM,SAAS,CAAC;AAO/D,MAAM,MAAM,oCAAoC,GAAG,QAAQ,CAAC;AAC5D,MAAM,MAAM,oCAAoC,GAAG,kBAAkB,GAAG,MAAM,CAAC;AAC/E,MAAM,MAAM,+BAA+B,GAAG,UAAU,GAAG,gBAAgB,CAAC;AAC5E,MAAM,MAAM,sCAAsC,GAAG;IACnD,OAAO,EAAE,+BAA+B,CAAC;CAC1C,CAAC;AAEF,KAAK,eAAe,GAAG;IACrB,KAAK,EAAE,cAAc,kBAAkB,EAAE,KAAK,CAAC;IAC/C,IAAI,EAAE,cAAc,kBAAkB,EAAE,IAAI,CAAC;IAC7C,EAAE,EAAE,cAAc,kBAAkB,EAAE,EAAE,CAAC;IACzC,MAAM,EAAE,cAAc,kBAAkB,EAAE,MAAM,CAAC;CAClD,CAAC;AAEF,KAAK,cAAc,GAAG,IAAI,CACxB,OAAO,MAAM,EACX,WAAW,GACX,WAAW,GACX,WAAW,GACX,eAAe,GACf,WAAW,GACX,UAAU,GACV,UAAU,GACV,QAAQ,GACR,YAAY,GACZ,WAAW,CACd,CAAC;AA0FF,wBAAsB,+BAA+B,CACnD,QAAQ,EAAE,eAAe,EACzB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,oCAAoC,GAC5C,OAAO,CAAC,IAAI,CAAC,CAwBf;AAED,wBAAgB,mCAAmC,CACjD,QAAQ,EAAE,cAAc,EACxB,IAAI,EAAE,MAAM,EACZ,MAAM,CAAC,EAAE,oCAAoC,GAC5C,IAAI,CAwBN;AAoID,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,QAAQ,EAAE,eAAe,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,oCAAoC,CAAC;IAC5D,OAAO,EAAE,oCAAoC,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,WAAW,CAAC;IACrC,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,OAAO,CAAC,IAAI,CAAC,CA4DhB;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,QAAQ,EAAE,cAAc,CAAC;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,MAAM,CAAC;IACb,oBAAoB,CAAC,EAAE,oCAAoC,CAAC;IAC5D,OAAO,EAAE,oCAAoC,CAAC;IAC9C,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,sBAAsB,CAAC,EAAE,WAAW,CAAC;IACrC,UAAU,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAChD,IAAI,EAAE,OAAO,CAAC;CACf,GAAG,IAAI,CAkEP"}
@@ -1,4 +1,5 @@
1
1
  import syncFs, {} from "node:fs";
2
+ import fs, {} from "node:fs/promises";
2
3
  import { FsSafeError } from "./errors.js";
3
4
  import { sameFileIdentity } from "./file-identity.js";
4
5
  import { readOwnedCopySource, readOwnedCopySourceSync } from "./replace-file-copy-source.js";
@@ -27,11 +28,15 @@ function assertPinnedDestination(pathname, opened, dest, hardlinks) {
27
28
  }
28
29
  }
29
30
  async function openPinnedDestination(fsModule, dest, hardlinks) {
30
- const preview = await fsModule.lstat(dest).catch((error) => {
31
+ let preview;
32
+ try {
33
+ preview = fsModule === fs ? syncFs.lstatSync(dest) : await fsModule.lstat(dest);
34
+ }
35
+ catch (error) {
31
36
  if (notFound(error))
32
37
  return null;
33
38
  throw error;
34
- });
39
+ }
35
40
  if (!preview)
36
41
  return null;
37
42
  if (preview.isSymbolicLink()) {
@@ -39,8 +44,8 @@ async function openPinnedDestination(fsModule, dest, hardlinks) {
39
44
  }
40
45
  const handle = await fsModule.open(dest, OPEN_READ_WRITE_FLAGS);
41
46
  try {
42
- const opened = await handle.stat();
43
- const current = await fsModule.lstat(dest);
47
+ const opened = fsModule === fs ? syncFs.fstatSync(handle.fd) : await handle.stat();
48
+ const current = fsModule === fs ? syncFs.lstatSync(dest) : await fsModule.lstat(dest);
44
49
  assertPinnedDestination(current, opened, dest, hardlinks);
45
50
  return { handle, stat: opened };
46
51
  }
@@ -76,17 +81,21 @@ function openPinnedDestinationSync(fsModule, dest, hardlinks) {
76
81
  export async function assertDestinationHardlinkPolicy(fsModule, dest, policy) {
77
82
  if (policy !== "reject")
78
83
  return;
79
- const preview = await fsModule.lstat(dest).catch((error) => {
84
+ let preview;
85
+ try {
86
+ preview = fsModule === fs ? syncFs.lstatSync(dest) : await fsModule.lstat(dest);
87
+ }
88
+ catch (error) {
80
89
  if (notFound(error))
81
- return null;
90
+ return;
82
91
  throw error;
83
- });
92
+ }
84
93
  if (!preview || preview.isSymbolicLink() || !preview.isFile())
85
94
  return;
86
95
  const handle = await fsModule.open(dest, OPEN_READ_FLAGS);
87
96
  try {
88
- const opened = await handle.stat();
89
- const current = await fsModule.lstat(dest);
97
+ const opened = fsModule === fs ? syncFs.fstatSync(handle.fd) : await handle.stat();
98
+ const current = fsModule === fs ? syncFs.lstatSync(dest) : await fsModule.lstat(dest);
90
99
  if (current.isSymbolicLink() || !current.isFile() || !sameFileIdentity(current, opened)) {
91
100
  throw new FsSafeError("path-mismatch", `Atomic replace destination changed while opening: ${dest}`);
92
101
  }
@@ -191,8 +200,8 @@ function restoreFailure(writeError, cleanup, restoreError) {
191
200
  ? `Atomic copy fallback failed; original destination restored: ${primary.message}`
192
201
  : `Atomic copy fallback failed and original restoration failed: ${primary.message}`, { cause, details });
193
202
  }
194
- async function replacePinnedWithRestore(handle, replacement, maxRestoreBytes, replacementMode) {
195
- const originalMode = (await handle.stat()).mode;
203
+ async function replacePinnedWithRestore(fsModule, handle, replacement, maxRestoreBytes, replacementMode) {
204
+ const originalMode = (fsModule === fs ? syncFs.fstatSync(handle.fd) : await handle.stat()).mode;
196
205
  const original = await readBounded(handle, maxRestoreBytes);
197
206
  try {
198
207
  await writeAll(handle, replacement);
@@ -250,15 +259,19 @@ export async function copyFallbackReplace(params) {
250
259
  const pinned = await openPinnedDestination(params.fsModule, params.dest, params.destinationHardlinks);
251
260
  if (pinned) {
252
261
  destHandle = pinned.handle;
253
- await replacePinnedWithRestore(destHandle, replacement, params.maxRestoreBytes, source.mode);
262
+ await replacePinnedWithRestore(params.fsModule, destHandle, replacement, params.maxRestoreBytes, source.mode);
254
263
  }
255
264
  }
256
265
  if (!destHandle) {
257
- const destStat = await params.fsModule.lstat(params.dest).catch((error) => {
258
- if (notFound(error))
259
- return null;
260
- throw error;
261
- });
266
+ let destStat = null;
267
+ try {
268
+ destStat = params.fsModule === fs
269
+ ? syncFs.lstatSync(params.dest) : await params.fsModule.lstat(params.dest);
270
+ }
271
+ catch (error) {
272
+ if (!notFound(error))
273
+ throw error;
274
+ }
262
275
  if (destStat?.isSymbolicLink()) {
263
276
  throw new FsSafeError("symlink", `Refusing copy fallback through symlink destination: ${params.dest}`);
264
277
  }
@@ -1 +1 @@
1
- {"version":3,"file":"replace-file-copy-source.d.ts","sourceRoot":"","sources":["../src/replace-file-copy-source.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAOnD,KAAK,qBAAqB,GAAG,IAAI,CAAC,cAAc,kBAAkB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;AACvF,KAAK,oBAAoB,GAAG,IAAI,CAC9B,OAAO,MAAM,EACb,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,CAClE,CAAC;AA0DF,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,QAAQ,EAAE,qBAAqB,CAAC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,WAAW,CAAC;CAChC,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CA0BjD;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,WAAW,CAAC;CAChC,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA8BxC"}
1
+ {"version":3,"file":"replace-file-copy-source.d.ts","sourceRoot":"","sources":["../src/replace-file-copy-source.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAOnD,KAAK,qBAAqB,GAAG,IAAI,CAAC,cAAc,kBAAkB,CAAC,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC;AACvF,KAAK,oBAAoB,GAAG,IAAI,CAC9B,OAAO,MAAM,EACb,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,UAAU,CAClE,CAAC;AA0DF,wBAAsB,mBAAmB,CAAC,MAAM,EAAE;IAChD,QAAQ,EAAE,qBAAqB,CAAC;IAChC,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,WAAW,CAAC;CAChC,GAAG,OAAO,CAAC;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CA8BjD;AAED,wBAAgB,uBAAuB,CAAC,MAAM,EAAE;IAC9C,QAAQ,EAAE,oBAAoB,CAAC;IAC/B,GAAG,EAAE,MAAM,CAAC;IACZ,gBAAgB,CAAC,EAAE,WAAW,CAAC;CAChC,GAAG;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CA8BxC"}
@@ -1,4 +1,5 @@
1
1
  import syncFs, {} from "node:fs";
2
+ import fs, {} from "node:fs/promises";
2
3
  import { resolveReadOpenFlags } from "./read-open-flags.js";
3
4
  import { FsSafeError } from "./errors.js";
4
5
  import { sameFileIdentity } from "./file-identity.js";
@@ -57,15 +58,19 @@ function readAllSync(fsModule, fd) {
57
58
  return Buffer.concat(chunks, position);
58
59
  }
59
60
  export async function readOwnedCopySource(params) {
60
- assertSourcePreview(await params.fsModule.lstat(params.src), params.src);
61
+ assertSourcePreview(params.fsModule === fs
62
+ ? syncFs.lstatSync(params.src) : await params.fsModule.lstat(params.src), params.src);
61
63
  const handle = await openSource(params.fsModule, params.src);
62
64
  try {
63
65
  if (params.expectedIdentity) {
64
- const exact = await inspectFileIdentity(() => handle.stat({ bigint: true }), params.expectedIdentity);
65
- await inspectFileIdentity(() => params.fsModule.lstat(params.src, { bigint: true }), exact);
66
+ const exact = await inspectFileIdentity(() => params.fsModule === fs
67
+ ? syncFs.fstatSync(handle.fd, { bigint: true }) : handle.stat({ bigint: true }), params.expectedIdentity);
68
+ await inspectFileIdentity(() => params.fsModule === fs
69
+ ? syncFs.lstatSync(params.src, { bigint: true }) : params.fsModule.lstat(params.src, { bigint: true }), exact);
66
70
  }
67
- const opened = await handle.stat();
68
- const current = await params.fsModule.lstat(params.src);
71
+ const opened = params.fsModule === fs ? syncFs.fstatSync(handle.fd) : await handle.stat();
72
+ const current = params.fsModule === fs
73
+ ? syncFs.lstatSync(params.src) : await params.fsModule.lstat(params.src);
69
74
  if (!opened.isFile() || current.isSymbolicLink() || !sameFileIdentity(opened, current)) {
70
75
  throw new FsSafeError("path-mismatch", `Copy fallback source changed while opening: ${params.src}`);
71
76
  }
@@ -1 +1 @@
1
- {"version":3,"file":"replace-file-temp-owner.d.ts","sourceRoot":"","sources":["../src/replace-file-temp-owner.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,EAAE,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAA6B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAO9F,KAAK,oBAAoB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC;AACzE,KAAK,mBAAmB,GAAG,IAAI,CAC7B,OAAO,MAAM,EACb,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,cAAc,GAAG,YAAY,CACrF,CAAC;AAgEF,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,WAAW,EAAE,sBAAsB,CAAC;CACrC,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhB;AA+BD,qBAAa,oBAAoB;;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAM1B,YAAY,QAAQ,EAAE,MAAM,EAG3B;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,QAAQ,CAAC,UAAU,aAAc,WAAW,KAAG,IAAI,CAGjD;IAEF,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI,CAG/D;IAED,IAAI,QAAQ,IAAI,WAAW,CAG1B;IAEK,aAAa,CAAC,QAAQ,EAAE,oBAAoB,EAAE,QAAQ,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3F;IAEK,eAAe,CACnB,QAAQ,EAAE,oBAAoB,EAC9B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CA4Cf;IAED,WAAW,IAAI,IAAI,CAGlB;IAEK,MAAM,CAAC,MAAM,EAAE;QACnB,QAAQ,EAAE,oBAAoB,CAAC;QAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,mBAAmB,EAAE,OAAO,CAAC;KAC9B,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BhB;CACF;AAED,qBAAa,mBAAmB;;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAM1B,YAAY,QAAQ,EAAE,MAAM,EAG3B;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,QAAQ,CAAC,UAAU,aAAc,WAAW,KAAG,IAAI,CAGjD;IAEF,KAAK,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI,CAGvD;IAED,IAAI,QAAQ,IAAI,WAAW,CAG1B;IAED,aAAa,CAAC,QAAQ,EAAE,mBAAmB,EAAE,QAAQ,SAAgB,GAAG,IAAI,CAkB3E;IAED,eAAe,CACb,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,IAAI,CAgDN;IAED,WAAW,IAAI,IAAI,CAGlB;IAED,MAAM,CAAC,MAAM,EAAE;QACb,QAAQ,EAAE,mBAAmB,CAAC;QAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,mBAAmB,EAAE,OAAO,CAAC;KAC9B,GAAG,IAAI,CAgCP;CACF"}
1
+ {"version":3,"file":"replace-file-temp-owner.d.ts","sourceRoot":"","sources":["../src/replace-file-temp-owner.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,EAAE,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,EAAE,EAAE,KAAK,UAAU,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAA6B,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;AAO9F,KAAK,oBAAoB,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,CAAC,CAAC;AACzE,KAAK,mBAAmB,GAAG,IAAI,CAC7B,OAAO,MAAM,EACb,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,UAAU,GAAG,cAAc,GAAG,YAAY,CACrF,CAAC;AAgEF,wBAAsB,qBAAqB,CAAC,MAAM,EAAE;IAClD,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,CAAC;IACnB,QAAQ,CAAC,EAAE,WAAW,CAAC;IACvB,WAAW,EAAE,sBAAsB,CAAC;CACrC,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBhB;AA+BD,qBAAa,oBAAoB;;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAM1B,YAAY,QAAQ,EAAE,MAAM,EAG3B;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,QAAQ,CAAC,UAAU,aAAc,WAAW,KAAG,IAAI,CAGjD;IAEF,KAAK,CAAC,IAAI,EAAE;QAAE,MAAM,EAAE,UAAU,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI,CAG/D;IAED,IAAI,QAAQ,IAAI,WAAW,CAG1B;IAEK,aAAa,CAAC,QAAQ,EAAE,oBAAoB,EAAE,QAAQ,SAAgB,GAAG,OAAO,CAAC,IAAI,CAAC,CAoB3F;IAEK,eAAe,CACnB,QAAQ,EAAE,oBAAoB,EAC9B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,OAAO,CAAC,IAAI,CAAC,CA4Cf;IAED,WAAW,IAAI,IAAI,CAGlB;IAEK,MAAM,CAAC,MAAM,EAAE;QACnB,QAAQ,EAAE,oBAAoB,CAAC;QAC/B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,mBAAmB,EAAE,OAAO,CAAC;KAC9B,GAAG,OAAO,CAAC,IAAI,CAAC,CA8BhB;CACF;AAED,qBAAa,mBAAmB;;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAM1B,YAAY,QAAQ,EAAE,MAAM,EAG3B;IAED,KAAK,IAAI,IAAI,CAEZ;IAED,QAAQ,CAAC,UAAU,aAAc,WAAW,KAAG,IAAI,CAGjD;IAEF,KAAK,CAAC,IAAI,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,WAAW,CAAA;KAAE,GAAG,IAAI,CAGvD;IAED,IAAI,QAAQ,IAAI,WAAW,CAG1B;IAED,aAAa,CAAC,QAAQ,EAAE,mBAAmB,EAAE,QAAQ,SAAgB,GAAG,IAAI,CAkB3E;IAED,eAAe,CACb,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,EAAE,MAAM,EAChB,YAAY,CAAC,EAAE,MAAM,GACpB,IAAI,CAkDN;IAED,WAAW,IAAI,IAAI,CAGlB;IAED,MAAM,CAAC,MAAM,EAAE;QACb,QAAQ,EAAE,mBAAmB,CAAC;QAC9B,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,mBAAmB,EAAE,OAAO,CAAC;KAC9B,GAAG,IAAI,CAkCP;CACF"}