@openclaw/fs-safe 0.15.0 → 0.17.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 (311) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/README.md +36 -7
  3. package/dist/absolute-path.d.ts.map +1 -1
  4. package/dist/absolute-path.js +2 -8
  5. package/dist/advanced.d.ts +3 -0
  6. package/dist/advanced.d.ts.map +1 -1
  7. package/dist/advanced.js +3 -0
  8. package/dist/archive-deadline.d.ts.map +1 -1
  9. package/dist/archive-deadline.js +22 -23
  10. package/dist/archive-kind.d.ts +0 -1
  11. package/dist/archive-kind.d.ts.map +1 -1
  12. package/dist/archive-kind.js +5 -17
  13. package/dist/archive-merge.d.ts +1 -0
  14. package/dist/archive-merge.d.ts.map +1 -1
  15. package/dist/archive-merge.js +4 -4
  16. package/dist/archive-native.d.ts +1 -0
  17. package/dist/archive-native.d.ts.map +1 -1
  18. package/dist/archive-native.js +1 -0
  19. package/dist/archive-options.d.ts +2 -0
  20. package/dist/archive-options.d.ts.map +1 -1
  21. package/dist/archive-parser.wasm +0 -0
  22. package/dist/archive-read.d.ts.map +1 -1
  23. package/dist/archive-read.js +6 -7
  24. package/dist/archive-tar-stream.d.ts +3 -0
  25. package/dist/archive-tar-stream.d.ts.map +1 -1
  26. package/dist/archive-tar-stream.js +56 -37
  27. package/dist/archive-tar-wasm.d.ts +16 -4
  28. package/dist/archive-tar-wasm.d.ts.map +1 -1
  29. package/dist/archive-tar-wasm.js +134 -34
  30. package/dist/archive-zip-count.d.ts.map +1 -1
  31. package/dist/archive-zip-count.js +21 -1
  32. package/dist/archive-zip-directory.d.ts.map +1 -1
  33. package/dist/archive-zip-directory.js +23 -1
  34. package/dist/archive-zip-loader.d.ts +2 -0
  35. package/dist/archive-zip-loader.d.ts.map +1 -1
  36. package/dist/archive-zip-loader.js +7 -0
  37. package/dist/archive-zip-names.d.ts.map +1 -1
  38. package/dist/archive-zip-names.js +7 -2
  39. package/dist/archive.d.ts.map +1 -1
  40. package/dist/archive.js +14 -9
  41. package/dist/byte-view.d.ts +3 -0
  42. package/dist/byte-view.d.ts.map +1 -0
  43. package/dist/byte-view.js +13 -0
  44. package/dist/clone-metadata.d.ts +1 -0
  45. package/dist/clone-metadata.d.ts.map +1 -1
  46. package/dist/clone-metadata.js +6 -2
  47. package/dist/create-directory.d.ts +20 -0
  48. package/dist/create-directory.d.ts.map +1 -0
  49. package/dist/create-directory.js +130 -0
  50. package/dist/create-file-async.d.ts +7 -0
  51. package/dist/create-file-async.d.ts.map +1 -0
  52. package/dist/create-file-async.js +121 -0
  53. package/dist/create-file.d.ts +8 -0
  54. package/dist/create-file.d.ts.map +1 -0
  55. package/dist/create-file.js +190 -0
  56. package/dist/create-owned-file.d.ts +8 -0
  57. package/dist/create-owned-file.d.ts.map +1 -0
  58. package/dist/create-owned-file.js +16 -0
  59. package/dist/create.d.ts +4 -0
  60. package/dist/create.d.ts.map +1 -0
  61. package/dist/create.js +2 -0
  62. package/dist/creation-darwin.d.ts +6 -0
  63. package/dist/creation-darwin.d.ts.map +1 -0
  64. package/dist/creation-darwin.js +70 -0
  65. package/dist/creation-file-state.d.ts +19 -0
  66. package/dist/creation-file-state.d.ts.map +1 -0
  67. package/dist/creation-file-state.js +118 -0
  68. package/dist/creation-path.d.ts +21 -0
  69. package/dist/creation-path.d.ts.map +1 -0
  70. package/dist/creation-path.js +71 -0
  71. package/dist/creation-permissions.d.ts +19 -0
  72. package/dist/creation-permissions.d.ts.map +1 -0
  73. package/dist/creation-permissions.js +125 -0
  74. package/dist/directory-durability.d.ts +7 -7
  75. package/dist/directory-durability.d.ts.map +1 -1
  76. package/dist/directory-durability.js +22 -80
  77. package/dist/directory-guard.d.ts +3 -0
  78. package/dist/directory-guard.d.ts.map +1 -1
  79. package/dist/directory-mode-node.d.ts +2 -0
  80. package/dist/directory-mode-node.d.ts.map +1 -1
  81. package/dist/directory-mode-node.js +8 -0
  82. package/dist/directory-receipt.d.ts +24 -0
  83. package/dist/directory-receipt.d.ts.map +1 -0
  84. package/dist/directory-receipt.js +123 -0
  85. package/dist/file-cleanup.d.ts +20 -0
  86. package/dist/file-cleanup.d.ts.map +1 -0
  87. package/dist/file-cleanup.js +81 -0
  88. package/dist/file-contents.d.ts +6 -0
  89. package/dist/file-contents.d.ts.map +1 -0
  90. package/dist/file-contents.js +40 -0
  91. package/dist/file-hash.d.ts.map +1 -1
  92. package/dist/file-hash.js +16 -4
  93. package/dist/file-lock-sync-root-acquire.d.ts.map +1 -1
  94. package/dist/file-lock-sync-root-acquire.js +3 -0
  95. package/dist/file-lock-sync-root-held.d.ts +1 -2
  96. package/dist/file-lock-sync-root-held.d.ts.map +1 -1
  97. package/dist/file-lock-sync-root-held.js +7 -5
  98. package/dist/file-lock-sync-stale-admission.d.ts.map +1 -1
  99. package/dist/file-lock-sync-stale-admission.js +3 -0
  100. package/dist/file-lock-sync.d.ts.map +1 -1
  101. package/dist/file-lock-sync.js +8 -11
  102. package/dist/file-observation.d.ts +1 -1
  103. package/dist/file-observation.d.ts.map +1 -1
  104. package/dist/file-store-boundary.d.ts +2 -6
  105. package/dist/file-store-boundary.d.ts.map +1 -1
  106. package/dist/file-store-boundary.js +3 -9
  107. package/dist/file-store-sync-write.d.ts.map +1 -1
  108. package/dist/file-store-sync-write.js +2 -5
  109. package/dist/file-store.js +3 -3
  110. package/dist/guarded-mkdir.d.ts +1 -0
  111. package/dist/guarded-mkdir.d.ts.map +1 -1
  112. package/dist/guarded-mkdir.js +27 -19
  113. package/dist/install-path.d.ts.map +1 -1
  114. package/dist/install-path.js +2 -5
  115. package/dist/json-durable-queue-ownership.d.ts.map +1 -1
  116. package/dist/json-durable-queue-ownership.js +2 -6
  117. package/dist/json-durable-queue-paths.d.ts.map +1 -1
  118. package/dist/json-durable-queue-paths.js +2 -24
  119. package/dist/json-durable-queue.d.ts.map +1 -1
  120. package/dist/json-durable-queue.js +10 -9
  121. package/dist/json.d.ts.map +1 -1
  122. package/dist/json.js +32 -75
  123. package/dist/local-roots.d.ts.map +1 -1
  124. package/dist/local-roots.js +19 -21
  125. package/dist/move-path-cleanup.d.ts +5 -19
  126. package/dist/move-path-cleanup.d.ts.map +1 -1
  127. package/dist/move-path-cleanup.js +57 -21
  128. package/dist/move-path.d.ts.map +1 -1
  129. package/dist/move-path.js +63 -40
  130. package/dist/native-binding.d.ts +11 -1
  131. package/dist/native-binding.d.ts.map +1 -1
  132. package/dist/native-fallback-warning.d.ts +4 -0
  133. package/dist/native-fallback-warning.d.ts.map +1 -0
  134. package/dist/native-fallback-warning.js +11 -0
  135. package/dist/native-operations.d.ts +0 -2
  136. package/dist/native-operations.d.ts.map +1 -1
  137. package/dist/native-operations.js +0 -24
  138. package/dist/native-parent-admission.d.ts +2 -0
  139. package/dist/native-parent-admission.d.ts.map +1 -1
  140. package/dist/native-parent-admission.js +3 -2
  141. package/dist/native-pinned-write-windows.d.ts +1 -1
  142. package/dist/native-pinned-write-windows.d.ts.map +1 -1
  143. package/dist/native-pinned-write-windows.js +173 -28
  144. package/dist/native-pinned-write.d.ts.map +1 -1
  145. package/dist/native-pinned-write.js +19 -3
  146. package/dist/native-policy-parent-windows.d.ts.map +1 -1
  147. package/dist/native-policy-parent-windows.js +15 -6
  148. package/dist/native-staged-file.d.ts +5 -3
  149. package/dist/native-staged-file.d.ts.map +1 -1
  150. package/dist/native-staged-file.js +90 -40
  151. package/dist/native.js +2 -2
  152. package/dist/opened-realpath.d.ts.map +1 -1
  153. package/dist/opened-realpath.js +11 -2
  154. package/dist/owner-dacl.d.ts.map +1 -1
  155. package/dist/owner-dacl.js +10 -4
  156. package/dist/path.d.ts.map +1 -1
  157. package/dist/path.js +2 -1
  158. package/dist/permissions.d.ts.map +1 -1
  159. package/dist/permissions.js +3 -17
  160. package/dist/pinned-write-input.d.ts +4 -0
  161. package/dist/pinned-write-input.d.ts.map +1 -0
  162. package/dist/pinned-write-input.js +35 -0
  163. package/dist/pinned-write-mode.d.ts +5 -0
  164. package/dist/pinned-write-mode.d.ts.map +1 -0
  165. package/dist/pinned-write-mode.js +31 -0
  166. package/dist/pinned-write-staged.d.ts +6 -0
  167. package/dist/pinned-write-staged.d.ts.map +1 -0
  168. package/dist/pinned-write-staged.js +186 -0
  169. package/dist/pinned-write-types.d.ts +3 -0
  170. package/dist/pinned-write-types.d.ts.map +1 -1
  171. package/dist/pinned-write.d.ts.map +1 -1
  172. package/dist/pinned-write.js +41 -147
  173. package/dist/private-directory.d.ts.map +1 -1
  174. package/dist/private-directory.js +18 -4
  175. package/dist/private-producer-handoff-sync.d.ts +14 -0
  176. package/dist/private-producer-handoff-sync.d.ts.map +1 -0
  177. package/dist/private-producer-handoff-sync.js +114 -0
  178. package/dist/private-producer-handoff.d.ts +22 -4
  179. package/dist/private-producer-handoff.d.ts.map +1 -1
  180. package/dist/private-producer-handoff.js +140 -77
  181. package/dist/publish-copy-stage.d.ts +2 -1
  182. package/dist/publish-copy-stage.d.ts.map +1 -1
  183. package/dist/publish-copy-stage.js +16 -7
  184. package/dist/publish-file.d.ts +2 -2
  185. package/dist/publish-file.d.ts.map +1 -1
  186. package/dist/publish-file.js +58 -98
  187. package/dist/regular-file.d.ts.map +1 -1
  188. package/dist/regular-file.js +35 -44
  189. package/dist/replace-file-copy-fallback.d.ts.map +1 -1
  190. package/dist/replace-file-copy-fallback.js +28 -26
  191. package/dist/replace-file-copy-source.d.ts.map +1 -1
  192. package/dist/replace-file-copy-source.js +13 -22
  193. package/dist/replace-file-descriptor.d.ts.map +1 -1
  194. package/dist/replace-file-descriptor.js +10 -16
  195. package/dist/replace-file-temp-owner.d.ts +0 -7
  196. package/dist/replace-file-temp-owner.d.ts.map +1 -1
  197. package/dist/replace-file-temp-owner.js +9 -60
  198. package/dist/replace-file.d.ts.map +1 -1
  199. package/dist/replace-file.js +9 -13
  200. package/dist/root-create-input.d.ts +2 -1
  201. package/dist/root-create-input.d.ts.map +1 -1
  202. package/dist/root-create-input.js +13 -4
  203. package/dist/root-directory-creation.d.ts +3 -3
  204. package/dist/root-directory-creation.d.ts.map +1 -1
  205. package/dist/root-directory-creation.js +15 -3
  206. package/dist/root-directory-list.d.ts.map +1 -1
  207. package/dist/root-directory-list.js +20 -3
  208. package/dist/root-file-final-admission.d.ts +1 -1
  209. package/dist/root-file-final-admission.d.ts.map +1 -1
  210. package/dist/root-file-final-admission.js +5 -2
  211. package/dist/root-file.d.ts.map +1 -1
  212. package/dist/root-file.js +3 -2
  213. package/dist/root-impl.d.ts.map +1 -1
  214. package/dist/root-impl.js +78 -27
  215. package/dist/root-move-noreplace.d.ts +2 -0
  216. package/dist/root-move-noreplace.d.ts.map +1 -1
  217. package/dist/root-move-noreplace.js +22 -13
  218. package/dist/root-options.d.ts +12 -4
  219. package/dist/root-options.d.ts.map +1 -1
  220. package/dist/root-path-stat.d.ts.map +1 -1
  221. package/dist/root-path-stat.js +59 -7
  222. package/dist/root-read-admission.d.ts.map +1 -1
  223. package/dist/root-read-admission.js +7 -2
  224. package/dist/root-remove.d.ts.map +1 -1
  225. package/dist/root-remove.js +15 -1
  226. package/dist/root-write-publication.js +1 -1
  227. package/dist/secret-file.d.ts.map +1 -1
  228. package/dist/secret-file.js +1 -0
  229. package/dist/secure-file-windows.d.ts +6 -0
  230. package/dist/secure-file-windows.d.ts.map +1 -1
  231. package/dist/secure-file-windows.js +34 -117
  232. package/dist/secure-file.js +2 -2
  233. package/dist/sidecar-lock-acquire.d.ts.map +1 -1
  234. package/dist/sidecar-lock-acquire.js +4 -6
  235. package/dist/sidecar-lock-handle.d.ts +3 -0
  236. package/dist/sidecar-lock-handle.d.ts.map +1 -1
  237. package/dist/sidecar-lock-handle.js +6 -0
  238. package/dist/sidecar-lock-reclaim.d.ts +1 -1
  239. package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
  240. package/dist/sidecar-lock-reclaim.js +11 -8
  241. package/dist/sidecar-lock-root.d.ts.map +1 -1
  242. package/dist/sidecar-lock-root.js +2 -1
  243. package/dist/sidecar-lock.d.ts.map +1 -1
  244. package/dist/sidecar-lock.js +3 -5
  245. package/dist/staged-directory.d.ts +2 -2
  246. package/dist/staged-directory.d.ts.map +1 -1
  247. package/dist/staged-directory.js +6 -6
  248. package/dist/staged-file-settlement.d.ts +17 -0
  249. package/dist/staged-file-settlement.d.ts.map +1 -0
  250. package/dist/staged-file-settlement.js +57 -0
  251. package/dist/strict-file-identity.d.ts +1 -1
  252. package/dist/strict-file-identity.d.ts.map +1 -1
  253. package/dist/strict-file-identity.js +9 -9
  254. package/dist/symlink-parents.d.ts.map +1 -1
  255. package/dist/symlink-parents.js +2 -27
  256. package/dist/temp-workspace-owner.js +4 -4
  257. package/dist/unicode-path.d.ts.map +1 -1
  258. package/dist/unicode-path.js +3 -0
  259. package/dist/walk.d.ts.map +1 -1
  260. package/dist/walk.js +4 -2
  261. package/dist/windows-owner.d.ts.map +1 -1
  262. package/dist/windows-owner.js +2 -1
  263. package/dist/windows-security-bridge.cs +336 -0
  264. package/dist/windows-security-bridge.ps1 +15 -0
  265. package/dist/windows-security-command.d.ts +26 -0
  266. package/dist/windows-security-command.d.ts.map +1 -0
  267. package/dist/windows-security-command.js +363 -0
  268. package/dist/windows-security-facts.d.ts +6 -0
  269. package/dist/windows-security-facts.d.ts.map +1 -0
  270. package/dist/windows-security-facts.js +108 -0
  271. package/dist/write-file-handle.d.ts +7 -0
  272. package/dist/write-file-handle.d.ts.map +1 -1
  273. package/dist/write-file-handle.js +23 -0
  274. package/dist/write-open-flags.d.ts.map +1 -1
  275. package/dist/write-open-flags.js +1 -8
  276. package/dist/write-queue.d.ts.map +1 -1
  277. package/dist/write-queue.js +1 -4
  278. package/docs/advanced.md +71 -2
  279. package/docs/archive.md +102 -39
  280. package/docs/atomic.md +29 -5
  281. package/docs/config.md +6 -2
  282. package/docs/contributing.md +48 -4
  283. package/docs/copy.md +2 -0
  284. package/docs/creation.md +132 -0
  285. package/docs/durability.md +59 -0
  286. package/docs/file-contents.md +68 -0
  287. package/docs/install.md +31 -7
  288. package/docs/json.md +5 -4
  289. package/docs/local-roots.md +2 -0
  290. package/docs/migrating-to-0.5.md +15 -6
  291. package/docs/migrating-to-0.6.md +9 -4
  292. package/docs/mutation-policy-proof.md +5 -3
  293. package/docs/native-helper.md +22 -9
  294. package/docs/native.md +47 -15
  295. package/docs/path.md +4 -4
  296. package/docs/permissions.md +37 -14
  297. package/docs/public-api.md +5 -0
  298. package/docs/quickstart.md +1 -1
  299. package/docs/reading.md +2 -2
  300. package/docs/regular-file.md +3 -0
  301. package/docs/root.md +43 -0
  302. package/docs/secret-file.md +11 -2
  303. package/docs/secure-file.md +9 -4
  304. package/docs/sidecar-lock.md +14 -5
  305. package/docs/staged-file.md +9 -3
  306. package/docs/store.md +3 -1
  307. package/docs/temp.md +4 -1
  308. package/docs/types.md +18 -2
  309. package/docs/walk.md +7 -0
  310. package/docs/writing.md +80 -7
  311. package/package.json +18 -15
