@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
@@ -1,44 +1,29 @@
1
- import { syncFileBestEffort, syncFileBestEffortSync } from "./file-sync.js";
2
1
  import syncFs, {} from "node:fs";
3
- import fs, {} from "node:fs/promises";
2
+ import fs from "node:fs/promises";
4
3
  import { FsSafeError } from "./errors.js";
5
4
  import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
6
5
  import { ownDirectoryMode } from "./directory-mode-node.js";
7
- export async function syncDirectoryBestEffort(fsModule, dirPath) {
8
- let handle;
6
+ import { inspectAtomicIdentity, wait } from "./atomic-io.js";
7
+ function parentCloseFailure(primary, closeError) {
8
+ return primary ? new AggregateError([primary.error, closeError], "Atomic parent preparation and close failed") : closeError;
9
+ }
10
+ export function* syncDirectoryBestEffort(io, dirPath) {
11
+ let file;
9
12
  try {
10
- handle = await fsModule.open(dirPath, "r");
11
- await handle.sync();
13
+ file = yield* io.open(dirPath, "r");
14
+ yield* file.sync();
12
15
  }
13
16
  catch {
14
- // Best-effort on platforms/filesystems that do not support directory fsync.
17
+ // Directory synchronization and close remain best-effort.
15
18
  }
16
19
  finally {
17
20
  try {
18
- await handle?.close();
21
+ const closing = file?.close();
22
+ if (file && io.asynchronous)
23
+ yield closing;
19
24
  }
20
25
  catch {
21
- // Best-effort close also covers synchronous adapter throws.
22
- }
23
- }
24
- }
25
- export function syncDirectoryBestEffortSync(fsModule, dirPath) {
26
- let fd;
27
- try {
28
- fd = fsModule.openSync(dirPath, "r");
29
- fsModule.fsyncSync(fd);
30
- }
31
- catch {
32
- // Best-effort on platforms/filesystems that do not support directory fsync.
33
- }
34
- finally {
35
- if (fd !== undefined) {
36
- try {
37
- fsModule.closeSync(fd);
38
- }
39
- catch {
40
- // Best-effort close after directory fsync.
41
- }
26
+ // Preserve the operation's best-effort contract.
42
27
  }
43
28
  }
44
29
  }
@@ -54,7 +39,7 @@ function assertDirectory(identity, dirPath) {
54
39
  }
55
40
  return identity;
56
41
  }
