@openclaw/fs-safe 0.14.0 → 0.16.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.
- package/CHANGELOG.md +88 -0
- package/README.md +38 -10
- package/dist/advanced.d.ts +2 -1
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +2 -1
- package/dist/archive-kind.d.ts +0 -1
- package/dist/archive-kind.d.ts.map +1 -1
- package/dist/archive-kind.js +5 -17
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +113 -46
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-read.d.ts.map +1 -1
- package/dist/archive-read.js +10 -11
- package/dist/archive-tar-stream.d.ts +3 -0
- package/dist/archive-tar-stream.d.ts.map +1 -1
- package/dist/archive-tar-stream.js +56 -37
- package/dist/archive-tar-wasm.d.ts +16 -4
- package/dist/archive-tar-wasm.d.ts.map +1 -1
- package/dist/archive-tar-wasm.js +134 -34
- package/dist/archive-zip-directory.d.ts +4 -0
- package/dist/archive-zip-directory.d.ts.map +1 -1
- package/dist/archive-zip-directory.js +2 -0
- package/dist/archive-zip-entry.d.ts +6 -2
- package/dist/archive-zip-entry.d.ts.map +1 -1
- package/dist/archive-zip-entry.js +23 -8
- package/dist/archive-zip-integrity.d.ts.map +1 -1
- package/dist/archive-zip-integrity.js +3 -4
- package/dist/archive-zip-loader.d.ts.map +1 -1
- package/dist/archive-zip-loader.js +107 -31
- package/dist/archive-zip-names.d.ts +1 -0
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +6 -0
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +11 -11
- package/dist/bounded-read-stream.d.ts +0 -1
- package/dist/bounded-read-stream.d.ts.map +1 -1
- package/dist/bounded-read-stream.js +0 -6
- package/dist/clone-metadata.d.ts +1 -0
- package/dist/clone-metadata.d.ts.map +1 -1
- package/dist/clone-metadata.js +6 -2
- package/dist/copy-publication.d.ts +6 -0
- package/dist/copy-publication.d.ts.map +1 -1
- package/dist/copy-publication.js +3 -0
- package/dist/copy-tree-portable.d.ts.map +1 -1
- package/dist/copy-tree-portable.js +44 -24
- package/dist/copy.d.ts.map +1 -1
- package/dist/copy.js +29 -11
- package/dist/create-directory.d.ts +20 -0
- package/dist/create-directory.d.ts.map +1 -0
- package/dist/create-directory.js +130 -0
- package/dist/create-file-async.d.ts +7 -0
- package/dist/create-file-async.d.ts.map +1 -0
- package/dist/create-file-async.js +121 -0
- package/dist/create-file.d.ts +8 -0
- package/dist/create-file.d.ts.map +1 -0
- package/dist/create-file.js +190 -0
- package/dist/create-owned-file.d.ts +8 -0
- package/dist/create-owned-file.d.ts.map +1 -0
- package/dist/create-owned-file.js +16 -0
- package/dist/create.d.ts +4 -0
- package/dist/create.d.ts.map +1 -0
- package/dist/create.js +2 -0
- package/dist/creation-darwin.d.ts +7 -0
- package/dist/creation-darwin.d.ts.map +1 -0
- package/dist/creation-darwin.js +79 -0
- package/dist/creation-file-state.d.ts +19 -0
- package/dist/creation-file-state.d.ts.map +1 -0
- package/dist/creation-file-state.js +118 -0
- package/dist/creation-path.d.ts +21 -0
- package/dist/creation-path.d.ts.map +1 -0
- package/dist/creation-path.js +71 -0
- package/dist/creation-permissions.d.ts +19 -0
- package/dist/creation-permissions.d.ts.map +1 -0
- package/dist/creation-permissions.js +125 -0
- package/dist/directory-durability.d.ts +1 -1
- package/dist/directory-durability.d.ts.map +1 -1
- package/dist/directory-durability.js +22 -80
- package/dist/directory-guard.d.ts +3 -0
- package/dist/directory-guard.d.ts.map +1 -1
- package/dist/directory-mode-node.d.ts +2 -0
- package/dist/directory-mode-node.d.ts.map +1 -1
- package/dist/directory-mode-node.js +8 -0
- package/dist/directory-mode-owner.js +5 -5
- package/dist/directory-receipt.d.ts +24 -0
- package/dist/directory-receipt.d.ts.map +1 -0
- package/dist/directory-receipt.js +127 -0
- package/dist/file-cleanup.d.ts +19 -0
- package/dist/file-cleanup.d.ts.map +1 -0
- package/dist/file-cleanup.js +78 -0
- package/dist/file-handle-transfer.d.ts +2 -0
- package/dist/file-handle-transfer.d.ts.map +1 -1
- package/dist/file-handle-transfer.js +57 -2
- package/dist/file-identity.d.ts.map +1 -1
- package/dist/file-identity.js +18 -4
- package/dist/file-lock-sync-admission.d.ts +19 -0
- package/dist/file-lock-sync-admission.d.ts.map +1 -0
- package/dist/file-lock-sync-admission.js +93 -0
- package/dist/file-lock-sync-root-acquire.d.ts +4 -0
- package/dist/file-lock-sync-root-acquire.d.ts.map +1 -0
- package/dist/file-lock-sync-root-acquire.js +370 -0
- package/dist/file-lock-sync-root-arbitration.d.ts +18 -0
- package/dist/file-lock-sync-root-arbitration.d.ts.map +1 -0
- package/dist/file-lock-sync-root-arbitration.js +66 -0
- package/dist/file-lock-sync-root-held.d.ts +34 -0
- package/dist/file-lock-sync-root-held.d.ts.map +1 -0
- package/dist/file-lock-sync-root-held.js +393 -0
- package/dist/file-lock-sync-root-io.d.ts +44 -0
- package/dist/file-lock-sync-root-io.d.ts.map +1 -0
- package/dist/file-lock-sync-root-io.js +209 -0
- package/dist/file-lock-sync-root-mutation.d.ts +17 -0
- package/dist/file-lock-sync-root-mutation.d.ts.map +1 -0
- package/dist/file-lock-sync-root-mutation.js +277 -0
- package/dist/file-lock-sync-root-options.d.ts +20 -0
- package/dist/file-lock-sync-root-options.d.ts.map +1 -0
- package/dist/file-lock-sync-root-options.js +58 -0
- package/dist/file-lock-sync-root-registration.d.ts +2 -0
- package/dist/file-lock-sync-root-registration.d.ts.map +1 -0
- package/dist/file-lock-sync-root-registration.js +90 -0
- package/dist/file-lock-sync-root.d.ts +36 -0
- package/dist/file-lock-sync-root.d.ts.map +1 -0
- package/dist/file-lock-sync-root.js +361 -0
- package/dist/file-lock-sync-stale-admission.d.ts +24 -0
- package/dist/file-lock-sync-stale-admission.d.ts.map +1 -0
- package/dist/file-lock-sync-stale-admission.js +205 -0
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +245 -205
- package/dist/file-observation.d.ts +1 -1
- package/dist/file-observation.d.ts.map +1 -1
- package/dist/file-store-boundary.d.ts +2 -6
- package/dist/file-store-boundary.d.ts.map +1 -1
- package/dist/file-store-boundary.js +3 -9
- package/dist/file-store-prune.d.ts.map +1 -1
- package/dist/file-store-prune.js +5 -1
- package/dist/file-store-sync-write.d.ts.map +1 -1
- package/dist/file-store-sync-write.js +5 -8
- package/dist/file-store.d.ts.map +1 -1
- package/dist/file-store.js +47 -12
- package/dist/guarded-mkdir.d.ts +1 -0
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +36 -7
- package/dist/json-document-store.d.ts.map +1 -1
- package/dist/json-document-store.js +22 -15
- package/dist/json-durable-queue-ownership.d.ts +0 -1
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +0 -6
- package/dist/move-path.js +1 -1
- package/dist/native-binding.d.ts +13 -1
- package/dist/native-binding.d.ts.map +1 -1
- package/dist/native-fallback-warning.d.ts +4 -0
- package/dist/native-fallback-warning.d.ts.map +1 -0
- package/dist/native-fallback-warning.js +11 -0
- package/dist/native-operations.d.ts +0 -2
- package/dist/native-operations.d.ts.map +1 -1
- package/dist/native-operations.js +0 -24
- package/dist/native-parent-admission.d.ts +5 -2
- package/dist/native-parent-admission.d.ts.map +1 -1
- package/dist/native-parent-admission.js +27 -7
- package/dist/native-pinned-write-windows.d.ts +1 -1
- package/dist/native-pinned-write-windows.d.ts.map +1 -1
- package/dist/native-pinned-write-windows.js +174 -29
- package/dist/native-pinned-write.d.ts.map +1 -1
- package/dist/native-pinned-write.js +26 -14
- package/dist/native-policy-parent-windows.d.ts +14 -0
- package/dist/native-policy-parent-windows.d.ts.map +1 -0
- package/dist/native-policy-parent-windows.js +209 -0
- package/dist/native-rename-outcome.d.ts +4 -0
- package/dist/native-rename-outcome.d.ts.map +1 -0
- package/dist/native-rename-outcome.js +8 -0
- package/dist/native-staged-file.d.ts +3 -2
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +121 -72
- package/dist/output.d.ts.map +1 -1
- package/dist/output.js +12 -8
- package/dist/owner-dacl.d.ts.map +1 -1
- package/dist/owner-dacl.js +10 -4
- package/dist/path-prefix.d.ts.map +1 -1
- package/dist/path-prefix.js +30 -8
- package/dist/path-suffix-aliases.d.ts +2 -0
- package/dist/path-suffix-aliases.d.ts.map +1 -1
- package/dist/path-suffix-aliases.js +25 -17
- package/dist/permission-exec.d.ts +2 -0
- package/dist/permission-exec.d.ts.map +1 -1
- package/dist/permission-exec.js +150 -21
- package/dist/permissions-windows.js +1 -1
- package/dist/pinned-mutation-admission.d.ts.map +1 -1
- package/dist/pinned-mutation-admission.js +10 -5
- package/dist/pinned-mutation-observation.d.ts +0 -1
- package/dist/pinned-mutation-observation.d.ts.map +1 -1
- package/dist/pinned-mutation-observation.js +0 -19
- package/dist/pinned-mutation-shared-route.d.ts +1 -0
- package/dist/pinned-mutation-shared-route.d.ts.map +1 -1
- package/dist/pinned-mutation-shared-route.js +1 -1
- package/dist/pinned-write-input.d.ts +4 -0
- package/dist/pinned-write-input.d.ts.map +1 -0
- package/dist/pinned-write-input.js +25 -0
- package/dist/pinned-write-mode.d.ts +5 -0
- package/dist/pinned-write-mode.d.ts.map +1 -0
- package/dist/pinned-write-mode.js +24 -0
- package/dist/pinned-write-staged.d.ts +6 -0
- package/dist/pinned-write-staged.d.ts.map +1 -0
- package/dist/pinned-write-staged.js +187 -0
- package/dist/pinned-write-types.d.ts +5 -0
- package/dist/pinned-write-types.d.ts.map +1 -1
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +35 -145
- package/dist/private-directory.d.ts.map +1 -1
- package/dist/private-directory.js +18 -4
- package/dist/private-producer-handoff-sync.d.ts +14 -0
- package/dist/private-producer-handoff-sync.d.ts.map +1 -0
- package/dist/private-producer-handoff-sync.js +114 -0
- package/dist/private-producer-handoff.d.ts +22 -4
- package/dist/private-producer-handoff.d.ts.map +1 -1
- package/dist/private-producer-handoff.js +140 -77
- package/dist/private-temp-workspace.d.ts.map +1 -1
- package/dist/private-temp-workspace.js +75 -121
- package/dist/publish-copy-stage.d.ts +2 -1
- package/dist/publish-copy-stage.d.ts.map +1 -1
- package/dist/publish-copy-stage.js +16 -7
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +2 -2
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +1 -15
- package/dist/replace-directory.d.ts.map +1 -1
- package/dist/replace-directory.js +256 -18
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +62 -70
- package/dist/replace-file-copy-source.d.ts.map +1 -1
- package/dist/replace-file-copy-source.js +10 -12
- package/dist/replace-file-temp-owner.d.ts +5 -9
- package/dist/replace-file-temp-owner.d.ts.map +1 -1
- package/dist/replace-file-temp-owner.js +56 -72
- package/dist/replace-file.js +6 -6
- package/dist/retained-directory-replacement.d.ts +26 -0
- package/dist/retained-directory-replacement.d.ts.map +1 -0
- package/dist/retained-directory-replacement.js +193 -0
- package/dist/root-boundary.d.ts +1 -0
- package/dist/root-boundary.d.ts.map +1 -1
- package/dist/root-boundary.js +4 -0
- package/dist/root-context.d.ts +0 -8
- package/dist/root-context.d.ts.map +1 -1
- package/dist/root-context.js +0 -3
- package/dist/root-create-input.d.ts +8 -1
- package/dist/root-create-input.d.ts.map +1 -1
- package/dist/root-create-input.js +17 -4
- package/dist/root-directory-creation.d.ts +3 -3
- package/dist/root-directory-creation.d.ts.map +1 -1
- package/dist/root-directory-creation.js +15 -3
- package/dist/root-directory-list.d.ts +1 -0
- package/dist/root-directory-list.d.ts.map +1 -1
- package/dist/root-directory-list.js +1 -0
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +46 -17
- package/dist/root-move-noreplace.d.ts.map +1 -1
- package/dist/root-move-noreplace.js +24 -15
- package/dist/root-options.d.ts +12 -4
- package/dist/root-options.d.ts.map +1 -1
- package/dist/root-path-errors.d.ts +1 -0
- package/dist/root-path-errors.d.ts.map +1 -1
- package/dist/root-path-errors.js +11 -2
- package/dist/root-path-existing.d.ts.map +1 -1
- package/dist/root-path-existing.js +11 -35
- package/dist/root-path-stat.d.ts.map +1 -1
- package/dist/root-path-stat.js +59 -7
- package/dist/root-path.js +1 -13
- package/dist/root-remove.d.ts +1 -0
- package/dist/root-remove.d.ts.map +1 -1
- package/dist/root-remove.js +4 -0
- package/dist/root-walk.d.ts +1 -1
- package/dist/root-walk.d.ts.map +1 -1
- package/dist/root-walk.js +17 -2
- package/dist/root-write-admission.d.ts +0 -2
- package/dist/root-write-admission.d.ts.map +1 -1
- package/dist/root-write-admission.js +1 -15
- package/dist/root-write-complete-parent.d.ts.map +1 -1
- package/dist/root-write-complete-parent.js +7 -23
- package/dist/root-write-publication.js +1 -1
- package/dist/root-write-verification.d.ts.map +1 -1
- package/dist/root-write-verification.js +29 -42
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +3 -24
- package/dist/secret-read-async.d.ts.map +1 -1
- package/dist/secret-read-async.js +3 -24
- package/dist/secret-read-policy.d.ts +6 -2
- package/dist/secret-read-policy.d.ts.map +1 -1
- package/dist/secret-read-policy.js +26 -2
- package/dist/secure-file-windows.d.ts +6 -0
- package/dist/secure-file-windows.d.ts.map +1 -1
- package/dist/secure-file-windows.js +34 -117
- package/dist/secure-file.js +2 -2
- package/dist/sibling-temp.d.ts.map +1 -1
- package/dist/sibling-temp.js +23 -12
- package/dist/sidecar-lock-acquire.d.ts +2 -28
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +288 -199
- package/dist/sidecar-lock-admission-context.d.ts +19 -0
- package/dist/sidecar-lock-admission-context.d.ts.map +1 -0
- package/dist/sidecar-lock-admission-context.js +60 -0
- package/dist/sidecar-lock-admission-parser.d.ts +43 -0
- package/dist/sidecar-lock-admission-parser.d.ts.map +1 -0
- package/dist/sidecar-lock-admission-parser.js +113 -0
- package/dist/sidecar-lock-admission.d.ts +35 -0
- package/dist/sidecar-lock-admission.d.ts.map +1 -0
- package/dist/sidecar-lock-admission.js +7 -0
- package/dist/sidecar-lock-reclaim.d.ts +9 -4
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +80 -25
- package/dist/sidecar-lock-root.d.ts.map +1 -1
- package/dist/sidecar-lock-root.js +2 -1
- package/dist/sidecar-lock-stale-admission.d.ts +39 -0
- package/dist/sidecar-lock-stale-admission.d.ts.map +1 -0
- package/dist/sidecar-lock-stale-admission.js +232 -0
- package/dist/sidecar-lock-target.d.ts +8 -0
- package/dist/sidecar-lock-target.d.ts.map +1 -0
- package/dist/sidecar-lock-target.js +55 -0
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +100 -16
- package/dist/staged-directory.d.ts.map +1 -1
- package/dist/staged-directory.js +6 -6
- package/dist/staged-file-settlement.d.ts +17 -0
- package/dist/staged-file-settlement.d.ts.map +1 -0
- package/dist/staged-file-settlement.js +57 -0
- package/dist/temp-workspace-descriptor.d.ts.map +1 -1
- package/dist/temp-workspace-descriptor.js +9 -27
- package/dist/temp-workspace-owner.d.ts.map +1 -1
- package/dist/temp-workspace-owner.js +8 -8
- package/dist/walk.d.ts +5 -1
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +19 -6
- package/dist/windows-owner.d.ts.map +1 -1
- package/dist/windows-owner.js +4 -3
- package/dist/windows-security-bridge.cs +336 -0
- package/dist/windows-security-bridge.ps1 +15 -0
- package/dist/windows-security-command.d.ts +26 -0
- package/dist/windows-security-command.d.ts.map +1 -0
- package/dist/windows-security-command.js +363 -0
- package/dist/windows-security-facts.d.ts +6 -0
- package/dist/windows-security-facts.d.ts.map +1 -0
- package/dist/windows-security-facts.js +108 -0
- package/docs/advanced.md +4 -2
- package/docs/archive.md +97 -46
- package/docs/atomic.md +85 -8
- package/docs/config.md +6 -2
- package/docs/contributing.md +44 -4
- package/docs/copy.md +37 -0
- package/docs/creation.md +128 -0
- package/docs/durability.md +24 -0
- package/docs/file-store.md +19 -0
- package/docs/install.md +31 -7
- package/docs/json-store.md +5 -0
- package/docs/migrating-to-0.5.md +15 -6
- package/docs/migrating-to-0.6.md +9 -4
- package/docs/native-helper.md +32 -12
- package/docs/native.md +38 -7
- package/docs/output.md +6 -0
- package/docs/path-prefix.md +10 -0
- package/docs/path-suffix-aliases.md +51 -6
- package/docs/permissions.md +50 -14
- package/docs/public-api.md +3 -2
- package/docs/root.md +56 -3
- package/docs/secret-file.md +11 -2
- package/docs/secure-file.md +9 -4
- package/docs/sidecar-lock.md +114 -8
- package/docs/staged-file.md +12 -3
- package/docs/temp.md +20 -3
- package/docs/walk.md +67 -1
- package/docs/writing.md +76 -6
- package/package.json +19 -16
- package/dist/darwin-acl.d.ts +0 -4
- package/dist/darwin-acl.d.ts.map +0 -1
- package/dist/darwin-acl.js +0 -24
package/docs/walk.md
CHANGED
|
@@ -59,12 +59,43 @@ type WalkDirectoryOptions = {
|
|
|
59
59
|
include?: (entry: WalkDirectoryEntry) => boolean;
|
|
60
60
|
descend?: (entry: WalkDirectoryEntry) => boolean;
|
|
61
61
|
};
|
|
62
|
+
|
|
63
|
+
type AsyncWalkDirectoryOptions = Omit<WalkDirectoryOptions, "include" | "descend"> & {
|
|
64
|
+
include?: (entry: WalkDirectoryEntry) => boolean | Promise<boolean>;
|
|
65
|
+
descend?: (entry: WalkDirectoryEntry) => boolean | Promise<boolean>;
|
|
66
|
+
};
|
|
62
67
|
```
|
|
63
68
|
|
|
64
69
|
`symlinks` defaults to `"skip"`. `"include"` returns symlink entries without following them. `"follow"` resolves symlinks with `stat()` and may descend into linked directories, so use it only when that is intentional. Already-visited real directories are skipped so symlink cycles do not recurse forever.
|
|
65
70
|
|
|
71
|
+
Before descending into a child directory, `skip` and `include` recheck whether
|
|
72
|
+
that entry has become a symlink, including changes made while a filter waits.
|
|
73
|
+
The explicitly supplied walk root may still be a symlink. This best-effort
|
|
74
|
+
child check does not turn the standalone walker into a confinement boundary;
|
|
75
|
+
use `Root.walk()` when root confinement is required.
|
|
76
|
+
|
|
66
77
|
`include` controls which entries are returned. `descend` controls which directory entries are traversed. A skipped directory can still be returned if `include` accepts it.
|
|
67
78
|
|
|
79
|
+
The asynchronous `walkDirectory()` accepts `AsyncWalkDirectoryOptions`. It resolves each `include` decision before calling `descend`, and resolves descent before reading the directory's children. Decisions run serially in the existing filesystem-order depth-first traversal. Both callbacks retain the supplied options object as their `this` receiver.
|
|
80
|
+
|
|
81
|
+
Absent callbacks and primitive results keep the synchronous selection path. Object and function results are awaited directly, including promises and thenables. For JavaScript callers, nullish results retain the default `true`; other resolved values use their existing truthiness. Return booleans or promises of booleans for the typed API.
|
|
82
|
+
|
|
83
|
+
```ts
|
|
84
|
+
import fs from "node:fs/promises";
|
|
85
|
+
import path from "node:path";
|
|
86
|
+
|
|
87
|
+
const scan = await walkDirectory("/safe/workspace", {
|
|
88
|
+
include: (entry) => entry.kind === "file",
|
|
89
|
+
descend: async (entry) => {
|
|
90
|
+
const marked = await fs.access(path.join(entry.path, "SKILL.md"))
|
|
91
|
+
.then(() => true, () => false);
|
|
92
|
+
return !marked;
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
This prunes a directory after finding its marker without listing that directory's children. Callback throws and promise rejections reject the walk; they are not directory failures in `failedDirs`. Filtering still consumes the examined-entry budget. `WalkDirectoryOptions` and `walkDirectorySync()` remain synchronous; the async options do not add confinement or cancellation to the standalone walker.
|
|
98
|
+
|
|
68
99
|
Unreadable directories are skipped rather than throwing, but every skipped directory is recorded in `failedDirs`. This keeps the helper suitable for best-effort inventories while letting pruning jobs tell an incomplete scan from an empty one: a destructive reconcile that deletes state for paths missing from `entries` must first confirm `failedDirs` holds no real read failures, or a transient `EIO`/`EACCES` blip would be mistaken for mass deletion. Use a stricter root-bounded operation when every entry must be accounted for.
|
|
69
100
|
|
|
70
101
|
## Root-bounded async iteration
|
|
@@ -123,7 +154,9 @@ If a thrown walk failure and directory close both fail, disposal throws a
|
|
|
123
154
|
`SuppressedError` with the close failure in `error` and the original failure in
|
|
124
155
|
`suppressed`, preserving both causes.
|
|
125
156
|
|
|
126
|
-
`entryFilter` is evaluated for each resolved file, directory, or other entry
|
|
157
|
+
`entryFilter` is evaluated for each resolved file, directory, or other entry.
|
|
158
|
+
The `RootWalkEntryFilter` callback returns a `RootWalkEntryFilterResult`
|
|
159
|
+
or a `Promise<RootWalkEntryFilterResult>`:
|
|
127
160
|
|
|
128
161
|
```ts
|
|
129
162
|
for await (const entry of capability.walk("", {
|
|
@@ -147,10 +180,43 @@ The result values are `"include"`, `"skip"`, and `"skip-subtree"`. Plain
|
|
|
147
180
|
`"skip-subtree"` omits that directory and prunes its descendants. Returning
|
|
148
181
|
`"skip-subtree"` for a non-directory is equivalent to `"skip"`.
|
|
149
182
|
|
|
183
|
+
An asynchronous filter can inspect a marker before deciding whether to prune:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
for await (const entry of capability.walk("", {
|
|
187
|
+
symlinkPolicy: "skip",
|
|
188
|
+
entryFilter: async (entry) => {
|
|
189
|
+
if (
|
|
190
|
+
entry.kind === "directory" &&
|
|
191
|
+
await capability.exists(`${entry.relativePath}/SKILL.md`)
|
|
192
|
+
) {
|
|
193
|
+
return "skip-subtree";
|
|
194
|
+
}
|
|
195
|
+
return "include";
|
|
196
|
+
},
|
|
197
|
+
})) {
|
|
198
|
+
consume(entry);
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
Filters run serially outside metadata batches and retain the supplied options
|
|
203
|
+
object as their `this` receiver. When an awaited filter resolves, the walk
|
|
204
|
+
checks cancellation and revalidates the current listing directory and Root
|
|
205
|
+
identities before using the decision. These checks do not refresh the entry's
|
|
206
|
+
captured metadata or pin a later operation.
|
|
207
|
+
|
|
208
|
+
Cancellation and iterator disposal wait for a pending filter to settle. The
|
|
209
|
+
walk does not race the callback against the abort signal or close its directory
|
|
210
|
+
while the callback is running; callbacks must settle their own work for
|
|
211
|
+
cancellation to finish. Callback throws and promise rejections reject the walk
|
|
212
|
+
through its normal cleanup path, even with `onDirectoryError: "skip-and-report"`.
|
|
213
|
+
|
|
150
214
|
`onDirectoryError` defaults to `"throw"`, preserving the original fail-fast
|
|
151
215
|
contract. `"skip-and-report"` yields a discriminated
|
|
152
216
|
`{ kind: "directory-error", relativePath, size: 0, error }` marker for a
|
|
153
217
|
directory that cannot be resolved or listed, then continues with its siblings.
|
|
218
|
+
This policy also applies when the directory or Root identity recheck after an
|
|
219
|
+
awaited filter fails; callback failures themselves are not directory errors.
|
|
154
220
|
Every examined directory entry consumes `maxEntries` before filtering, so
|
|
155
221
|
`"skip"` cannot turn the iterator into an unbounded traversal. Reporting and
|
|
156
222
|
`"truncated"` markers describe already-reached state and do not authorize
|
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.
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
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.
|
|
@@ -154,6 +162,41 @@ try {
|
|
|
154
162
|
}
|
|
155
163
|
```
|
|
156
164
|
|
|
165
|
+
### Atomic buffered creation
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
await fs.create("config/seed.json", initial, { atomic: true });
|
|
169
|
+
await fs.createJson("config/settings.json", { enabled: true }, { atomic: true });
|
|
170
|
+
await fs.create("config/flushed.json", initial, { atomic: true, durable: "file" });
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`atomic: true` keeps the destination absent until all bytes have been written.
|
|
174
|
+
The native backend uses its no-replace rename; the JavaScript fallback hardlinks
|
|
175
|
+
the completed stage and unlinks its temporary name in the same JavaScript turn.
|
|
176
|
+
The fallback requires hardlink support and fails without publishing partial bytes
|
|
177
|
+
when that mechanism is unavailable. Other processes can briefly observe both
|
|
178
|
+
names. Existing and raced entries are preserved, including dangling symlinks;
|
|
179
|
+
ordinary confinement, type, hardlink, and symlink-policy rejections still apply.
|
|
180
|
+
`assertBeforeMutation` retains its live checks through content writes and publication.
|
|
181
|
+
|
|
182
|
+
Omitted or `false` preserves the existing buffered behavior. The option belongs
|
|
183
|
+
to buffered `create` and `createJson`, not replacement writes or Root defaults.
|
|
184
|
+
Streamed creation has no atomic opt-out. `atomic` changes visibility, not the
|
|
185
|
+
existing `durable` file/directory synchronization policy; it does not turn
|
|
186
|
+
best-effort synchronization into a strict crash-durability guarantee or strengthen
|
|
187
|
+
JavaScript pathname containment.
|
|
188
|
+
|
|
189
|
+
Atomic and streamed creates settle owned cleanup and close operations before
|
|
190
|
+
returning. Failed or unverifiable cleanup is reported rather than silently
|
|
191
|
+
discarded. Errors after publication and incomplete-settlement errors carry the
|
|
192
|
+
existing `StagedFileFailureDetails` publication/cleanup receipts where the writer
|
|
193
|
+
can establish them; native disposal can retain them inside a `SuppressedError`
|
|
194
|
+
cause. Preserve those details when handling errors: a rejection can follow
|
|
195
|
+
complete publication, and an indeterminate link or native rename must preserve names for
|
|
196
|
+
recovery. A cleanup or close failure also retains the original operation failure.
|
|
197
|
+
No later verification, mode, or synchronization failure authorizes deleting an
|
|
198
|
+
already published complete destination. See [receipt meanings](staged-file.md).
|
|
199
|
+
|
|
157
200
|
### Streamed creation
|
|
158
201
|
|
|
159
202
|
Pass an `AsyncIterable<Uint8Array>` to `create()` when bytes come from a database,
|
|
@@ -202,6 +245,12 @@ forcibly interrupted, so cancellation waits for its pending work and cleanup to
|
|
|
202
245
|
settle. Do not mutate a yielded chunk until the next pull. Producer errors retain
|
|
203
246
|
their original value when cleanup succeeds.
|
|
204
247
|
|
|
248
|
+
Streamed creation retains the `signal` and `assertBeforeMutation` callback
|
|
249
|
+
selected when the call starts. Replacing or deleting those options during a
|
|
250
|
+
producer wait does not change the in-flight operation. Abort the original signal
|
|
251
|
+
or update the live authority state checked by the original callback to revoke
|
|
252
|
+
it; the callback continues to receive the original options object as `this`.
|
|
253
|
+
|
|
205
254
|
An aborted or failed operation can leave created parent directories. If a
|
|
206
255
|
stage's identity or parent cannot be verified during cleanup, the existing
|
|
207
256
|
guarded cleanup preserves it. After publication, later verification or cleanup
|
|
@@ -268,6 +317,10 @@ await fs.move("incoming/foo.txt", "archive/foo.txt", { overwrite: true });
|
|
|
268
317
|
|
|
269
318
|
Both `from` and `to` are bounded; `..` in either is rejected.
|
|
270
319
|
|
|
320
|
+
Mutation policy is captured at call start; changes to caller-owned denial arrays
|
|
321
|
+
apply to later moves. For live cancellation or revocation, throw from
|
|
322
|
+
`assertBeforeMutation` immediately before dispatch.
|
|
323
|
+
|
|
271
324
|
The default no-clobber mode requires the native helper. It admits both parent
|
|
272
325
|
directory descriptors and performs a descriptor-relative no-replace rename, so
|
|
273
326
|
a competitor that creates the target first is preserved and the source remains
|
|
@@ -407,6 +460,19 @@ an atomic check-and-delete syscall. Use OS isolation for that threat model.
|
|
|
407
460
|
await fs.mkdir("snapshots/2026/05");
|
|
408
461
|
```
|
|
409
462
|
|
|
463
|
+
Pass `{ private: true }` to create missing components with private permissions.
|
|
464
|
+
An existing requested directory must already satisfy that policy; fs-safe does
|
|
465
|
+
not repair it or change existing ancestor permissions. Concurrent creators may
|
|
466
|
+
reuse the winner only after it passes the same checks.
|
|
467
|
+
|
|
468
|
+
Buffered, streamed, and JSON `create` calls also accept `private: true`.
|
|
469
|
+
New POSIX directories default to `0700` and files to `0600`; conflicting
|
|
470
|
+
group/world or privilege bits are rejected before creation. Restrictive
|
|
471
|
+
owner-only file modes remain available through `mode`. On Windows, creation
|
|
472
|
+
uses protected ACLs rather than interpreting POSIX mode bits as access rules.
|
|
473
|
+
This does not change `create`'s no-overwrite behavior or select its durability
|
|
474
|
+
policy. See [creation](creation.md) for supported backends and owned descriptors.
|
|
475
|
+
|
|
410
476
|
### `fs.ensureRoot()`
|
|
411
477
|
|
|
412
478
|
Treats `""` / `"."` as the root itself. Useful when a generic helper computes a relative directory and might end up at the root.
|
|
@@ -501,7 +567,11 @@ for (const file of files) await fs.write(`${stagingDir}/${file.name}`, file.body
|
|
|
501
567
|
await fs.move(stagingDir, "snapshots/2026-05-05", { overwrite: true });
|
|
502
568
|
```
|
|
503
569
|
|
|
504
|
-
For
|
|
570
|
+
For guarded whole-directory publication, use
|
|
571
|
+
[`replaceDirectoryAtomic`](atomic.md#replacedirectoryatomic). Replacing an
|
|
572
|
+
existing target is a two-rename protocol with a temporary target-absence
|
|
573
|
+
interval and conditional no-replace rollback, not a transactional
|
|
574
|
+
commit-or-rollback.
|
|
505
575
|
|
|
506
576
|
### Rotate logs
|
|
507
577
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/fs-safe",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.16.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.
|
|
171
|
-
"@openclaw/fs-safe-darwin-x64": "0.
|
|
172
|
-
"@openclaw/fs-safe-linux-arm64-gnu": "0.
|
|
173
|
-
"@openclaw/fs-safe-linux-arm64-musl": "0.
|
|
174
|
-
"@openclaw/fs-safe-linux-x64-gnu": "0.
|
|
175
|
-
"@openclaw/fs-safe-linux-x64-musl": "0.
|
|
176
|
-
"@openclaw/fs-safe-win32-x64-msvc": "0.
|
|
173
|
+
"@openclaw/fs-safe-darwin-arm64": "0.16.0",
|
|
174
|
+
"@openclaw/fs-safe-darwin-x64": "0.16.0",
|
|
175
|
+
"@openclaw/fs-safe-linux-arm64-gnu": "0.16.0",
|
|
176
|
+
"@openclaw/fs-safe-linux-arm64-musl": "0.16.0",
|
|
177
|
+
"@openclaw/fs-safe-linux-x64-gnu": "0.16.0",
|
|
178
|
+
"@openclaw/fs-safe-linux-x64-musl": "0.16.0",
|
|
179
|
+
"@openclaw/fs-safe-win32-x64-msvc": "0.16.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.
|
|
182
|
-
"@types/node": "^26.
|
|
183
|
-
"@vitest/coverage-v8": "5.0.
|
|
184
|
-
"fast-check": "^4.
|
|
184
|
+
"@napi-rs/cli": "3.10.3",
|
|
185
|
+
"@types/node": "^26.6.1",
|
|
186
|
+
"@vitest/coverage-v8": "5.0.1",
|
|
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.
|
|
195
|
+
"vitest": "^5.0.1"
|
|
193
196
|
},
|
|
194
197
|
"engines": {
|
|
195
198
|
"node": ">=22"
|
|
196
199
|
},
|
|
197
|
-
"packageManager": "pnpm@
|
|
200
|
+
"packageManager": "pnpm@12.4.2"
|
|
198
201
|
}
|
package/dist/darwin-acl.d.ts
DELETED
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import type { NativeDarwinAclFacts } from "./native-binding.js";
|
|
2
|
-
/** Internal descriptor facts only; callers own identity, lifetime, and ACL policy. */
|
|
3
|
-
export declare function inspectDarwinAcl(fd: number): NativeDarwinAclFacts;
|
|
4
|
-
//# sourceMappingURL=darwin-acl.d.ts.map
|
package/dist/darwin-acl.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"darwin-acl.d.ts","sourceRoot":"","sources":["../src/darwin-acl.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAGhE,sFAAsF;AACtF,wBAAgB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,oBAAoB,CAmBjE"}
|
package/dist/darwin-acl.js
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { FsSafeError } from "./errors.js";
|
|
2
|
-
import { getNativeBinding } from "./native.js";
|
|
3
|
-
/** Internal descriptor facts only; callers own identity, lifetime, and ACL policy. */
|
|
4
|
-
export function inspectDarwinAcl(fd) {
|
|
5
|
-
if (!Number.isInteger(fd) || fd < 0 || fd > 0x7fff_ffff) {
|
|
6
|
-
throw new FsSafeError("permission-unverified", "Darwin ACL inspection requires a valid descriptor");
|
|
7
|
-
}
|
|
8
|
-
const native = getNativeBinding();
|
|
9
|
-
if (typeof native?.inspectDarwinAcl !== "function") {
|
|
10
|
-
throw new FsSafeError("helper-unavailable", "Darwin ACL inspection requires the matching native capability");
|
|
11
|
-
}
|
|
12
|
-
let facts;
|
|
13
|
-
try {
|
|
14
|
-
facts = native.inspectDarwinAcl(fd);
|
|
15
|
-
}
|
|
16
|
-
catch (cause) {
|
|
17
|
-
throw new FsSafeError("permission-unverified", "Darwin descriptor ACL could not be inspected", { cause });
|
|
18
|
-
}
|
|
19
|
-
const state = facts && typeof facts === "object" && "state" in facts ? facts.state : undefined;
|
|
20
|
-
if (state !== "absent" && state !== "empty" && state !== "present") {
|
|
21
|
-
throw new FsSafeError("permission-unverified", "Darwin ACL inspection returned invalid facts");
|
|
22
|
-
}
|
|
23
|
-
return { state };
|
|
24
|
-
}
|