package/docs/writing.md CHANGED
@@ -6,11 +6,11 @@ half-written replacement appears at the destination. Create-only writes
6
6
  (`create`, `createJson`, and `write` with `overwrite: false`) use sibling-temp
7
7
  staging with an atomic no-replace rename only on backends that provide one —
8
8
  the native binding, which `require` mode guarantees and `auto` mode uses when
9
- the binding loads. The pure-JavaScript fallback has no atomic no-clobber
10
- rename and does not stage: it claims the final name exclusively with `O_EXCL`
11
- and writes content in place, so a concurrent observer can see the new file
12
- before its content is complete. Use `require` mode when that visibility window
13
- matters.
9
+ the binding loads. Ordinary buffered creation in the pure-JavaScript fallback
10
+ claims the final name exclusively with `O_EXCL` and writes content in place, so
11
+ a concurrent observer can see the new file before its content is complete.
12
+ Buffered `create` and `createJson` accept `atomic: true` to stage complete content
13
+ on this fallback too. Streamed creation already stages before publication.
14
14
  `append` and `openWritable` intentionally modify an opened file in place;
15
15
  `move`, `remove`, and `mkdir` mutate directory entries rather than file bytes.
16
16
  Each verb applies the boundary checks appropriate to its operation.
@@ -113,6 +113,14 @@ Native and pure-JavaScript Windows writers honor the option. Replacement writes
113
113
  sync staged content before rename and the final mode through the retained file
