@openclaw/fs-safe 0.16.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.
- package/CHANGELOG.md +48 -0
- package/README.md +8 -1
- package/dist/absolute-path.d.ts.map +1 -1
- package/dist/absolute-path.js +2 -8
- package/dist/advanced.d.ts +2 -0
- package/dist/advanced.d.ts.map +1 -1
- package/dist/advanced.js +2 -0
- package/dist/archive-deadline.d.ts.map +1 -1
- package/dist/archive-deadline.js +22 -23
- package/dist/archive-merge.d.ts +1 -0
- package/dist/archive-merge.d.ts.map +1 -1
- package/dist/archive-merge.js +4 -4
- package/dist/archive-native.d.ts +1 -0
- package/dist/archive-native.d.ts.map +1 -1
- package/dist/archive-native.js +1 -0
- package/dist/archive-options.d.ts +2 -0
- package/dist/archive-options.d.ts.map +1 -1
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-zip-count.d.ts.map +1 -1
- package/dist/archive-zip-count.js +21 -1
- package/dist/archive-zip-directory.d.ts.map +1 -1
- package/dist/archive-zip-directory.js +23 -1
- package/dist/archive-zip-loader.d.ts +2 -0
- package/dist/archive-zip-loader.d.ts.map +1 -1
- package/dist/archive-zip-loader.js +7 -0
- package/dist/archive-zip-names.d.ts.map +1 -1
- package/dist/archive-zip-names.js +7 -2
- package/dist/archive.d.ts.map +1 -1
- package/dist/archive.js +9 -3
- package/dist/byte-view.d.ts +3 -0
- package/dist/byte-view.d.ts.map +1 -0
- package/dist/byte-view.js +13 -0
- package/dist/creation-darwin.d.ts +0 -1
- package/dist/creation-darwin.d.ts.map +1 -1
- package/dist/creation-darwin.js +0 -9
- package/dist/directory-durability.d.ts +6 -6
- package/dist/directory-durability.d.ts.map +1 -1
- package/dist/directory-receipt.d.ts +2 -2
- package/dist/directory-receipt.d.ts.map +1 -1
- package/dist/directory-receipt.js +15 -19
- package/dist/file-cleanup.d.ts +1 -0
- package/dist/file-cleanup.d.ts.map +1 -1
- package/dist/file-cleanup.js +7 -4
- package/dist/file-contents.d.ts +6 -0
- package/dist/file-contents.d.ts.map +1 -0
- package/dist/file-contents.js +40 -0
- package/dist/file-hash.d.ts.map +1 -1
- package/dist/file-hash.js +16 -4
- package/dist/file-lock-sync-root-acquire.d.ts.map +1 -1
- package/dist/file-lock-sync-root-acquire.js +3 -0
- package/dist/file-lock-sync-root-held.d.ts +1 -2
- package/dist/file-lock-sync-root-held.d.ts.map +1 -1
- package/dist/file-lock-sync-root-held.js +7 -5
- package/dist/file-lock-sync-stale-admission.d.ts.map +1 -1
- package/dist/file-lock-sync-stale-admission.js +3 -0
- package/dist/file-lock-sync.d.ts.map +1 -1
- package/dist/file-lock-sync.js +8 -11
- package/dist/file-store.js +3 -3
- package/dist/guarded-mkdir.d.ts.map +1 -1
- package/dist/guarded-mkdir.js +6 -27
- package/dist/install-path.d.ts.map +1 -1
- package/dist/install-path.js +2 -5
- package/dist/json-durable-queue-ownership.d.ts.map +1 -1
- package/dist/json-durable-queue-ownership.js +2 -6
- package/dist/json-durable-queue-paths.d.ts.map +1 -1
- package/dist/json-durable-queue-paths.js +2 -24
- package/dist/json-durable-queue.d.ts.map +1 -1
- package/dist/json-durable-queue.js +10 -9
- package/dist/json.d.ts.map +1 -1
- package/dist/json.js +32 -75
- package/dist/local-roots.d.ts.map +1 -1
- package/dist/local-roots.js +19 -21
- package/dist/move-path-cleanup.d.ts +5 -19
- package/dist/move-path-cleanup.d.ts.map +1 -1
- package/dist/move-path-cleanup.js +57 -21
- package/dist/move-path.d.ts.map +1 -1
- package/dist/move-path.js +62 -39
- package/dist/native-staged-file.d.ts +2 -1
- package/dist/native-staged-file.d.ts.map +1 -1
- package/dist/native-staged-file.js +8 -5
- package/dist/native.js +2 -2
- package/dist/opened-realpath.d.ts.map +1 -1
- package/dist/opened-realpath.js +11 -2
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +2 -1
- package/dist/permissions.d.ts.map +1 -1
- package/dist/permissions.js +3 -17
- package/dist/pinned-write-input.d.ts.map +1 -1
- package/dist/pinned-write-input.js +11 -1
- package/dist/pinned-write-mode.d.ts +3 -3
- package/dist/pinned-write-mode.d.ts.map +1 -1
- package/dist/pinned-write-mode.js +15 -8
- package/dist/pinned-write-staged.d.ts.map +1 -1
- package/dist/pinned-write-staged.js +10 -11
- package/dist/pinned-write.d.ts.map +1 -1
- package/dist/pinned-write.js +17 -13
- package/dist/publish-file.d.ts +2 -2
- package/dist/publish-file.d.ts.map +1 -1
- package/dist/publish-file.js +56 -96
- package/dist/regular-file.d.ts.map +1 -1
- package/dist/regular-file.js +35 -44
- package/dist/replace-file-copy-fallback.d.ts.map +1 -1
- package/dist/replace-file-copy-fallback.js +28 -26
- package/dist/replace-file-copy-source.d.ts.map +1 -1
- package/dist/replace-file-copy-source.js +13 -22
- package/dist/replace-file-descriptor.d.ts.map +1 -1
- package/dist/replace-file-descriptor.js +10 -16
- package/dist/replace-file-temp-owner.js +6 -6
- package/dist/replace-file.d.ts.map +1 -1
- package/dist/replace-file.js +9 -13
- package/dist/root-directory-list.d.ts.map +1 -1
- package/dist/root-directory-list.js +20 -3
- package/dist/root-file-final-admission.d.ts +1 -1
- package/dist/root-file-final-admission.d.ts.map +1 -1
- package/dist/root-file-final-admission.js +5 -2
- package/dist/root-file.d.ts.map +1 -1
- package/dist/root-file.js +3 -2
- package/dist/root-impl.d.ts.map +1 -1
- package/dist/root-impl.js +50 -20
- package/dist/root-move-noreplace.d.ts +2 -0
- package/dist/root-move-noreplace.d.ts.map +1 -1
- package/dist/root-move-noreplace.js +2 -2
- package/dist/root-read-admission.d.ts.map +1 -1
- package/dist/root-read-admission.js +7 -2
- package/dist/root-remove.d.ts.map +1 -1
- package/dist/root-remove.js +15 -1
- package/dist/sidecar-lock-acquire.d.ts.map +1 -1
- package/dist/sidecar-lock-acquire.js +4 -6
- package/dist/sidecar-lock-handle.d.ts +3 -0
- package/dist/sidecar-lock-handle.d.ts.map +1 -1
- package/dist/sidecar-lock-handle.js +6 -0
- package/dist/sidecar-lock-reclaim.d.ts +1 -1
- package/dist/sidecar-lock-reclaim.d.ts.map +1 -1
- package/dist/sidecar-lock-reclaim.js +11 -8
- package/dist/sidecar-lock.d.ts.map +1 -1
- package/dist/sidecar-lock.js +3 -5
- package/dist/staged-directory.d.ts +2 -2
- package/dist/staged-directory.d.ts.map +1 -1
- package/dist/strict-file-identity.d.ts +1 -1
- package/dist/strict-file-identity.d.ts.map +1 -1
- package/dist/strict-file-identity.js +9 -9
- package/dist/symlink-parents.d.ts.map +1 -1
- package/dist/symlink-parents.js +2 -27
- package/dist/temp-workspace-owner.js +4 -4
- package/dist/unicode-path.d.ts.map +1 -1
- package/dist/unicode-path.js +3 -0
- package/dist/walk.d.ts.map +1 -1
- package/dist/walk.js +4 -2
- package/dist/write-file-handle.d.ts +7 -0
- package/dist/write-file-handle.d.ts.map +1 -1
- package/dist/write-file-handle.js +23 -0
- package/dist/write-open-flags.d.ts.map +1 -1
- package/dist/write-open-flags.js +1 -8
- package/dist/write-queue.d.ts.map +1 -1
- package/dist/write-queue.js +1 -4
- package/docs/advanced.md +68 -1
- package/docs/archive.md +41 -2
- package/docs/atomic.md +29 -5
- package/docs/contributing.md +4 -0
- package/docs/creation.md +8 -4
- package/docs/durability.md +35 -0
- package/docs/file-contents.md +68 -0
- package/docs/json.md +5 -4
- package/docs/local-roots.md +2 -0
- package/docs/mutation-policy-proof.md +5 -3
- package/docs/native.md +9 -8
- package/docs/path.md +4 -4
- package/docs/public-api.md +5 -0
- package/docs/quickstart.md +1 -1
- package/docs/reading.md +2 -2
- package/docs/regular-file.md +3 -0
- package/docs/root.md +9 -0
- package/docs/sidecar-lock.md +9 -1
- package/docs/staged-file.md +4 -3
- package/docs/store.md +3 -1
- package/docs/temp.md +4 -1
- package/docs/types.md +18 -2
- package/docs/walk.md +7 -0
- package/docs/writing.md +9 -2
- package/package.json +8 -8
package/docs/root.md
CHANGED
|
@@ -167,6 +167,8 @@ cleanup, and filesystem requirements.
|
|
|
167
167
|
`append` accepts `prependNewlineIfNeeded: true` to separate text from existing
|
|
168
168
|
content when neither side supplies a newline. String data uses its `encoding`
|
|
169
169
|
for the newline check, including UTF-16LE; Buffer data uses a single LF byte.
|
|
170
|
+
Empty strings and Buffers add no separator; an empty append still creates a
|
|
171
|
+
missing file.
|
|
170
172
|
|
|
171
173
|
These five methods and `copyIn` also accept `durable?: boolean`: the per-call value overrides
|
|
172
174
|
`Root.defaults.durable`, which defaults to `true` when omitted. An explicitly
|
|
@@ -308,6 +310,13 @@ from that dispatch. A thrown value rejects the operation unchanged; an async
|
|
|
308
310
|
or thenable-returning callback rejects with `TypeError` before that mutation.
|
|
309
311
|
Synchronous return values are ignored. Callbacks can run multiple times and
|
|
310
312
|
must inspect current authority each time.
|
|
313
|
+
Directory creation rechecks the retained parent after the callback and before
|
|
314
|
+
submitting mkdir, so a replacement is rejected before creating that component.
|
|
315
|
+
Overwrite moves recheck the retained root, parents, source identity and both
|
|
316
|
+
routes after the callback, including destination parents that were missing
|
|
317
|
+
during preparation. Removals recheck cancellation, retained ancestry and exact
|
|
318
|
+
leaf identity before dispatch; `force` tolerates a missing leaf, not replaced
|
|
319
|
+
ancestry. Removing an admitted hardlink still leaves its other names intact.
|
|
311
320
|
|
|
312
321
|
Already dispatched I/O cannot be revoked. Identity-checked cleanup, final
|
|
313
322
|
permissions, and durability finish under the existing operation owner even
|
package/docs/sidecar-lock.md
CHANGED
|
@@ -55,6 +55,7 @@ invocation is not mutation authority. A failing final parser keeps its error
|
|
|
55
55
|
even if guard ownership has also changed.
|
|
56
56
|
`manager.reset()` invalidates admission bookkeeping but preserves a pending
|
|
57
57
|
Root guard; let its original attempt settle before retrying that guarded path.
|
|
58
|
+
It stops compromise monitoring for forgotten holders, including callbacks from checks already in flight.
|
|
58
59
|
|
|
59
60
|
Always release locks in a `finally` block. Application-managed graceful shutdown can await `release()` or `manager.drain()` before terminating. Explicit `process.exit()`, uncaught failures, crashes, default signal handling, and fatal termination (including `SIGKILL`) do not reliably run asynchronous Root cleanup and may leave sidecars. Recover only after an application-owned liveness policy proves the holder cannot still be writing.
|
|
60
61
|
|
|
@@ -64,7 +65,7 @@ Each new sidecar also carries an internal random ownership token encoded as JSON
|
|
|
64
65
|
|
|
65
66
|
The raw sidecar bytes are not a canonical JSON representation: tools that trim or rewrite the trailing whitespace invalidate the ownership token, so release leaves the changed sidecar in place and fails closed. The token distinguishes cooperating acquisitions; it is not a secret and does not make pathname compare-and-remove atomic against a hostile process that can replace files outside the lock protocol.
|
|
66
67
|
|
|
67
|
-
`release()` propagates an I/O failure that prevents deletion of an unchanged, owned sidecar; it never reports successful cleanup while leaving that lock behind. The handle and manager retain the exact cleanup receipt after a failure, so the same handle can retry `release()` and `manager.drain()` can retry retained cleanup. A changed sidecar remains an ownership mismatch rather than a deletion failure and is left untouched. If both a `withFileLock()` callback and release fail, the release error is the primary `SuppressedError.error` and the callback failure remains available as `SuppressedError.suppressed`. Failed asynchronous acquisition cleanup uses the same shape, with the cleanup error primary and the acquisition failure suppressed. On Node runtimes without the global `SuppressedError` constructor, fs-safe returns the equivalent `Error` shape with the same name and properties.
|
|
68
|
+
`release()` propagates an I/O failure that prevents deletion of an unchanged, owned sidecar; it never reports successful cleanup while leaving that lock behind. The handle and manager retain the exact cleanup receipt after a failure, so the same handle can retry `release()` and `manager.drain()` can retry retained cleanup. A changed sidecar remains an ownership mismatch rather than a deletion failure and is left untouched. If both a `withFileLock()` or `withFileLockSync()` callback and release fail, the release error is the primary `SuppressedError.error` and the callback failure remains available as `SuppressedError.suppressed`. Failed asynchronous acquisition cleanup uses the same shape, with the cleanup error primary and the acquisition failure suppressed. On Node runtimes without the global `SuppressedError` constructor, fs-safe returns the equivalent `Error` shape with the same name and properties.
|
|
68
69
|
|
|
69
70
|
## API
|
|
70
71
|
|
|
@@ -336,6 +337,12 @@ sidecar no longer matches or after a verification I/O failure. This is
|
|
|
336
337
|
detection, not revocation of work already in progress. Asynchronous checks are
|
|
337
338
|
serialized, so a slow verification never overlaps the next timer tick.
|
|
338
339
|
|
|
340
|
+
Ownership-only checks compare serialized bytes, tokens, and file identities
|
|
341
|
+
without decoding an unused default JSON payload. Stale-policy reads still
|
|
342
|
+
decode the payload. Explicit `parsePayload` callbacks keep their existing
|
|
343
|
+
verification and asynchronous-cleanup calls, receivers, and errors;
|
|
344
|
+
synchronous release continues without invoking a custom parser.
|
|
345
|
+
|
|
339
346
|
The compromise-check interval is validated before payload evaluation or
|
|
340
347
|
filesystem acquisition. Omit it or pass `0` to disable monitoring; enabled
|
|
341
348
|
intervals must be finite and between 1 and 2,147,483,647 milliseconds. Values
|
|
@@ -438,6 +445,7 @@ error.
|
|
|
438
445
|
The sync payload, reclaim, and parsing callbacks must also be synchronous. This
|
|
439
446
|
shape is appropriate for a short boot migration; it is a poor fit for a server
|
|
440
447
|
request because retry backoff uses a blocking wait.
|
|
448
|
+
Synchronous `shouldReclaim` and `shouldRemoveStaleLock` reject Promise or thenable results with `TypeError` before deleting the observed sidecar; an asynchronous result is never approval.
|
|
441
449
|
|
|
442
450
|
If termination skips the relevant cleanup handler or cleanup fails, the sidecar remains. In particular, `process.exit()` skips asynchronous Root cleanup; await explicit release or drain during application-managed graceful shutdown. Once `staleMs` elapses (or your `shouldReclaim` returns true), acquisition fails closed by default instead of deleting by path.
|
|
443
451
|
|
package/docs/staged-file.md
CHANGED
|
@@ -49,7 +49,7 @@ remain with the caller.
|
|
|
49
49
|
|
|
50
50
|
```ts
|
|
51
51
|
function stageFileInDirectory(options: {
|
|
52
|
-
directory: string | DirectoryReceipt
|
|
52
|
+
directory: string | DirectoryReceipt<Stats | BigIntStats>;
|
|
53
53
|
content: string | Uint8Array;
|
|
54
54
|
mode?: number;
|
|
55
55
|
}): Promise<StagedFile>;
|
|
@@ -81,8 +81,9 @@ Creation uses an exclusive, no-follow, close-on-exec open of a generated direct
|
|
|
81
81
|
child name. Writes use that descriptor. Inspection uses non-following metadata
|
|
82
82
|
operations, never a potentially blocking reopen of the leaf.
|
|
83
83
|
|
|
84
|
-
A supplied directory receipt must still match at admission.
|
|
85
|
-
|
|
84
|
+
A supplied directory receipt must still match at admission. Caller receipts can
|
|
85
|
+
carry numeric `Stats` or exact `BigIntStats`; untracked numeric identities must
|
|
86
|
+
be exactly representable. Ambiguous identity fails closed.
|
|
86
87
|
Returned receipts are frozen descriptive snapshots, not mutable authority.
|
|
87
88
|
Changing a supplied receipt after admission cannot retarget the lifecycle.
|
|
88
89
|
|
package/docs/store.md
CHANGED
|
@@ -89,7 +89,9 @@ claim before publication. If another consumer acknowledged, quarantined, or
|
|
|
89
89
|
replaced that claim, the migration rejects with `FsSafeError("path-mismatch")`
|
|
90
90
|
and leaves the newer generation or failed evidence intact. A stale migration
|
|
91
91
|
rejects both single and batch loads; ordinary callback failures retain their
|
|
92
|
-
existing single-load rejection and batch-skip behavior.
|
|
92
|
+
existing single-load rejection and batch-skip behavior. A caller or migration
|
|
93
|
+
error with code `ENOENT` is still a failure, not a missing queue entry; only a
|
|
94
|
+
claim that is absent or disappears before reading returns `null` from a single load.
|
|
93
95
|
|
|
94
96
|
On Windows, migration releases its read pin once at this publication boundary
|
|
95
97
|
because an open target can block replacement. It rechecks the exact pathname
|
package/docs/temp.md
CHANGED
|
@@ -155,12 +155,15 @@ uses guarded pathname-recursive removal. This fallback never recursively
|
|
|
155
155
|
removes the public workspace name, but it is not atomic conditional deletion: a
|
|
156
156
|
same-privilege peer that discovers and replaces the private quarantine after
|
|
157
157
|
verification can still redirect the final pathname removal.
|
|
158
|
+
If admitting a cleanup parent fails and closing its descriptor also fails,
|
|
159
|
+
creation rejects with both failures in an `AggregateError`. This does not select
|
|
160
|
+
compatible fallback or retry the indeterminate descriptor close.
|
|
158
161
|
|
|
159
162
|
Set `cleanupSafety: "require-bounded"` when that concurrent attacker is in scope.
|
|
160
163
|
Creation then requires native no-replace directory rename, native owned-tree
|
|
161
164
|
removal, and a readable retained parent descriptor **before** child creation.
|
|
162
165
|
On POSIX, the final requested `dirMode` must also include owner read
|
|
163
|
-
and search (`(dirMode & 0o500) === 0o500`).
|
|
166
|
+
and search (`(dirMode & 0o500) === 0o500`). An unavailable capability throws
|
|
164
167
|
`FsSafeError("helper-unavailable")` without creating a child or calling a scoped
|
|
165
168
|
callback. The child descriptor is opened
|
|
166
169
|
while the new directory still has its private creation mode, before an explicit
|
package/docs/types.md
CHANGED
|
@@ -128,6 +128,8 @@ type RootOptions = {
|
|
|
128
128
|
## `RootReadOptions` / `RootWriteOptions` / `RootCopyOptions`
|
|
129
129
|
|
|
130
130
|
```ts
|
|
131
|
+
import type { CopyCloneMode, RootCopyPublicationReceipt } from "@openclaw/fs-safe";
|
|
132
|
+
|
|
131
133
|
type RootReadOptions = Pick<RootDefaults, "hardlinks" | "maxBytes" | "nonBlockingRead" | "symlinks">;
|
|
132
134
|
type RootWriteOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "durable" | "mkdir" | "mode" | "renameIdentity" | "mutationSymlinks"> & {
|
|
133
135
|
encoding?: BufferEncoding;
|
|
@@ -135,6 +137,11 @@ type RootWriteOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutatio
|
|
|
135
137
|
};
|
|
136
138
|
type RootCopyOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "durable" | "maxBytes" | "mkdir" | "mode" | "mutationSymlinks"> & {
|
|
137
139
|
sourceHardlinks?: "reject" | "allow";
|
|
140
|
+
overwrite?: boolean;
|
|
141
|
+
clone?: CopyCloneMode;
|
|
142
|
+
signal?: AbortSignal;
|
|
143
|
+
preserveSourceMode?: boolean;
|
|
144
|
+
onDestinationPublished?: (receipt: RootCopyPublicationReceipt) => void;
|
|
138
145
|
};
|
|
139
146
|
type RootOpenWritableOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "mkdir" | "mode" | "mutationSymlinks"> & {
|
|
140
147
|
writeMode?: "replace" | "append" | "update";
|
|
@@ -150,8 +157,17 @@ type RootAppendOptions = RootWriteOptions & {
|
|
|
150
157
|
type RootMoveOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "mutationSymlinks"> & {
|
|
151
158
|
overwrite?: boolean;
|
|
152
159
|
};
|
|
153
|
-
type RootRemoveOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "mutationSymlinks"
|
|
154
|
-
|
|
160
|
+
type RootRemoveOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "mutationSymlinks"> & {
|
|
161
|
+
recursive?: boolean;
|
|
162
|
+
force?: boolean;
|
|
163
|
+
order?: "filesystem" | "sorted";
|
|
164
|
+
maxEntries?: number;
|
|
165
|
+
maxDepth?: number;
|
|
166
|
+
signal?: AbortSignal;
|
|
167
|
+
};
|
|
168
|
+
type RootMkdirOptions = Pick<RootDefaults, "assertBeforeMutation" | "denyMutations" | "mutationSymlinks"> & {
|
|
169
|
+
private?: boolean;
|
|
170
|
+
};
|
|
155
171
|
```
|
|
156
172
|
|
|
157
173
|
Per-method option shapes. Each picks the `RootDefaults` keys that apply, plus method-specific extras.
|
package/docs/walk.md
CHANGED
|
@@ -47,6 +47,10 @@ type WalkDirectoryFailure = {
|
|
|
47
47
|
|
|
48
48
|
`depth` starts at `1` for direct children of `rootDir`. `relativePath` is always relative to the supplied root. `scannedEntryCount` counts directory entries examined, including entries filtered out by `include`.
|
|
49
49
|
|
|
50
|
+
Each entry's `path` is absolute and retains the normalized spelling of the
|
|
51
|
+
supplied root, including followed directory aliases. Paths do not switch to
|
|
52
|
+
the canonical symlink target during descent.
|
|
53
|
+
|
|
50
54
|
`walkDirectory()` and `walkDirectorySync()` always return `failedDirs`; the property remains optional on the exported `WalkDirectoryResult` type so existing callers that manually construct the legacy result shape remain source-compatible. It lists every directory whose `realpath`/`readdir` threw, so its contents are absent from `entries`. `error` is the thrown value (a `NodeJS.ErrnoException` at runtime), so callers can distinguish a benign missing-directory race (`ENOENT`) from a real read failure (`EACCES`, `EIO`, `ESTALE`, …). The walk-root failure has an empty `relativePath` and `depth: 0`. Failures resolving a symlink's target kind are not reported here.
|
|
51
55
|
|
|
52
56
|
## Options
|
|
@@ -227,6 +231,9 @@ its exact identity, and rechecks it and the Root identity around each metadata
|
|
|
227
231
|
batch or individual filesystem-order observation. Sorted batches contain no
|
|
228
232
|
await or caller code between their before/after checks. It tracks canonical
|
|
229
233
|
directories to stop symlink cycles.
|
|
234
|
+
Directory rechecks retain exact identities while using ordinary numeric metadata
|
|
235
|
+
when it represents those identities without rounding. Large identities and
|
|
236
|
+
Windows unknown-identity retries keep the bigint inspection path.
|
|
230
237
|
Neither mode holds a descriptor for every path component, so it is not a process sandbox against a hostile peer that
|
|
231
238
|
can continuously swap and restore directories. Each individual lookup retains
|
|
232
239
|
the documented Node `Root` boundary checks.
|
package/docs/writing.md
CHANGED
|
@@ -154,11 +154,17 @@ claimed exclusively first and content is written afterward, so observers can
|
|
|
154
154
|
briefly see an empty file; failure cleanup removes a claimed file only when its
|
|
155
155
|
identity is unchanged.
|
|
156
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
|
+
|
|
157
163
|
```ts
|
|
158
164
|
try {
|
|
159
165
|
await fs.create("config/seed.json", initial);
|
|
160
166
|
} catch (err) {
|
|
161
|
-
if (err instanceof FsSafeError
|
|
167
|
+
if (!(err instanceof FsSafeError) || err.code !== "already-exists") throw err;
|
|
162
168
|
}
|
|
163
169
|
```
|
|
164
170
|
|
|
@@ -201,7 +207,8 @@ already published complete destination. See [receipt meanings](staged-file.md).
|
|
|
201
207
|
|
|
202
208
|
Pass an `AsyncIterable<Uint8Array>` to `create()` when bytes come from a database,
|
|
203
209
|
network response, or another incremental producer. Buffers are accepted chunks.
|
|
204
|
-
The writer
|
|
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
|
|
205
212
|
does not collect the full input in memory or expose a writable descriptor.
|
|
206
213
|
|
|
207
214
|
```ts
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/fs-safe",
|
|
3
|
-
"version": "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",
|
|
@@ -170,13 +170,13 @@
|
|
|
170
170
|
"archive:producer-smoke": "node scripts/archive-producer-smoke.mjs"
|
|
171
171
|
},
|
|
172
172
|
"optionalDependencies": {
|
|
173
|
-
"@openclaw/fs-safe-darwin-arm64": "0.
|
|
174
|
-
"@openclaw/fs-safe-darwin-x64": "0.
|
|
175
|
-
"@openclaw/fs-safe-linux-arm64-gnu": "0.
|
|
176
|
-
"@openclaw/fs-safe-linux-arm64-musl": "0.
|
|
177
|
-
"@openclaw/fs-safe-linux-x64-gnu": "0.
|
|
178
|
-
"@openclaw/fs-safe-linux-x64-musl": "0.
|
|
179
|
-
"@openclaw/fs-safe-win32-x64-msvc": "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",
|
|
180
180
|
"jszip": "^3.10.2"
|
|
181
181
|
},
|
|
182
182
|
"devDependencies": {
|