@openclaw/feishu 2026.9.8 → 2026.10.1-beta.2

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 (225) hide show
  1. package/dist/.setup/{channel-DV4hM2Uf.mjs → channel-CX62T71S.mjs} +146 -245
  2. package/dist/.setup/{channel.runtime-CXQcH5zZ.mjs → channel.runtime-COiaq-Rv.mjs} +6 -12
  3. package/dist/.setup/{chat-D8MlFAZi.mjs → chat-BdyvImsY.mjs} +6 -9
  4. package/dist/.setup/{client-BYG-_IZl.mjs → client-BsBVWJzN.mjs} +1 -1
  5. package/dist/.setup/{doctor-contract-3XC0vbIw.mjs → doctor-contract-B1B9ogdd.mjs} +212 -27
  6. package/dist/.setup/{monitor-D_rsHBZ6.mjs → monitor-7DvwUs4Y.mjs} +5 -6
  7. package/dist/.setup/{monitor.account-DvvY7i6k.mjs → monitor.account-7xVu7XWY.mjs} +190 -393
  8. package/dist/.setup/{probe-_ViqSi0j.mjs → probe-B9o7iey6.mjs} +13 -39
  9. package/dist/.setup/{reply-delivery-result-COAjaukc.mjs → reply-delivery-result-Bt-BWBEA.mjs} +151 -297
  10. package/dist/.setup/{setup-api-C4S5I3ac.mjs → setup-api-DmzX9S01.mjs} +1 -1
  11. package/dist/.setup/{subagent-hooks-BaFoMu8W.mjs → subagent-hooks-CUXg0pC-.mjs} +5 -11
  12. package/dist/.setup/{thread-bindings-EdLXPXzw.mjs → thread-bindings-jW2x5VuN.mjs} +23 -41
  13. package/dist/api.js +234 -297
  14. package/dist/channel-plugin-api.js +1 -1
  15. package/dist/doctor-contract-api.js +2 -2
  16. package/dist/session-binding-contract-api.js +1 -1
  17. package/dist/setup-api.js +1 -1
  18. package/dist/setup-entry.js +1 -1
  19. package/dist/subagent-hooks-api.js +1 -1
  20. package/node_modules/@openclaw/fs-safe/CHANGELOG.md +95 -0
  21. package/node_modules/@openclaw/fs-safe/README.md +70 -379
  22. package/node_modules/@openclaw/fs-safe/dist/absolute-path.js +35 -51
  23. package/node_modules/@openclaw/fs-safe/dist/advanced.d.ts +2 -0
  24. package/node_modules/@openclaw/fs-safe/dist/advanced.js +1 -0
  25. package/node_modules/@openclaw/fs-safe/dist/archive-input.js +2 -0
  26. package/node_modules/@openclaw/fs-safe/dist/archive-parser.wasm +0 -0
  27. package/node_modules/@openclaw/fs-safe/dist/archive-staging.d.ts +1 -1
  28. package/node_modules/@openclaw/fs-safe/dist/archive-staging.js +9 -10
  29. package/node_modules/@openclaw/fs-safe/dist/archive.js +2 -5
  30. package/node_modules/@openclaw/fs-safe/dist/atomic-io.d.ts +51 -0
  31. package/node_modules/@openclaw/fs-safe/dist/atomic-io.js +242 -0
  32. package/node_modules/@openclaw/fs-safe/dist/config.d.ts +1 -1
  33. package/node_modules/@openclaw/fs-safe/dist/config.js +1 -1
  34. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.d.ts +1 -1
  35. package/node_modules/@openclaw/fs-safe/dist/copy-file-input.js +2 -2
  36. package/node_modules/@openclaw/fs-safe/dist/copy-tree-portable.js +2 -0
  37. package/node_modules/@openclaw/fs-safe/dist/create.js +12 -5
  38. package/node_modules/@openclaw/fs-safe/dist/directory-guard.d.ts +11 -0
  39. package/node_modules/@openclaw/fs-safe/dist/directory-guard.js +4 -7
  40. package/node_modules/@openclaw/fs-safe/dist/entry-publication-types.d.ts +55 -0
  41. package/node_modules/@openclaw/fs-safe/dist/entry-publication-types.js +1 -0
  42. package/node_modules/@openclaw/fs-safe/dist/entry-publication.d.ts +8 -0
  43. package/node_modules/@openclaw/fs-safe/dist/entry-publication.js +262 -0
  44. package/node_modules/@openclaw/fs-safe/dist/exclusive-create.d.ts +3 -0
  45. package/node_modules/@openclaw/fs-safe/dist/exclusive-create.js +22 -0
  46. package/node_modules/@openclaw/fs-safe/dist/file-cleanup.d.ts +1 -6
  47. package/node_modules/@openclaw/fs-safe/dist/file-cleanup.js +3 -6
  48. package/node_modules/@openclaw/fs-safe/dist/file-identity.js +3 -6
  49. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-io.js +2 -2
  50. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root-mutation.js +9 -0
  51. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-root.js +4 -6
  52. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync-stale-admission.js +1 -1
  53. package/node_modules/@openclaw/fs-safe/dist/file-lock-sync.js +8 -0
  54. package/node_modules/@openclaw/fs-safe/dist/file-store-boundary.js +2 -0
  55. package/node_modules/@openclaw/fs-safe/dist/file-store-sync-write.js +20 -18
  56. package/node_modules/@openclaw/fs-safe/dist/file-store.js +4 -10
  57. package/node_modules/@openclaw/fs-safe/dist/fs.d.ts +2 -10
  58. package/node_modules/@openclaw/fs-safe/dist/fs.js +2 -10
  59. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.d.ts +3 -4
  60. package/node_modules/@openclaw/fs-safe/dist/guarded-mkdir.js +6 -18
  61. package/node_modules/@openclaw/fs-safe/dist/guest-dispatch-python.js +1 -1
  62. package/node_modules/@openclaw/fs-safe/dist/guest-native-python.js +0 -2
  63. package/node_modules/@openclaw/fs-safe/dist/guest.js +3 -9
  64. package/node_modules/@openclaw/fs-safe/dist/index.d.ts +1 -1
  65. package/node_modules/@openclaw/fs-safe/dist/index.js +1 -1
  66. package/node_modules/@openclaw/fs-safe/dist/json-durable-queue-directory.js +1 -5
  67. package/node_modules/@openclaw/fs-safe/dist/json.js +19 -37
  68. package/node_modules/@openclaw/fs-safe/dist/move-path.js +2 -0
  69. package/node_modules/@openclaw/fs-safe/dist/mutation-authority.d.ts +1 -0
  70. package/node_modules/@openclaw/fs-safe/dist/mutation-authority.js +9 -1
  71. package/node_modules/@openclaw/fs-safe/dist/native-binding.d.ts +47 -2
  72. package/node_modules/@openclaw/fs-safe/dist/native-config.d.ts +1 -9
  73. package/node_modules/@openclaw/fs-safe/dist/native-config.js +6 -40
  74. package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.d.ts +2 -0
  75. package/node_modules/@openclaw/fs-safe/dist/native-parent-admission.js +5 -2
  76. package/node_modules/@openclaw/fs-safe/dist/native-pinned-write.js +11 -316
  77. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent-windows.d.ts +7 -7
  78. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent-windows.js +28 -175
  79. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.d.ts +15 -0
  80. package/node_modules/@openclaw/fs-safe/dist/native-policy-parent.js +418 -0
  81. package/node_modules/@openclaw/fs-safe/dist/native-rename-outcome.js +9 -4
  82. package/node_modules/@openclaw/fs-safe/dist/native-staged-file.js +17 -13
  83. package/node_modules/@openclaw/fs-safe/dist/native-staged-symlink.js +6 -24
  84. package/node_modules/@openclaw/fs-safe/dist/native.js +5 -1
  85. package/node_modules/@openclaw/fs-safe/dist/path-case.js +10 -9
  86. package/node_modules/@openclaw/fs-safe/dist/path-scope-lexical.js +4 -1
  87. package/node_modules/@openclaw/fs-safe/dist/path-segment-route.d.ts +1 -0
  88. package/node_modules/@openclaw/fs-safe/dist/path-segment-route.js +3 -0
  89. package/node_modules/@openclaw/fs-safe/dist/path.js +4 -1
  90. package/node_modules/@openclaw/fs-safe/dist/permissions-windows.d.ts +0 -1
  91. package/node_modules/@openclaw/fs-safe/dist/pinned-mutation-admission.js +1 -3
  92. package/node_modules/@openclaw/fs-safe/dist/pinned-write-staged.js +2 -0
  93. package/node_modules/@openclaw/fs-safe/dist/pinned-write.js +9 -10
  94. package/node_modules/@openclaw/fs-safe/dist/private-producer-handoff.js +3 -6
  95. package/node_modules/@openclaw/fs-safe/dist/publish-copy-stage.js +2 -2
  96. package/node_modules/@openclaw/fs-safe/dist/publish-file.js +4 -3
  97. package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.d.ts +3 -4
  98. package/node_modules/@openclaw/fs-safe/dist/replace-file-buffer.js +18 -32
  99. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.d.ts +5 -17
  100. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-fallback.js +92 -230
  101. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.d.ts +4 -15
  102. package/node_modules/@openclaw/fs-safe/dist/replace-file-copy-source.js +22 -63
  103. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.d.ts +9 -34
  104. package/node_modules/@openclaw/fs-safe/dist/replace-file-descriptor.js +78 -83
  105. package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.d.ts +18 -20
  106. package/node_modules/@openclaw/fs-safe/dist/replace-file-destination.js +64 -98
  107. package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.d.ts +16 -33
  108. package/node_modules/@openclaw/fs-safe/dist/replace-file-temp-owner.js +61 -200
  109. package/node_modules/@openclaw/fs-safe/dist/replace-file-types.d.ts +1 -6
  110. package/node_modules/@openclaw/fs-safe/dist/replace-file.js +76 -216
  111. package/node_modules/@openclaw/fs-safe/dist/root-boundary.js +14 -24
  112. package/node_modules/@openclaw/fs-safe/dist/root-context.d.ts +1 -4
  113. package/node_modules/@openclaw/fs-safe/dist/root-context.js +4 -19
  114. package/node_modules/@openclaw/fs-safe/dist/root-create-native.d.ts +52 -0
  115. package/node_modules/@openclaw/fs-safe/dist/root-create-native.js +350 -0
  116. package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.d.ts +35 -0
  117. package/node_modules/@openclaw/fs-safe/dist/root-directory-list-types.js +1 -0
  118. package/node_modules/@openclaw/fs-safe/dist/root-directory-list.d.ts +2 -27
  119. package/node_modules/@openclaw/fs-safe/dist/root-directory-list.js +31 -28
  120. package/node_modules/@openclaw/fs-safe/dist/root-directory.js +1 -4
  121. package/node_modules/@openclaw/fs-safe/dist/root-errors.js +34 -0
  122. package/node_modules/@openclaw/fs-safe/dist/root-file.js +9 -21
  123. package/node_modules/@openclaw/fs-safe/dist/root-impl.js +105 -37
  124. package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.d.ts +7 -2
  125. package/node_modules/@openclaw/fs-safe/dist/root-move-noreplace.js +67 -38
  126. package/node_modules/@openclaw/fs-safe/dist/root-observed-path.d.ts +1 -1
  127. package/node_modules/@openclaw/fs-safe/dist/root-observed-path.js +0 -2
  128. package/node_modules/@openclaw/fs-safe/dist/root-options.d.ts +2 -3
  129. package/node_modules/@openclaw/fs-safe/dist/root-path-existing.js +3 -1
  130. package/node_modules/@openclaw/fs-safe/dist/root-path-stat.js +68 -85
  131. package/node_modules/@openclaw/fs-safe/dist/root-path.js +6 -1
  132. package/node_modules/@openclaw/fs-safe/dist/root-paths-lexical.d.ts +4 -0
  133. package/node_modules/@openclaw/fs-safe/dist/root-paths-lexical.js +1 -1
  134. package/node_modules/@openclaw/fs-safe/dist/root-paths.js +4 -9
  135. package/node_modules/@openclaw/fs-safe/dist/root-remove-native.d.ts +4 -0
  136. package/node_modules/@openclaw/fs-safe/dist/root-remove-native.js +311 -0
  137. package/node_modules/@openclaw/fs-safe/dist/root-remove.d.ts +11 -0
  138. package/node_modules/@openclaw/fs-safe/dist/root-remove.js +3 -1
  139. package/node_modules/@openclaw/fs-safe/dist/root-walk.d.ts +2 -2
  140. package/node_modules/@openclaw/fs-safe/dist/root-walk.js +1 -3
  141. package/node_modules/@openclaw/fs-safe/dist/root-write-admission.d.ts +3 -3
  142. package/node_modules/@openclaw/fs-safe/dist/root-write-admission.js +8 -10
  143. package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.d.ts +0 -1
  144. package/node_modules/@openclaw/fs-safe/dist/safe-path-segment.js +0 -4
  145. package/node_modules/@openclaw/fs-safe/dist/secret-file.js +12 -9
  146. package/node_modules/@openclaw/fs-safe/dist/secure-temp-dir.d.ts +0 -2
  147. package/node_modules/@openclaw/fs-safe/dist/sibling-staged-file.js +2 -0
  148. package/node_modules/@openclaw/fs-safe/dist/sibling-temp.js +7 -4
  149. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-acquire.js +13 -6
  150. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.d.ts +0 -3
  151. package/node_modules/@openclaw/fs-safe/dist/sidecar-lock-reclaim.js +5 -23
  152. package/node_modules/@openclaw/fs-safe/dist/staged-file-settlement.d.ts +2 -1
  153. package/node_modules/@openclaw/fs-safe/dist/staged-file-settlement.js +11 -6
  154. package/node_modules/@openclaw/fs-safe/dist/temp-cleanup.js +1 -8
  155. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.d.ts +1 -1
  156. package/node_modules/@openclaw/fs-safe/dist/temp-workspace-child-admission.js +2 -9
  157. package/node_modules/@openclaw/fs-safe/dist/text-atomic.d.ts +1 -6
  158. package/node_modules/@openclaw/fs-safe/dist/trash.js +6 -2
  159. package/node_modules/@openclaw/fs-safe/dist/walk.js +100 -46
  160. package/node_modules/@openclaw/fs-safe/dist/watch-hints.d.ts +7 -0
  161. package/node_modules/@openclaw/fs-safe/dist/watch-hints.js +235 -19
  162. package/node_modules/@openclaw/fs-safe/dist/watch-native.d.ts +21 -2
  163. package/node_modules/@openclaw/fs-safe/dist/watch-native.js +21 -3
  164. package/node_modules/@openclaw/fs-safe/dist/watch-rescan.d.ts +6 -0
  165. package/node_modules/@openclaw/fs-safe/dist/watch-rescan.js +111 -0
  166. package/node_modules/@openclaw/fs-safe/dist/watch-scan.d.ts +13 -0
  167. package/node_modules/@openclaw/fs-safe/dist/watch-scan.js +27 -4
  168. package/node_modules/@openclaw/fs-safe/dist/watch-stream.js +2 -0
  169. package/node_modules/@openclaw/fs-safe/dist/watch.js +131 -40
  170. package/node_modules/@openclaw/fs-safe/dist/windows-path-alias.d.ts +8 -0
  171. package/node_modules/@openclaw/fs-safe/dist/windows-path-alias.js +24 -6
  172. package/node_modules/@openclaw/fs-safe/dist/windows-path-syntax.d.ts +13 -0
  173. package/node_modules/@openclaw/fs-safe/dist/windows-path-syntax.js +51 -0
  174. package/node_modules/@openclaw/fs-safe/docs/advanced.md +4 -0
  175. package/node_modules/@openclaw/fs-safe/docs/atomic.md +4 -1
  176. package/node_modules/@openclaw/fs-safe/docs/config.md +6 -14
  177. package/node_modules/@openclaw/fs-safe/docs/contributing.md +4 -14
  178. package/node_modules/@openclaw/fs-safe/docs/durability.md +6 -29
  179. package/node_modules/@openclaw/fs-safe/docs/entry-publication.md +146 -0
  180. package/node_modules/@openclaw/fs-safe/docs/errors.md +16 -0
  181. package/node_modules/@openclaw/fs-safe/docs/file-store.md +28 -5
  182. package/node_modules/@openclaw/fs-safe/docs/index.md +2 -31
  183. package/node_modules/@openclaw/fs-safe/docs/install.md +10 -31
  184. package/node_modules/@openclaw/fs-safe/docs/json.md +1 -1
  185. package/node_modules/@openclaw/fs-safe/docs/local-roots.md +0 -1
  186. package/node_modules/@openclaw/fs-safe/docs/migrating-to-0.5.md +12 -12
  187. package/node_modules/@openclaw/fs-safe/docs/native-helper.md +35 -96
  188. package/node_modules/@openclaw/fs-safe/docs/native.md +22 -20
  189. package/node_modules/@openclaw/fs-safe/docs/path.md +1 -1
  190. package/node_modules/@openclaw/fs-safe/docs/permissions.md +1 -2
  191. package/node_modules/@openclaw/fs-safe/docs/public-api.md +1 -2
  192. package/node_modules/@openclaw/fs-safe/docs/reading.md +1 -2
  193. package/node_modules/@openclaw/fs-safe/docs/root.md +26 -184
  194. package/node_modules/@openclaw/fs-safe/docs/secret-file.md +1 -1
  195. package/node_modules/@openclaw/fs-safe/docs/security-model.md +115 -3
  196. package/node_modules/@openclaw/fs-safe/docs/store.md +2 -2
  197. package/node_modules/@openclaw/fs-safe/docs/temp.md +29 -65
  198. package/node_modules/@openclaw/fs-safe/docs/testing.md +90 -58
  199. package/node_modules/@openclaw/fs-safe/docs/types.md +3 -16
  200. package/node_modules/@openclaw/fs-safe/docs/walk.md +33 -5
  201. package/node_modules/@openclaw/fs-safe/docs/watch.md +96 -14
  202. package/node_modules/@openclaw/fs-safe/docs/writing.md +172 -16
  203. package/node_modules/@openclaw/fs-safe/package.json +10 -10
  204. package/node_modules/@openclaw/fs-safe-darwin-arm64/fs-safe-native.node +0 -0
  205. package/node_modules/@openclaw/fs-safe-darwin-arm64/package.json +1 -1
  206. package/node_modules/@openclaw/fs-safe-darwin-x64/fs-safe-native.node +0 -0
  207. package/node_modules/@openclaw/fs-safe-darwin-x64/package.json +1 -1
  208. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/fs-safe-native.node +0 -0
  209. package/node_modules/@openclaw/fs-safe-linux-arm64-gnu/package.json +1 -1
  210. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/fs-safe-native.node +0 -0
  211. package/node_modules/@openclaw/fs-safe-linux-arm64-musl/package.json +1 -1
  212. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/fs-safe-native.node +0 -0
  213. package/node_modules/@openclaw/fs-safe-linux-x64-gnu/package.json +1 -1
  214. package/node_modules/@openclaw/fs-safe-linux-x64-musl/fs-safe-native.node +0 -0
  215. package/node_modules/@openclaw/fs-safe-linux-x64-musl/package.json +1 -1
  216. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/fs-safe-native.node +0 -0
  217. package/node_modules/@openclaw/fs-safe-win32-x64-msvc/package.json +1 -1
  218. package/package.json +5 -5
  219. package/skills/feishu-wiki/SKILL.md +1 -1
  220. package/dist/.setup/accounts-wRqItHug.mjs +0 -206
  221. package/node_modules/@openclaw/fs-safe/dist/watch-alias.d.ts +0 -6
  222. package/node_modules/@openclaw/fs-safe/dist/watch-alias.js +0 -88
  223. package/node_modules/@openclaw/fs-safe/docs/mutation-policy-proof.md +0 -69
  224. package/node_modules/@openclaw/fs-safe/docs/private-file-store.md +0 -68
  225. package/node_modules/@openclaw/fs-safe/docs/test-hooks.md +0 -110