114
114
  handle. Directory sync remains best-effort.
115
115
 
116
+ For `create` and `createJson`, `durable: "file"` requires each file sync to succeed,
117
+ including on `EPERM`; it overrides a disabled Root durability default. Parent
118
+ directory synchronization retains the existing best-effort policy. This also
119
+ applies to streamed creation and is independent of publication strategy. Boolean
120
+ durability options keep their existing behavior, including compatibility paths
121
+ that tolerate `EPERM`. A failed file sync before staged publication prevents
122
+ publication; a failure after publication can leave the complete file present.
123
+
116
124
  POSIX modes without read permission, including `0o000` and `0o200`, succeed:
117
125
  final verification uses a descriptor retained by the writer rather than reopening
118
126
  the published file. The requested mode is not relaxed for verification.
@@ -146,19 +154,61 @@ claimed exclusively first and content is written afterward, so observers can
146
154
  briefly see an empty file; failure cleanup removes a claimed file only when its
147
155
  identity is unchanged.
148
156
 
157
+ After a successful create-only write, failure to close its owned file handle
158
+ rejects the operation and leaves the complete file present. Ordinary buffered
159
+ creation in the JavaScript fallback preserves an earlier write or verification
160
+ failure if close also fails. Native, atomic, and streamed creation retain their
161
+ existing publication and cleanup diagnostics.
162
+
149
163
  ```ts
150
164
  try {
151
165
  await fs.create("config/seed.json", initial);
152
166
  } catch (err) {
153
- if (err instanceof FsSafeError && err.code !== "already-exists") throw err;
167
+ if (!(err instanceof FsSafeError) || err.code !== "already-exists") throw err;
154
168
  }
155
169
  ```