57
- export async function pinDirectoryForMode(params) {
42
+ async function pinDirectoryForMode(params) {
58
43
  // Node does not enforce POSIX directory modes on Windows, and its directory
59
44
  // descriptors are not consistently openable. mkdir(mode) remains the only
60
45
  // bounded behavior there; never fall back to a pathname chmod.
@@ -84,81 +69,91 @@ export async function pinDirectoryForMode(params) {
84
69
  return owner;
85
70
  }
86
71
  catch (error) {
87
- await handle.close();
72
+ try {
73
+ await handle.close();
74
+ }
75
+ catch (closeError) {
76
+ throw parentCloseFailure({ error }, closeError);
77
+ }
88
78
  throw error;
89
79
  }
90
80
  }
91
- export async function applyDirectoryMode(params) {
92
- const owner = await pinDirectoryForMode(params);
93
- try {
94
- await owner?.apply(params.mode);
95
- }
96
- finally {
97
- await owner?.close();
98
- }
99
- }
100
- export function applyDirectoryModeSync(params) {
101
- if (process.platform === "win32") {
81
+ export function* applyDirectoryMode(io, params) {
82
+ if (io.asynchronous) {
83
+ const owner = yield* wait(pinDirectoryForMode({
84
+ ...params,
85
+ fsModule: io.asyncFs,
86
+ }));
87
+ let primary;
88
+ try {
89
+ if (owner)
90
+ yield* wait(owner.apply(params.mode));
91
+ }
92
+ catch (error) {
93
+ primary = { error };
94
+ throw error;
95
+ }
96
+ finally {
97
+ try {
98
+ if (owner)
99
+ yield* wait(owner.close());
100
+ }
101
+ catch (closeError) {
102
+ throw parentCloseFailure(primary, closeError);
103
+ }
104
+ }
102
105
  return;
103
106
  }
104
- const expected = inspectFileIdentitySync(() => assertDirectory(params.fsModule.lstatSync(params.dirPath, { bigint: true }), params.dirPath));
105
- const fd = params.fsModule.openSync(params.dirPath, directoryOpenFlags());
106
- try {
107
- inspectFileIdentitySync(() => assertDirectory(params.fsModule.fstatSync(fd, { bigint: true }), params.dirPath), expected);
108
- // chmod ignores file-type bits; mask so raw stat modes are tolerated.
109
- params.mutation?.assert();
110
- params.fchmodSync?.(fd, params.mode & 0o7777);
111
- }
112
- finally {
113
- params.fsModule.closeSync(fd);
114
- }
115
- }
116
- export async function writeTempFile(params) {
117
- params.mutation?.assert();
118
- const handle = await params.fsModule.open(params.tempPath, "wx", params.mode);
107
+ if (process.platform === "win32")
108
+ return;
109
+ const admit = (stat) => assertDirectory(stat, params.dirPath);
110
+ const expected = inspectAtomicIdentity(io, () => io.lstatExact(params.dirPath), undefined, false, admit);
111
+ const file = yield* io.open(params.dirPath, directoryOpenFlags());
112
+ let primary;
119
113
  try {
120
- // Custom adapters retain their async-only metadata contract.
121
- const inspect = () => params.fsModule === fs
122
- ? syncFs.fstatSync(handle.fd, { bigint: true }) : handle.stat({ bigint: true });
123
- const identity = await inspectFileIdentity(inspect);
124
- params.onIdentity?.(identity);
114
+ inspectAtomicIdentity(io, () => file.statExact(), expected, false, admit);
125
115
  params.mutation?.assert();
126
- await params.fsModule.writeFile(handle, params.content);
127
- await handle.chmod(params.mode);
128
- if (params.sync) {
129
- await syncFileBestEffort(handle);
130
- }
131
- await inspectFileIdentity(inspect, identity);
132
- return { handle, identity };
116
+ file.chmod(params.mode & 0o7777);
133
117
  }
134
118
  catch (error) {
119
+ primary = { error };
120
+ throw error;
121
+ }
122
+ finally {
135
123
  try {
136
- await handle.close();
124
+ file.close();
137
125
  }
138
126
  catch (closeError) {
139
- throw new AggregateError([error, closeError], "Atomic temp write and close failed");
127
+ throw parentCloseFailure(primary, closeError);
140
128
  }
141
- throw error;
142
129
  }
143
130
  }
144
- export function writeTempFileSync(params) {
131
+ export function* writeTempFile(io, params) {
145
132
  params.mutation?.assert();
146
- const fd = params.fsModule.openSync(params.tempPath, "wx", params.mode);
133
+ const file = yield* io.open(params.tempPath, "wx", params.mode);
147
134
  try {
148
- const identity = inspectFileIdentitySync(() => params.fsModule.fstatSync(fd, { bigint: true }));
135
+ const openedInspection = inspectAtomicIdentity(io, () => file.statExact());
136
+ const identity = (io.asynchronous ? (yield openedInspection) : openedInspection);
149
137
  params.onIdentity?.(identity);
150
138
  params.mutation?.assert();
151
- params.fsModule.writeFileSync(fd, params.content);
152
- params.fchmodSync?.(fd, params.mode);
153
- if (params.sync) {
154
- syncFileBestEffortSync(fd, params.fsModule);
155
- }
156
- inspectFileIdentitySync(() => params.fsModule.fstatSync(fd, { bigint: true }), identity);
157
- return { fd, identity };
139
+ const writing = file.writeFile(params.content, true);
140
+ if (io.asynchronous)
141
+ yield writing;
142
+ const chmod = file.chmod(params.mode);
143
+ if (io.asynchronous)
144
+ yield chmod;
145
+ if (params.sync)
146
+ yield* file.syncBestEffort();
147
+ const currentInspection = inspectAtomicIdentity(io, () => file.statExact(), identity);
148
+ if (io.asynchronous)
149
+ yield currentInspection;
150
+ return { file, identity };
158
151
  }
159
152
  catch (error) {
160
153
  try {
161
- params.fsModule.closeSync(fd);
154
+ const closing = file.close();
155
+ if (io.asynchronous)
156
+ yield closing;
162
157
  }
163
158
  catch (closeError) {
164
159
  throw new AggregateError([error, closeError], "Atomic temp write and close failed");
@@ -1,21 +1,19 @@
1
- import fsSync from "node:fs";
2
- import fs, { type FileHandle } from "node:fs/promises";
1
+ import type { BigIntStats } from "node:fs";
2
+ import { type AtomicFile, type AtomicIo, type Procedure } from "./atomic-io.js";
3
3
  import type { AtomicMutation } from "./replace-file-mutation.js";
4
- /** Borrow the writer's descriptor; never adopt an identity from a later pathname open. */
5
- export declare function captureAtomicDestination(fsModule: Pick<typeof fs, "lstat">, handle: FileHandle, pathname: string, mutation: AtomicMutation, rejectHardlinks: boolean): Promise<{
6
- verify: () => Promise<void>;
7
- writing: () => void;
8
- beforeWrite: () => Promise<void>;
9
- beforeRestore: () => Promise<void>;
10
- verifyRestore: () => Promise<void>;
11
- assertBeforeMutation: () => void;
12
- published: () => void;
13
- }>;
14
- export declare function captureAtomicDestinationSync(fsModule: Pick<typeof fsSync, "lstatSync" | "fstatSync">, fd: number, pathname: string, mutation: AtomicMutation, rejectHardlinks: boolean): {
15
- verify: () => void;
16
- writing: () => void;
17
- beforeWrite: () => void;
18
- beforeRestore: () => void;
19
- verifyRestore: () => void;
20
- published: () => void;
21
- };
4
+ /** Borrows the writer's descriptor; no later pathname open can replace it. */
5
+ export declare class AtomicDestination {
6
+ #private;
7
+ readonly io: AtomicIo;
8
+ readonly file: AtomicFile;
9
+ readonly pathname: string;
10
+ readonly mutation: AtomicMutation;
11
+ readonly rejectHardlinks: boolean;
12
+ readonly identity: BigIntStats;
13
+ constructor(io: AtomicIo, file: AtomicFile, pathname: string, mutation: AtomicMutation, rejectHardlinks: boolean, identity: BigIntStats);
14
+ verify(restore?: boolean): Procedure<void>;
15
+ beforeWrite(restore: boolean): Procedure<void>;
16
+ writing(): void;
17
+ published(): void;
18
+ }
19
+ export declare function captureAtomicDestination(io: AtomicIo, file: AtomicFile, pathname: string, mutation: AtomicMutation, rejectHardlinks: boolean): Procedure<AtomicDestination>;
@@ -1,8 +1,6 @@
1
- import fsSync, {} from "node:fs";
2
- import fs, {} from "node:fs/promises";
1
+ import { inspectAtomicIdentity } from "./atomic-io.js";
3
2
  import { hasErrorCode } from "./file-cleanup.js";
4
3
  import { FsSafeError } from "./errors.js";
5
- import { inspectFileIdentity, inspectFileIdentitySync } from "./strict-file-identity.js";
6
4
  function regular(stat, pathname, rejectHardlinks) {
7
5
  if (stat.isSymbolicLink() || !stat.isFile()) {
8
6
  throw new FsSafeError("path-mismatch", `Atomic replace destination changed: ${pathname}`);
@@ -12,112 +10,80 @@ function regular(stat, pathname, rejectHardlinks) {
12
10
  }
13
11
  return stat;
14
12
  }
15
- /** Borrow the writer's descriptor; never adopt an identity from a later pathname open. */
16
- export async function captureAtomicDestination(fsModule, handle, pathname, mutation, rejectHardlinks) {
17
- const inspect = () => fsModule === fs
18
- ? fsSync.fstatSync(handle.fd, { bigint: true }) : handle.stat({ bigint: true });
19
- const identity = await inspectFileIdentity(inspect);
20
- regular(identity, pathname, rejectHardlinks);
21
- let writing = false;
22
- let restoreDescriptorOnly = false;
23
- const verifyDescriptor = async () => {
24
- await inspectFileIdentity(async () => regular(await inspect(), pathname, rejectHardlinks), identity);
25
- };
26
- const verify = async () => {
13
+ /** Borrows the writer's descriptor; no later pathname open can replace it. */
14
+ export class AtomicDestination {
15
+ io;
16
+ file;
17
+ pathname;
18
+ mutation;
19
+ rejectHardlinks;
20
+ identity;
21
+ #writing = false;
22
+ #restoreDescriptorOnly = false;
23
+ constructor(io, file, pathname, mutation, rejectHardlinks, identity) {
24
+ this.io = io;
25
+ this.file = file;
26
+ this.pathname = pathname;
27
+ this.mutation = mutation;
28
+ this.rejectHardlinks = rejectHardlinks;
29
+ this.identity = identity;
30
+ }
31
+ *verify(restore = false) {
27
32
  let metadataFailure = false;
28
- const observe = async (read) => {
33
+ const descriptorOnly = restore && this.#restoreDescriptorOnly;
34
+ const owner = this;
35
+ function readFailure(error) {
36
+ metadataFailure = hasErrorCode(error, "EIO");
37
+ throw error;
38
+ }
39
+ function read(pathname) {
29
40
  try {
30
- return await read();
41
+ const stat = pathname
42
+ ? owner.io.lstatExact(owner.pathname, false) : owner.file.statExact();
43
+ return owner.io.asynchronous ? Promise.resolve(stat).catch(readFailure) : stat;
31
44
  }
32
45
  catch (error) {
33
- metadataFailure = hasErrorCode(error, "EIO");
34
- throw error;
46
+ return readFailure(error);
35
47
  }
36
- };
37
- try {
38
- await inspectFileIdentity(async () => regular(await observe(inspect), pathname, rejectHardlinks), identity);
39
- await inspectFileIdentity(async () => regular(await observe(() => fsModule.lstat(pathname, { bigint: true })), pathname, rejectHardlinks), identity);
40
48
  }
41
- catch (error) {
42
- if (writing && metadataFailure) {
43
- // A metadata observation failed, but the already-owned descriptor can
44
- // still restore its bytes after fresh exact descriptor verification.
45
- restoreDescriptorOnly = true;
46
- throw error;
47
- }
48
- mutation.refuse(error);
49
- }
50
- };
51
- const verifyRestore = async () => {
52
- if (!restoreDescriptorOnly)
53
- return await verify();
49
+ const admit = (stat) => regular(stat, owner.pathname, owner.rejectHardlinks);
54
50
  try {
55
- await verifyDescriptor();
56
- }
57
- catch (error) {
58
- mutation.refuse(error);
59
- }
60
- };
61
- return {
62
- verify,
63
- writing: () => { writing = true; mutation.destination("writing", pathname, identity); },
64
- beforeWrite: async () => {
65
- mutation.assert();
66
- await verify();
67
- },
68
- beforeRestore: async () => { mutation.assert(); await verifyRestore(); },
69
- verifyRestore,
70
- assertBeforeMutation: () => mutation.assert(),
71
- published: () => mutation.destination("published", pathname, identity),
72
- };
73
- }
74
- export function captureAtomicDestinationSync(fsModule, fd, pathname, mutation, rejectHardlinks) {
75
- const identity = inspectFileIdentitySync(() => fsModule.fstatSync(fd, { bigint: true }));
76
- regular(identity, pathname, rejectHardlinks);
77
- let writing = false;
78
- let restoreDescriptorOnly = false;
79
- const verify = () => {
80
- let metadataFailure = false;
81
- const observe = (read) => {
82
- try {
83
- return read();
84
- }
85
- catch (error) {
86
- metadataFailure = hasErrorCode(error, "EIO");
87
- throw error;
51
+ const descriptorInspection = inspectAtomicIdentity(this.io, () => read(false), this.identity, false, admit);
52
+ if (this.io.asynchronous)
53
+ yield descriptorInspection;
54
+ if (!descriptorOnly) {
55
+ const pathnameInspection = inspectAtomicIdentity(this.io, () => read(true), this.identity, false, admit);
56
+ if (this.io.asynchronous)
57
+ yield pathnameInspection;
88
58
  }
89
- };
90
- try {
91
- inspectFileIdentitySync(() => regular(observe(() => fsModule.fstatSync(fd, { bigint: true })), pathname, rejectHardlinks), identity);
92
- inspectFileIdentitySync(() => regular(observe(() => fsModule.lstatSync(pathname, { bigint: true })), pathname, rejectHardlinks), identity);
93
59
  }
94
60
  catch (error) {
95
- if (writing && metadataFailure) {
96
- restoreDescriptorOnly = true;
61
+ if (!descriptorOnly && this.#writing && metadataFailure) {
62
+ // Restore through the retained descriptor after a fresh exact check.
63
+ this.#restoreDescriptorOnly = true;
97
64
  throw error;
98
65
  }
99
- mutation.refuse(error);
100
- }
101
- };
102
- const verifyRestore = () => {
103
- if (!restoreDescriptorOnly)
104
- return verify();
105
- try {
106
- inspectFileIdentitySync(() => regular(fsModule.fstatSync(fd, { bigint: true }), pathname, rejectHardlinks), identity);
66
+ this.mutation.refuse(error);
107
67
  }
108
- catch (error) {
109
- mutation.refuse(error);
110
- }
111
- };
112
- return {
113
- verify,
114
- writing: () => { writing = true; mutation.destination("writing", pathname, identity); },
115
- beforeWrite: () => {
116
- mutation.assert();
117
- verify();
118
- },
119
- beforeRestore: () => { mutation.assert(); verifyRestore(); },
120
- verifyRestore,
121
- published: () => mutation.destination("published", pathname, identity),
122
- };
68
+ }
69
+ *beforeWrite(restore) {
70
+ this.mutation.assert();
71
+ yield* this.verify(restore);
72
+ // Async identity observations may revoke authority before dispatch resumes.
73
+ if (this.io.asynchronous)
74
+ this.mutation.assert();
75
+ }
76
+ writing() {
77
+ this.#writing = true;
78
+ this.mutation.destination("writing", this.pathname, this.identity);
79
+ }
80
+ published() {
81
+ this.mutation.destination("published", this.pathname, this.identity);
82
+ }
83
+ }
84
+ export function* captureAtomicDestination(io, file, pathname, mutation, rejectHardlinks) {
85
+ const inspection = inspectAtomicIdentity(io, () => file.statExact());
86
+ const identity = (io.asynchronous ? (yield inspection) : inspection);
87
+ regular(identity, pathname, rejectHardlinks);
88
+ return new AtomicDestination(io, file, pathname, mutation, rejectHardlinks, identity);
123
89
  }
@@ -1,49 +1,32 @@
1
- import syncFs, { type BigIntStats } from "node:fs";
2
- import fs, { type FileHandle } from "node:fs/promises";
3
- import { type TempPathRegistration } from "./temp-cleanup.js";
4
- type AsyncOwnerFileSystem = Pick<typeof fs, "lstat" | "open" | "unlink">;
5
- type SyncOwnerFileSystem = Pick<typeof syncFs, "closeSync" | "fstatSync" | "lstatSync" | "openSync" | "readFileSync" | "unlinkSync">;
1
+ import { type BigIntStats } from "node:fs";
2
+ import { type AtomicFile, type AtomicIo, type Procedure } from "./atomic-io.js";
6
3
  export type AtomicTempFailure = Readonly<{
7
4
  error: unknown;
8
5
  }>;
9
6
  export declare function removePathIfIdentityUnchanged(targetPath: string, identity: Pick<BigIntStats, "dev" | "ino">): Promise<void>;
10
- declare class AtomicTempOwner<Resource> {
7
+ export declare class AtomicTempOwner {
11
8
  readonly pathname: string;
12
- protected resource: Resource | undefined;
13
- protected recordedIdentity: BigIntStats | undefined;
14
- protected exists: boolean;
15
- protected unregister: TempPathRegistration;
16
- constructor(pathname: string);
9
+ private readonly io;
10
+ private resource;
11
+ private recordedIdentity;
12
+ private exists;
13
+ private readonly unregister;
14
+ constructor(pathname: string, io: AtomicIo);
17
15
  start(): void;
18
16
  readonly onIdentity: (identity: BigIntStats) => void;
19
17
  get identity(): BigIntStats;
20
18
  markRenamed(): void;
21
- protected takeResource(): Resource | undefined;
22
- }
23
- export declare class AsyncAtomicTempOwner extends AtomicTempOwner<FileHandle> {
19
+ private takeResource;
24
20
  adopt(temp: {
25
- handle: FileHandle;
21
+ file: AtomicFile;
26
22
  identity: BigIntStats;
27
23
  }): void;
28
- assertCurrent(fsModule: AsyncOwnerFileSystem, pathname?: string): Promise<void>;
29
- assertPublished(fsModule: AsyncOwnerFileSystem, pathname: string, expectedHash?: string, onVerified?: (identity: BigIntStats) => void): Promise<void>;
24
+ private inspectOwned;
25
+ assertCurrent(pathname?: string): Procedure<void>;
26
+ assertPublished(pathname: string, expectedHash?: string, onVerified?: (identity: BigIntStats) => void): Procedure<void>;
27
+ private cleanupOwnedPath;
30
28
  finish(params: {
31
- fsModule: AsyncOwnerFileSystem;
32
29
  originalFailure?: AtomicTempFailure;
33
30
  throwOnCleanupError: boolean;
34
- }): Promise<void>;
35
- }
36
- export declare class SyncAtomicTempOwner extends AtomicTempOwner<number> {
37
- adopt(temp: {
38
- fd: number;
39
- identity: BigIntStats;
40
- }): void;
41
- assertCurrent(fsModule: SyncOwnerFileSystem, pathname?: string): void;
42
- assertPublished(fsModule: SyncOwnerFileSystem, pathname: string, expectedHash?: string, onVerified?: (identity: BigIntStats) => void): void;
43
- finish(params: {
44
- fsModule: SyncOwnerFileSystem;
45
- originalFailure?: AtomicTempFailure;
46
- throwOnCleanupError: boolean;
47
- }): void;
31
+ }): Procedure<void>;
48
32
  }
49
- export {};