@@ -3,81 +3,52 @@ import syncFs, {} from "node:fs";
3
3
  import fs from "node:fs/promises";
4
4
  import path from "node:path";
5
5
  import { recursiveMkdirPath } from "./recursive-mkdir-path.js";
6
- import { assertDestinationHardlinkPolicy, assertDestinationHardlinkPolicySync, copyFallbackReplace, copyFallbackReplaceSync, } from "./replace-file-copy-fallback.js";
7
- import { applyDirectoryMode, applyDirectoryModeSync, syncDirectoryBestEffort, syncDirectoryBestEffortSync, writeTempFile, writeTempFileSync, } from "./replace-file-descriptor.js";
6
+ import { assertDestinationHardlinkPolicy, copyFallbackReplace } from "./replace-file-copy-fallback.js";
7
+ import { applyDirectoryMode, syncDirectoryBestEffort, writeTempFile } from "./replace-file-descriptor.js";
8
8
  import { inheritedRegularFileMode } from "./replace-file-mode.js";
9
9
  import { atomicExpectedContentHash, validateRenameIdentity, withAtomicRenameIdentityLock, withAtomicRenameIdentityLockSync, } from "./replace-file-rename-policy.js";
10
- import { AsyncAtomicTempOwner, SyncAtomicTempOwner } from "./replace-file-temp-owner.js";
10
+ import { AtomicTempOwner } from "./replace-file-temp-owner.js";
11
11
  import { assertSafePathPrefix } from "./safe-path-segment.js";