156
170
 
171
+ ### Atomic buffered creation
172
+
173
+ ```ts
174
+ await fs.create("config/seed.json", initial, { atomic: true });
175
+ await fs.createJson("config/settings.json", { enabled: true }, { atomic: true });
176
+ await fs.create("config/flushed.json", initial, { atomic: true, durable: "file" });
177
+ ```
178
+
179
+ `atomic: true` keeps the destination absent until all bytes have been written.
180
+ The native backend uses its no-replace rename; the JavaScript fallback hardlinks
181
+ the completed stage and unlinks its temporary name in the same JavaScript turn.
182
+ The fallback requires hardlink support and fails without publishing partial bytes
183
+ when that mechanism is unavailable. Other processes can briefly observe both
184
+ names. Existing and raced entries are preserved, including dangling symlinks;
185
+ ordinary confinement, type, hardlink, and symlink-policy rejections still apply.
186
+ `assertBeforeMutation` retains its live checks through content writes and publication.
187
+
188
+ Omitted or `false` preserves the existing buffered behavior. The option belongs
189
+ to buffered `create` and `createJson`, not replacement writes or Root defaults.
190
+ Streamed creation has no atomic opt-out. `atomic` changes visibility, not the
191
+ existing `durable` file/directory synchronization policy; it does not turn
192
+ best-effort synchronization into a strict crash-durability guarantee or strengthen
193
+ JavaScript pathname containment.
194
+
195
+ Atomic and streamed creates settle owned cleanup and close operations before
196
+ returning. Failed or unverifiable cleanup is reported rather than silently
197
+ discarded. Errors after publication and incomplete-settlement errors carry the
198
+ existing `StagedFileFailureDetails` publication/cleanup receipts where the writer
199
+ can establish them; native disposal can retain them inside a `SuppressedError`
200
+ cause. Preserve those details when handling errors: a rejection can follow
201
+ complete publication, and an indeterminate link or native rename must preserve names for
202
+ recovery. A cleanup or close failure also retains the original operation failure.
203
+ No later verification, mode, or synchronization failure authorizes deleting an
204
+ already published complete destination. See [receipt meanings](staged-file.md).
205
+
157
206
  ### Streamed creation
158
207
 
159
208
  Pass an `AsyncIterable<Uint8Array>` to `create()` when bytes come from a database,
160
209
  network response, or another incremental producer. Buffers are accepted chunks.
161
- The writer consumes each chunk completely before requesting the next one; it
210
+ The writer borrows `Uint8Array` slices without copying their payloads and counts
211
+ their actual byte bounds. It consumes each chunk completely before requesting the next one; it
162
212
  does not collect the full input in memory or expose a writable descriptor.
163
213
 
164
214
  ```ts
@@ -202,6 +252,12 @@ forcibly interrupted, so cancellation waits for its pending work and cleanup to
202
252
  settle. Do not mutate a yielded chunk until the next pull. Producer errors retain
203
253
  their original value when cleanup succeeds.
204
254
 
255
+ Streamed creation retains the `signal` and `assertBeforeMutation` callback
256
+ selected when the call starts. Replacing or deleting those options during a
257
+ producer wait does not change the in-flight operation. Abort the original signal
258
+ or update the live authority state checked by the original callback to revoke
259
+ it; the callback continues to receive the original options object as `this`.
260
+
205
261
  An aborted or failed operation can leave created parent directories. If a
206
262
  stage's identity or parent cannot be verified during cleanup, the existing
207
263
  guarded cleanup preserves it. After publication, later verification or cleanup
@@ -268,6 +324,10 @@ await fs.move("incoming/foo.txt", "archive/foo.txt", { overwrite: true });
268
324
 
269
325
  Both `from` and `to` are bounded; `..` in either is rejected.
270
326
 
327
+ Mutation policy is captured at call start; changes to caller-owned denial arrays
328
+ apply to later moves. For live cancellation or revocation, throw from
329
+ `assertBeforeMutation` immediately before dispatch.
330
+
271
331
  The default no-clobber mode requires the native helper. It admits both parent
272
332
  directory descriptors and performs a descriptor-relative no-replace rename, so
273
333
  a competitor that creates the target first is preserved and the source remains
@@ -407,6 +467,19 @@ an atomic check-and-delete syscall. Use OS isolation for that threat model.
407
467
  await fs.mkdir("snapshots/2026/05");
408
468
  ```