12
12
  import { admitStandalonePublicationPath } from "./windows-path-alias.js";
13
- import { sleep, sleepSync } from "./timing.js";
14
13
  import { serializePathWrite } from "./write-queue.js";
15
14
  import { hasErrorCode, readErrorCode } from "./file-cleanup.js";
16
15
  import { AtomicMutation } from "./replace-file-mutation.js";
17
16
  import { assertSynchronousCallbackResult } from "./mutation-authority.js";
18
- async function renameWithRetry(params) {
19
- for (let attempt = 0; attempt <= params.maxRetries; attempt++) {
17
+ import { AtomicIo, runAsync, runSync, wait } from "./atomic-io.js";
18
+ function* renameWithRetry(io, source, destination, options, mutation) {
19
+ const maxRetries = options.renameMaxRetries ?? 0;
20
+ const baseDelayMs = options.renameRetryBaseDelayMs ?? 50;
21
+ const copyFallbackOnPermissionError = options.copyFallbackOnPermissionError === true;
22
+ const restore = options.copyFallbackRestore ?? "none";
23
+ const maxRestoreBytes = options.maxRestoreBytes;
24
+ const destinationHardlinks = options.destinationHardlinks;
25
+ const sourceIdentity = source.identity;
26
+ const sync = options.syncTempFile === true;
27
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
20
28
  if (attempt > 0)
21
- await params.assertSourceCurrent();
29
+ yield* source.assertCurrent();
22
30
  try {
23
- params.mutation.assert();
24
- await params.fsModule.rename(params.src, params.dest);
31
+ mutation.assert();
32
+ yield* io.rename(source.pathname, destination);
25
33
  return { method: "rename" };
26
34
  }
27
35
  catch (error) {
28
- params.mutation.rethrowRefusal();
36
+ mutation.rethrowRefusal();
29
37
  const code = readErrorCode(error);
30
- if (code === "EBUSY" && attempt < params.maxRetries) {
31
- await sleep(params.baseDelayMs * 2 ** attempt);
38
+ if (code === "EBUSY" && attempt < maxRetries) {
39
+ yield* io.delay(baseDelayMs * 2 ** attempt);
32
40
  continue;
33
41
  }
34
- if (params.copyFallbackOnPermissionError && (code === "EPERM" || code === "EEXIST")) {
35
- await copyFallbackReplace({
36
- fsModule: params.fsModule,
37
- src: params.src,
38
- dest: params.dest,
39
- destinationHardlinks: params.destinationHardlinks,
40
- restore: params.copyFallbackRestore,
41
- maxRestoreBytes: params.maxRestoreBytes,
42
- expectedSourceIdentity: params.sourceIdentity,
43
- sync: params.syncFallback,
44
- mutation: params.mutation,
45
- });
46
- return { method: "copy-fallback" };
47
- }
48
- throw error;
49
- }
50
- }
51
- throw new Error("Atomic rename retry loop exhausted.");
52
- }
53
- function renameWithRetrySync(params) {
54
- for (let attempt = 0; attempt <= params.maxRetries; attempt++) {
55
- if (attempt > 0)
56
- params.assertSourceCurrent();
57
- try {
58
- params.mutation.assert();
59
- params.fsModule.renameSync(params.src, params.dest);
60
- return { method: "rename" };
61
- }
62
- catch (error) {
63
- params.mutation.rethrowRefusal();
64
- const code = readErrorCode(error);
65
- if (code === "EBUSY" && attempt < params.maxRetries) {
66
- sleepSync(params.baseDelayMs * 2 ** attempt);
67
- continue;
68
- }
69
- if (params.copyFallbackOnPermissionError && (code === "EPERM" || code === "EEXIST")) {
70
- copyFallbackReplaceSync({
71
- fsModule: params.fsModule,
72
- src: params.src,
73
- dest: params.dest,
74
- destinationHardlinks: params.destinationHardlinks,
75
- restore: params.copyFallbackRestore,
76
- maxRestoreBytes: params.maxRestoreBytes,
77
- expectedSourceIdentity: params.sourceIdentity,
78
- fchmodSync: params.fchmodSync,
79
- sync: params.syncFallback,
80
- mutation: params.mutation,
42
+ if (copyFallbackOnPermissionError && (code === "EPERM" || code === "EEXIST")) {
43
+ yield* copyFallbackReplace(io, {
44
+ src: source.pathname,
45
+ dest: destination,
46
+ destinationHardlinks,
47
+ restore,
48
+ maxRestoreBytes,
49
+ expectedSourceIdentity: sourceIdentity,
50
+ sync,
51
+ mutation,
81
52
  });
82
53
  return { method: "copy-fallback" };
83
54
  }
@@ -107,48 +78,24 @@ function buildReplaceTempPath(filePath, tempPrefix) {
107
78
  const safePrefix = assertSafePathPrefix(tempPrefix ?? ".fs-safe-replace", { label: "atomic replace temp prefix" });
108
79
  return path.join(dir, `${safePrefix}.${process.pid}.${randomUUID()}.tmp`);
109
80
  }
110
- async function resolveMode(options, filePath) {
81
+ function* resolveMode(io, options, filePath) {
111
82
  const defaultMode = options.mode ?? 0o600;
112
- if (!options.preserveExistingMode) {
83
+ if (!options.preserveExistingMode)
113
84
  return defaultMode;
114
- }
115
- const fsModule = options.fileSystem?.promises ?? fs;
116
85
  let stat;
117
86
  try {
118
- stat = fsModule === fs ? syncFs.lstatSync(filePath) : await fsModule.lstat(filePath);
87
+ stat = yield* io.lstat(filePath);
119
88
  }
120
89
  catch (error) {
121
- if (hasErrorCode(error, "ENOENT")) {
122
- return defaultMode;
123
- }
124
- throw error;
125
- }
126
- return stat ? inheritedRegularFileMode(stat) : defaultMode;
127
- }
128
- function resolveModeSync(options, filePath) {
129
- const defaultMode = options.mode ?? 0o600;
130
- if (!options.preserveExistingMode) {
131
- return defaultMode;
132
- }
133
- const fsModule = options.fileSystem ?? syncFs;
134
- let stat;
135
- try {
136
- stat = fsModule.lstatSync(filePath);
137
- }
138
- catch (error) {
139
- if (!hasErrorCode(error, "ENOENT")) {
90
+ if (!hasErrorCode(error, "ENOENT"))
140
91
  throw error;
141
- }
142
92
  }
143
93
  return stat ? inheritedRegularFileMode(stat) : defaultMode;
144
94
  }
145
- function missingFchmodSyncError() {
146
- return new TypeError("fileSystem.fchmodSync is required when mode, dirMode, or preserveExistingMode is specified");
147
- }
148
95
  export async function replaceFileAtomic(options) {
149
96
  return await replaceFileAtomicWithDirectorySync(options);
150
97
  }
151
- // Internal owner hook: keep directory durability inside publication verification and serialization.
98
+ // Directory durability remains inside the serialized publication and verification lifetime.
152
99
  export async function replaceFileAtomicWithDirectorySync(options, syncParent) {
153
100
  const mutation = new AtomicMutation(options);
154
101
  const filePath = validateReplaceFilePath(options.filePath);
@@ -157,7 +104,7 @@ export async function replaceFileAtomicWithDirectorySync(options, syncParent) {
157
104
  validateRenameIdentity(renameIdentity);
158
105
  return await serializePathWrite(path.resolve(filePath), async () => {
159
106
  if (renameIdentity !== "verify-content-with-lock") {
160
- return await replaceFileAtomicUnserialized(options, filePath, renameIdentity, mutation, syncParent);
107
+ return await replaceAsync(options, filePath, renameIdentity, mutation, syncParent);
161
108
  }
162
109
  const fsModule = options.fileSystem?.promises ?? fs;
163
110
  const dir = path.dirname(filePath);
@@ -167,88 +114,11 @@ export async function replaceFileAtomicWithDirectorySync(options, syncParent) {
167
114
  mode: options.dirMode ?? 0o700,
168
115
  });
169
116
  mutation.assert();
170
- return await withAtomicRenameIdentityLock(filePath, async () => await replaceFileAtomicUnserialized(options, filePath, renameIdentity, mutation, syncParent));
117
+ return await withAtomicRenameIdentityLock(filePath, () => replaceAsync(options, filePath, renameIdentity, mutation, syncParent));
171
118
  });
172
119
  }
173
- async function replaceFileAtomicUnserialized(options, filePath, renameIdentity, mutation, syncParent) {
174
- const fsModule = options.fileSystem?.promises ?? fs;
175
- const dir = path.dirname(filePath);
176
- const dirMode = options.dirMode ?? 0o700;
177
- const mode = await resolveMode(options, filePath);
178
- const expectedHash = atomicExpectedContentHash(renameIdentity, options.content);
179
- const tempPath = buildReplaceTempPath(filePath, options.tempPrefix);
180
- const tempOwner = new AsyncAtomicTempOwner(tempPath);
181
- let originalFailure;
182
- try {
183
- mutation.assert();
184
- await fsModule.mkdir(fsModule === fs ? recursiveMkdirPath(dir) : dir, { recursive: true, mode: dirMode });
185
- await applyDirectoryMode({ fsModule, dirPath: dir, mode: dirMode, mutation });
186
- tempOwner.start();
187
- tempOwner.adopt(await writeTempFile({
188
- fsModule,
189
- tempPath,
190
- content: options.content,
191
- mode,
192
- sync: options.syncTempFile === true,
193
- onIdentity: tempOwner.onIdentity,
194
- mutation,
195
- }));
196
- await tempOwner.assertCurrent(fsModule);
197
- if (options.beforeRename) {
198
- await options.beforeRename({ filePath, tempPath });
199
- await tempOwner.assertCurrent(fsModule);
200
- }
201
- if (options.destinationHardlinks === "reject") {
202
- await assertDestinationHardlinkPolicy(fsModule, filePath, options.destinationHardlinks);
203
- await tempOwner.assertCurrent(fsModule);
204
- }
205
- const result = await renameWithRetry({
206
- fsModule,
207
- src: tempPath,
208
- dest: filePath,
209
- maxRetries: options.renameMaxRetries ?? 0,
210
- baseDelayMs: options.renameRetryBaseDelayMs ?? 50,
211
- copyFallbackOnPermissionError: options.copyFallbackOnPermissionError === true,
212
- copyFallbackRestore: options.copyFallbackRestore ?? "none",
213
- maxRestoreBytes: options.maxRestoreBytes,
214
- destinationHardlinks: options.destinationHardlinks,
215
- sourceIdentity: tempOwner.identity,
216
- assertSourceCurrent: () => tempOwner.assertCurrent(fsModule),
217
- syncFallback: options.syncTempFile === true,
218
- mutation,
219
- });
220
- if (result.method === "rename") {
221
- tempOwner.markRenamed();
222
- await tempOwner.assertPublished(fsModule, filePath, expectedHash, identity => mutation.destination("published", filePath, identity));
223
- }
224
- else {
225
- await tempOwner.assertCurrent(fsModule);
226
- }
227
- let didSyncParent = false;
228
- if (syncParent) {
229
- await syncParent(dir);
230
- didSyncParent = true;
231
- }
232
- else if (options.syncParentDir) {
233
- await syncDirectoryBestEffort(fsModule, dir);
234
- didSyncParent = true;
235
- }
236
- if (result.method === "rename" && didSyncParent) {
237
- await tempOwner.assertPublished(fsModule, filePath, expectedHash);
238
- }
239
- return result;
240
- }
241
- catch (error) {
242
- originalFailure = { error };
243
- throw error;
244
- }
245
- finally {
246
- await tempOwner.finish({
247
- fsModule,
248
- originalFailure,
249
- throwOnCleanupError: options.throwOnCleanupError === true,
250
- });
251
- }
120
+ function replaceAsync(options, filePath, renameIdentity, mutation, syncParent) {
121
+ return runAsync(replace(AtomicIo.async(options.fileSystem?.promises ?? fs), options, filePath, renameIdentity, mutation, syncParent));
252
122
  }
253
123
  export function replaceFileAtomicSync(options) {
254
124
  const mutation = new AtomicMutation(options);
@@ -257,7 +127,7 @@ export function replaceFileAtomicSync(options) {
257
127
  const renameIdentity = options.renameIdentity;
258
128
  validateRenameIdentity(renameIdentity);
259
129
  if (renameIdentity !== "verify-content-with-lock") {
260
- return replaceFileAtomicSyncUnserialized(options, filePath, renameIdentity, mutation);
130
+ return replaceSync(options, filePath, renameIdentity, mutation);
261
131
  }
262
132
  const fsModule = options.fileSystem ?? syncFs;
263
133
  const dir = path.dirname(filePath);
@@ -267,78 +137,72 @@ export function replaceFileAtomicSync(options) {
267
137
  mode: options.dirMode ?? 0o700,
268
138
  });
269
139
  mutation.assert();
270
- return withAtomicRenameIdentityLockSync(filePath, () => replaceFileAtomicSyncUnserialized(options, filePath, renameIdentity, mutation));
140
+ return withAtomicRenameIdentityLockSync(filePath, () => replaceSync(options, filePath, renameIdentity, mutation));
271
141
  }
272
- function replaceFileAtomicSyncUnserialized(options, filePath, renameIdentity, mutation) {
273
- const fsModule = options.fileSystem ?? syncFs;
142
+ function replaceSync(options, filePath, renameIdentity, mutation) {
143
+ return runSync(replace(AtomicIo.sync(options.fileSystem ?? syncFs), options, filePath, renameIdentity, mutation));
144
+ }
145
+ function* replace(io, options, filePath, renameIdentity, mutation, syncParent) {
274
146
  const dir = path.dirname(filePath);
275
147
  const dirMode = options.dirMode ?? 0o700;
276
- const mode = resolveModeSync(options, filePath);
148
+ const mode = yield* resolveMode(io, options, filePath);
277
149
  const expectedHash = atomicExpectedContentHash(renameIdentity, options.content);
278
- const fchmodSync = options.fileSystem?.fchmodSync ?? (options.fileSystem === undefined ? syncFs.fchmodSync : undefined);
279
- if (!fchmodSync &&
280
- (options.mode !== undefined ||
281
- options.preserveExistingMode === true ||
150
+ if (!io.asynchronous) {
151
+ const syncOptions = options;
152
+ io.fchmodSync = syncOptions.fileSystem?.fchmodSync ?? (syncOptions.fileSystem === undefined ? syncFs.fchmodSync : undefined);
153
+ if (!io.fchmodSync && (options.mode !== undefined || options.preserveExistingMode === true ||
282
154
  (process.platform !== "win32" && options.dirMode !== undefined))) {
283
- throw missingFchmodSyncError();
155
+ throw new TypeError("fileSystem.fchmodSync is required when mode, dirMode, or preserveExistingMode is specified");
156
+ }
284
157
  }
285
158
  const tempPath = buildReplaceTempPath(filePath, options.tempPrefix);
286
- const tempOwner = new SyncAtomicTempOwner(tempPath);
159
+ const tempOwner = new AtomicTempOwner(tempPath, io);
287
160
  let originalFailure;
288
161
  try {
289
162
  mutation.assert();
290
- fsModule.mkdirSync(fsModule === syncFs ? recursiveMkdirPath(dir) : dir, { recursive: true, mode: dirMode });
291
- applyDirectoryModeSync({ fsModule, dirPath: dir, mode: dirMode, fchmodSync, mutation });
163
+ yield* io.mkdir(dir, dirMode);
164
+ yield* applyDirectoryMode(io, { dirPath: dir, mode: dirMode, mutation });
292
165
  tempOwner.start();
293
- tempOwner.adopt(writeTempFileSync({
294
- fsModule,
166
+ tempOwner.adopt(yield* writeTempFile(io, {
295
167
  tempPath,
296
168
  content: options.content,
297
169
  mode,
298
- fchmodSync,
299
170
  sync: options.syncTempFile === true,
300
171
  onIdentity: tempOwner.onIdentity,
301
172
  mutation,
302
173
  }));
303
- tempOwner.assertCurrent(fsModule);
174
+ yield* tempOwner.assertCurrent();
304
175
  if (options.beforeRename) {
305
- assertSynchronousCallbackResult(options.beforeRename({ filePath, tempPath }), "beforeRename");
306
- tempOwner.assertCurrent(fsModule);
176
+ const result = options.beforeRename({ filePath, tempPath });
177
+ if (io.asynchronous)
178
+ yield* wait(result);
179
+ else
180
+ assertSynchronousCallbackResult(result, "beforeRename");
181
+ yield* tempOwner.assertCurrent();
307
182
  }
308
183
  if (options.destinationHardlinks === "reject") {
309
- assertDestinationHardlinkPolicySync(fsModule, filePath, options.destinationHardlinks);
310
- tempOwner.assertCurrent(fsModule);
184
+ yield* assertDestinationHardlinkPolicy(io, filePath, options.destinationHardlinks);
185
+ yield* tempOwner.assertCurrent();
311
186
  }
312
- const result = renameWithRetrySync({
313
- fsModule,
314
- src: tempPath,
315
- dest: filePath,
316
- maxRetries: options.renameMaxRetries ?? 0,
317
- baseDelayMs: options.renameRetryBaseDelayMs ?? 50,
318
- copyFallbackOnPermissionError: options.copyFallbackOnPermissionError === true,
319
- copyFallbackRestore: options.copyFallbackRestore ?? "none",
320
- maxRestoreBytes: options.maxRestoreBytes,
321
- destinationHardlinks: options.destinationHardlinks,
322
- sourceIdentity: tempOwner.identity,
323
- assertSourceCurrent: () => tempOwner.assertCurrent(fsModule),
324
- fchmodSync,
325
- syncFallback: options.syncTempFile === true,
326
- mutation,
327
- });
187
+ const result = yield* renameWithRetry(io, tempOwner, filePath, options, mutation);
328
188
  if (result.method === "rename") {
329
189
  tempOwner.markRenamed();
330
- tempOwner.assertPublished(fsModule, filePath, expectedHash, identity => mutation.destination("published", filePath, identity));
190
+ yield* tempOwner.assertPublished(filePath, expectedHash, identity => mutation.destination("published", filePath, identity));
331
191
  }
332
192
  else {
333
- tempOwner.assertCurrent(fsModule);
193
+ yield* tempOwner.assertCurrent();
334
194
  }
335
195
  let didSyncParent = false;
336
- if (options.syncParentDir) {
337
- syncDirectoryBestEffortSync(fsModule, dir);
196
+ if (syncParent) {
197
+ yield* wait(syncParent(dir));
198
+ didSyncParent = true;
199
+ }
200
+ else if (options.syncParentDir) {
201
+ yield* syncDirectoryBestEffort(io, dir);
338
202
  didSyncParent = true;
339
203
  }
340
204
  if (result.method === "rename" && didSyncParent) {
341
- tempOwner.assertPublished(fsModule, filePath, expectedHash);
205
+ yield* tempOwner.assertPublished(filePath, expectedHash);
342
206
  }
343
207
  return result;
344
208
  }
@@ -347,10 +211,6 @@ function replaceFileAtomicSyncUnserialized(options, filePath, renameIdentity, mu
347
211
  throw error;
348
212
  }
349
213
  finally {
350
- tempOwner.finish({
351
- fsModule,
352
- originalFailure,
353
- throwOnCleanupError: options.throwOnCleanupError === true,
354
- });
214
+ yield* tempOwner.finish({ originalFailure, throwOnCleanupError: options.throwOnCleanupError === true });
355
215
  }
356
216
  }
@@ -6,25 +6,14 @@ export function sameNormalizedPathSpelling(left, right) {
6
6
  // Preserve case on Windows, where individual directories can be case-sensitive.
7
7
  return path.resolve(left) === path.resolve(right);
8
8
  }
9
- function windowsComparablePath(value, normalize) {
10
- const original = normalize
11
- ? path.win32.normalize(value.replaceAll("/", "\\"))
12
- : value.replaceAll("/", "\\");
9
+ function windowsComparisonSpelling(original) {
13
10
  if (/^\\\\\?\\UNC\\/i.test(original)) {
14
- return {
15
- original,
16
- comparison: `\\\\${original.slice(8)}`,
17
- originalIndex: (comparisonIndex) => comparisonIndex + 6,
18
- };
11
+ return `\\\\${original.slice(8)}`;
19
12
  }
20
13
  if (original.startsWith("\\\\?\\")) {
21
- return {
22
- original,
23
- comparison: original.slice(4),
24
- originalIndex: (comparisonIndex) => comparisonIndex + 4,
25
- };
14
+ return original.slice(4);
26
15
  }
27
- return { original, comparison: original, originalIndex: (comparisonIndex) => comparisonIndex };
16
+ return original;
28
17
  }
29
18
  function trimWindowsTrailingSeparators(value) {
30
19
  const rootLength = path.win32.parse(value).root.length;
@@ -63,24 +52,25 @@ function windowsRootPrefix(rootPath, candidatePath) {
63
52
  const exact = exactWindowsRootPrefix(rootPath, candidatePath);
64
53
  if (exact)
65
54
  return exact;
66
- const trusted = windowsComparablePath(rootPath, true);
67
- const supplied = windowsComparablePath(candidatePath, false);
68
- const root = trimWindowsTrailingSeparators(trusted.original);
69
- const rootComparison = trimWindowsTrailingSeparators(trusted.comparison);
70
- const candidate = supplied.comparison;
71
- if (!path.win32.isAbsolute(supplied.original))
55
+ const trusted = path.win32.normalize(rootPath.replaceAll("/", "\\"));
56
+ const trustedComparison = windowsComparisonSpelling(trusted);
57
+ const supplied = candidatePath.replaceAll("/", "\\");
58
+ const candidate = windowsComparisonSpelling(supplied);
59
+ const root = trimWindowsTrailingSeparators(trusted);
60
+ const rootComparison = trimWindowsTrailingSeparators(trustedComparison);
61
+ if (!path.win32.isAbsolute(supplied))
72
62
  return undefined;
73
63
  let relativePath;
74
64
  let candidateRootPath;
75
65
  let admission;
76
66
  if (candidate === rootComparison) {
77
67
  relativePath = "";
78
- candidateRootPath = supplied.original;
68
+ candidateRootPath = supplied;
79
69
  admission = "exact";
80
70
  }
81
71
  else if (candidate.toLowerCase() === rootComparison.toLowerCase()) {
82
72
  relativePath = "";
83
- candidateRootPath = supplied.original;
73
+ candidateRootPath = supplied;
84
74
  admission = "identity";
85
75
  }
86
76
  else {
@@ -96,7 +86,7 @@ function windowsRootPrefix(rootPath, candidatePath) {
96
86
  return undefined;
97
87
  }
98
88
  relativePath = trimWindowsLeadingSeparators(candidate.slice(rootWithSep.length));
99
- candidateRootPath = supplied.original.slice(0, supplied.originalIndex(rootComparison.length));
89
+ candidateRootPath = supplied.slice(0, rootComparison.length + (supplied.length - candidate.length));
100
90
  }
101
91
  const rebasedPath = relativePath === ""
102
92
  ? root
@@ -4,9 +4,6 @@ export type RootContext = {
4
4
  rootDir: string;
5
5
  rootGuard?: AsyncDirectoryGuard<BigIntStats>;
6
6
  rootIdentity: {
7
- dev: number;
8
- ino: number;
9
- } | {
10
7
  dev: bigint;
11
8
  ino: bigint;
12
9
  };
@@ -27,7 +24,7 @@ export declare function assertRootIdentityCurrent(root: RootContext, observe?: (
27
24
  * This is deliberately separate from {@link assertRootIdentityCurrent}: callers
28
25
  * must not retain the returned guard beyond the operation that requested it.
29
26
  */
30
- export declare function createRootObservationGuard(root: RootContext): Promise<DirectoryObservationGuard | undefined>;
27
+ export declare function createRootObservationGuard(root: RootContext): Promise<DirectoryObservationGuard>;
31
28
  export declare function resolvePathInRoot(root: RootContext, relativePath: string, options?: {
32
29
  aliasErrorCode?: "outside-workspace" | "path-alias";
33
30
  allowFinalSymlink?: boolean;
@@ -3,7 +3,6 @@ import os from "node:os";
3
3
  import path from "node:path";
4
4
  import { extendDirectoryObservationGuard, inspectDirectoryIdentitySync, inspectDirectoryObservationSync, } from "./directory-guard.js";
5
5
  import { FsSafeError } from "./errors.js";
6
- import { sameFileIdentity } from "./file-identity.js";
7
6
  import { assertNoNulPathInput, assertNoUnsafeDeviceReadPath, hasNodeErrorCode, isNotFoundPathError, isPathInside, } from "./path.js";
8
7
  import { ROOT_PATH_ALIAS_POLICIES, resolveRootPath } from "./root-path.js";
9
8
  import { admitPathInsideRoot } from "./root-boundary.js";
@@ -12,9 +11,6 @@ import { isDriveRelativePath } from "./safe-path-segment.js";
12
11
  import { realpathSync } from "./realpath.js";
13
12
  import { inspectFileIdentity } from "./strict-file-identity.js";
14
13
  import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathFromBasePreservingWindowsRoot, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
15
- function hasExactRootIdentity(identity) {
16
- return typeof identity.dev === "bigint" && typeof identity.ino === "bigint";
17
- }
18
14
  export const ensureTrailingSep = (value) => value.endsWith(path.sep) ? value : value + path.sep;
19
15
  export function assertValidRootRelativePath(relativePath) {
20
16
  assertNoNulPathInput(relativePath, "relative path contains a NUL byte");
@@ -123,23 +119,13 @@ export function rootRelativeReadPath(root, filePath) {
123
119
  return raw;
124
120
  }
125
121
  export function assertRootIdentityCurrentSync(root, observe) {
126
- let current;
127
122
  try {
128
- if (typeof root.rootIdentity.dev === "bigint" && typeof root.rootIdentity.ino === "bigint") {
129
- const stat = inspectDirectoryIdentitySync(root.rootReal, { dev: root.rootIdentity.dev, ino: root.rootIdentity.ino });
130
- observe?.(stat);
131
- return;
132
- }
133
- current = fs.lstatSync(root.rootReal);
123
+ const stat = inspectDirectoryIdentitySync(root.rootReal, { dev: root.rootIdentity.dev, ino: root.rootIdentity.ino });
124
+ observe?.(stat);
134
125
  }
135
126
  catch (error) {
136
127
  throw rootPathChangedError(error instanceof Error ? error : undefined);
137
128
  }
138
- if (current.isSymbolicLink() ||
139
- !current.isDirectory() ||
140
- !sameFileIdentity(current, root.rootIdentity)) {
141
- throw rootPathChangedError();
142
- }
143
129
  }
144
130
  export async function assertRootIdentityCurrent(root, observe) {
145
131
  assertRootIdentityCurrentSync(root, observe);
@@ -152,9 +138,8 @@ export async function assertRootIdentityCurrent(root, observe) {
152
138
  */
153
139
  export async function createRootObservationGuard(root) {
154
140
  const rootIdentity = root.rootIdentity;
155
- if (!hasExactRootIdentity(rootIdentity)) {
156
- await assertRootIdentityCurrent(root);
157
- return undefined;
141
+ if (typeof rootIdentity?.dev !== "bigint" || typeof rootIdentity.ino !== "bigint") {
142
+ throw rootPathChangedError();
158
143
  }
159
144
  try {
160
145
  const observed = await inspectDirectoryObservationSync(root.rootReal, rootIdentity);
@@ -0,0 +1,52 @@
1
+ import fs from "node:fs";
2
+ import type { FileHandle } from "node:fs/promises";
3
+ import { type AnyAsyncDirectoryGuard } from "./directory-guard.js";
4
+ import { type PinnedMutationPolicySnapshot } from "./pinned-mutation-admission.js";
5
+ import { type RootContext } from "./root-context.js";
6
+ import type { RootWritePathSelection, RetainedRootWriteSelection } from "./root-write-admission.js";
7
+ import type { WritableOpenResult } from "./root-impl.js";
8
+ import type { RootOpenWritableOptions } from "./root-options.js";
9
+ export type OpenedWritableFileInRoot = {
10
+ opened: WritableOpenResult;
11
+ identity: fs.BigIntStats;
12
+ writeSelection?: RetainedRootWriteSelection;
13
+ cleanupCreated?: () => Promise<void>;
14
+ releaseCreationParent?: () => void;
15
+ };
16
+ export type WritableFileInRootParams = Omit<RootOpenWritableOptions, "writeMode"> & {
17
+ relativePath: string;
18
+ truncateExisting?: boolean;
19
+ append?: boolean;
20
+ expectedWritePath?: string;
21
+ keepCreationParent?: boolean;
22
+ };
23
+ export declare function rootWriteQueueKey(root: RootContext, relativePath: string): string;
24
+ export declare function buildAtomicWriteTempPath(targetPath: string): string;
25
+ export type MissingWritableFileInRoot = {
26
+ missing: true;
27
+ targetPath: string;
28
+ parentGuard: AnyAsyncDirectoryGuard;
29
+ writeSelection?: RootWritePathSelection;
30
+ };
31
+ type Creation = {
32
+ root: RootContext;
33
+ directory: string;
34
+ target: string;
35
+ mkdir: boolean;
36
+ private?: boolean;
37
+ policy?: PinnedMutationPolicySnapshot;
38
+ resolveCurrent(): Promise<string>;
39
+ assertBeforeMutation?: () => void;
40
+ originalPath?: string;
41
+ };
42
+ export declare function tryMkdirRootNative(params: Creation): Promise<boolean>;
43
+ export declare function tryOpenCreateRootNative(params: Creation & {
44
+ flags: number;
45
+ existingFlags: number;
46
+ mode: number;
47
+ }): Promise<{
48
+ handle: FileHandle;
49
+ cleanupCreated(): Promise<void>;
50
+ releaseCreationParent(): void;
51
+ }>;
52
+ export {};