409
469
 
470
+ Pass `{ private: true }` to create missing components with private permissions.
471
+ An existing requested directory must already satisfy that policy; fs-safe does
472
+ not repair it or change existing ancestor permissions. Concurrent creators may
473
+ reuse the winner only after it passes the same checks.
474
+
475
+ Buffered, streamed, and JSON `create` calls also accept `private: true`.
476
+ New POSIX directories default to `0700` and files to `0600`; conflicting
477
+ group/world or privilege bits are rejected before creation. Restrictive
478
+ owner-only file modes remain available through `mode`. On Windows, creation
479
+ uses protected ACLs rather than interpreting POSIX mode bits as access rules.
480
+ This does not change `create`'s no-overwrite behavior or select its durability
481
+ policy. See [creation](creation.md) for supported backends and owned descriptors.
482
+
410
483
  ### `fs.ensureRoot()`
411
484
 
412
485
  Treats `""` / `"."` as the root itself. Useful when a generic helper computes a relative directory and might end up at the root.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openclaw/fs-safe",
3
- "version": "0.15.0",
3
+ "version": "0.17.0",
4
4
  "description": "Capability-style filesystem roots for Node.js apps that handle untrusted relative paths.",
5
5
  "keywords": [
6
6
  "filesystem",
@@ -25,6 +25,8 @@
25
25
  },
26
26
  "files": [
27
27
  "dist/archive-parser.wasm",
28
+ "dist/windows-security-bridge.cs",
29
+ "dist/windows-security-bridge.ps1",
28
30
  "dist/**/*.js",
29
31
  "dist/**/*.d.ts",
30
32
  "dist/**/*.d.ts.map",
@@ -144,10 +146,10 @@
144
146
  "test:bun": "bun node_modules/vitest/vitest.mjs run --config scripts/bun-vitest.config.ts",
145
147
  "test:bun:native": "bun scripts/bun-native-proof.mjs && bun node_modules/vitest/vitest.mjs run --config scripts/bun-native-vitest.config.ts",
146
148
  "test:coverage": "vitest run --coverage",
147
- "test:coverage:collect": "pnpm build && vitest run --coverage --coverage.reporter=json --coverage.thresholds.lines=0 --coverage.thresholds.functions=0 --coverage.thresholds.statements=0 --coverage.thresholds.branches=0",
149
+ "test:coverage:collect": "pnpm build && pnpm archive:wasm:allocator-tests && vitest run --coverage --coverage.reporter=json --coverage.thresholds.lines=0 --coverage.thresholds.functions=0 --coverage.thresholds.statements=0 --coverage.thresholds.branches=0",
148
150
  "test:coverage:merge": "node scripts/merge-coverage.mjs",
149
151
  "test:security": "vitest run test/fs-safe.test.ts test/read-boundary-bypass.test.ts test/write-boundary-bypass.test.ts test/additional-boundary-bypass.test.ts test/adversarial-boundary-payloads.test.ts",
150
- "check": "pnpm lint:file-size && pnpm lint:fs-boundary && pnpm build && pnpm docs:check && pnpm test && node scripts/check-pack.mjs",
152
+ "check": "pnpm lint:file-size && pnpm lint:fs-boundary && pnpm build && pnpm archive:wasm:allocator-tests && pnpm docs:check && pnpm test && node scripts/check-pack.mjs",
151
153
  "docs:check": "node scripts/check-doc-examples.mjs",
152
154
  "docs:site": "node scripts/build-docs-site.mjs",
153
155
  "native:build": "pnpm --filter @openclaw/fs-safe-native-build build",
@@ -164,24 +166,25 @@
164
166
  "crabbox:stop": "crabbox stop",
165
167
  "crabbox:warmup": "crabbox warmup",
166
168
  "archive:wasm": "node scripts/build-archive-wasm.mjs",
169
+ "archive:wasm:allocator-tests": "node scripts/build-archive-wasm.mjs --allocator-tests",
167
170
  "archive:producer-smoke": "node scripts/archive-producer-smoke.mjs"
168
171
  },
169
172
  "optionalDependencies": {
170
- "@openclaw/fs-safe-darwin-arm64": "0.15.0",
171
- "@openclaw/fs-safe-darwin-x64": "0.15.0",
172
- "@openclaw/fs-safe-linux-arm64-gnu": "0.15.0",
173
- "@openclaw/fs-safe-linux-arm64-musl": "0.15.0",
174
- "@openclaw/fs-safe-linux-x64-gnu": "0.15.0",
175
- "@openclaw/fs-safe-linux-x64-musl": "0.15.0",
176
- "@openclaw/fs-safe-win32-x64-msvc": "0.15.0",
173
+ "@openclaw/fs-safe-darwin-arm64": "0.17.0",
174
+ "@openclaw/fs-safe-darwin-x64": "0.17.0",
175
+ "@openclaw/fs-safe-linux-arm64-gnu": "0.17.0",
176
+ "@openclaw/fs-safe-linux-arm64-musl": "0.17.0",
177
+ "@openclaw/fs-safe-linux-x64-gnu": "0.17.0",
178
+ "@openclaw/fs-safe-linux-x64-musl": "0.17.0",
179
+ "@openclaw/fs-safe-win32-x64-msvc": "0.17.0",
177
180
  "jszip": "^3.10.2"
178
181
  },
179
182
  "devDependencies": {
180
183
  "@emnapi/runtime": "2.0.0-alpha.5",
181
- "@napi-rs/cli": "3.10.0",
182
- "@types/node": "^26.5.1",
184
+ "@napi-rs/cli": "3.10.3",
185
+ "@types/node": "^26.6.1",
183
186
  "@vitest/coverage-v8": "5.0.1",
184
- "fast-check": "^4.9.0",
187
+ "fast-check": "^4.10.1",
185
188
  "istanbul-lib-coverage": "3.2.2",
186
189
  "istanbul-lib-report": "3.0.1",
187
190
  "istanbul-reports": "3.2.0",
@@ -189,10 +192,10 @@
189
192
  "tar": "7.5.22",
190
193
  "typescript": "^7.0.2",
191
194
  "vite": "8.3.0",
192
- "vitest": "^5.0.0"
195
+ "vitest": "^5.0.1"
193
196
  },
194
197
  "engines": {
195
198
  "node": ">=22"
196
199
  },
197
- "packageManager": "pnpm@11.25.0"
200
+ "packageManager": "pnpm@12.4.2"
198
201
  }