@openclaw/fs-safe 0.18.0 → 0.18.1
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 +10 -0
- package/dist/advanced.d.ts +2 -1
- package/dist/archive-durability.d.ts +1 -1
- package/dist/archive-parser.wasm +0 -0
- package/dist/archive-plan.d.ts +64 -3
- package/dist/archive-plan.js +55 -1
- package/dist/archive-tar-wasm.d.ts +1 -1
- package/dist/archive-zip-directory.d.ts +1 -1
- package/dist/archive-zip-entry.d.ts +1 -1
- package/dist/archive-zip-entry.js +1 -1
- package/dist/archive.d.ts +8 -5
- package/dist/archive.js +122 -9
- package/dist/atomic.d.ts +3 -1
- package/dist/copy-publication.d.ts +1 -1
- package/dist/create.d.ts +16 -2
- package/dist/create.js +388 -2
- package/dist/creation-boundary.d.ts +60 -0
- package/dist/creation-boundary.js +371 -0
- package/dist/directory-mode-node.js +12 -2
- package/dist/durability.d.ts +2 -1
- package/dist/file-lock-sync-acquisition.js +2 -2
- package/dist/file-lock-sync-root-acquire.js +2 -6
- package/dist/file-lock-sync-root.d.ts +2 -1
- package/dist/file-lock-sync-stale-admission.js +3 -10
- package/dist/file-lock-sync.d.ts +4 -21
- package/dist/file-store-boundary.d.ts +7 -0
- package/dist/file-store-boundary.js +58 -1
- package/dist/file-store-sync-write.js +2 -3
- package/dist/file-store.js +1 -6
- package/dist/guarded-mkdir.d.ts +1 -1
- package/dist/index.d.ts +9 -6
- package/dist/json-durable-queue-read.js +7 -3
- package/dist/json-durable-queue.js +1 -2
- package/dist/json-store.js +1 -2
- package/dist/json.js +1 -2
- package/dist/move-path.js +1 -2
- package/dist/native-operations.js +0 -1
- package/dist/native-pinned-write-windows.d.ts +1 -1
- package/dist/native-pinned-write-windows.js +1 -1
- package/dist/native-pinned-write.d.ts +1 -1
- package/dist/native-pinned-write.js +3 -6
- package/dist/native-policy-parent-windows.d.ts +1 -1
- package/dist/native-staged-file.d.ts +2 -2
- package/dist/native-staged-file.js +1 -2
- package/dist/pinned-mutation-admission.d.ts +1 -1
- package/dist/pinned-open.js +6 -1
- package/dist/pinned-operation.d.ts +1 -1
- package/dist/pinned-operation.js +3 -16
- package/dist/pinned-write-mode.js +1 -1
- package/dist/pinned-write-staged.js +1 -2
- package/dist/pinned-write.d.ts +0 -1
- package/dist/pinned-write.js +3 -5
- package/dist/private-producer-handoff-sync.d.ts +2 -12
- package/dist/private-producer-handoff-sync.js +1 -1
- package/dist/private-producer-handoff.d.ts +3 -3
- package/dist/private-temp-workspace.js +1 -2
- package/dist/publish-copy-stage.d.ts +1 -1
- package/dist/publish-file.d.ts +0 -1
- package/dist/publish-file.js +1 -2
- package/dist/replace-directory.js +1 -2
- package/dist/replace-file-rename-policy.d.ts +1 -2
- package/dist/replace-file.d.ts +3 -3
- package/dist/replace-file.js +1 -1
- package/dist/root-context.js +3 -7
- package/dist/root-create-input.d.ts +1 -1
- package/dist/root-directory-creation.d.ts +1 -1
- package/dist/root-directory-creation.js +1 -1
- package/dist/root-directory-list.js +3 -7
- package/dist/root-entries.js +2 -2
- package/dist/root-errors.d.ts +4 -0
- package/dist/root-errors.js +5 -3
- package/dist/root-impl.d.ts +0 -5
- package/dist/root-impl.js +22 -21
- package/dist/root-move-noreplace.js +4 -10
- package/dist/root-observed-path.js +2 -4
- package/dist/root-options.d.ts +1 -1
- package/dist/root-path-existing.js +2 -3
- package/dist/root-path-stat.js +2 -4
- package/dist/root-public-types.d.ts +11 -0
- package/dist/root-remove-identity.js +2 -4
- package/dist/root-remove.js +2 -4
- package/dist/root-write-admission.d.ts +1 -1
- package/dist/root-write-admission.js +3 -7
- package/dist/root-write-complete-parent.js +2 -3
- package/dist/root-write-verification.d.ts +1 -1
- package/dist/root.d.ts +2 -6
- package/dist/secret-file.js +0 -1
- package/dist/secure-temp-repair.js +5 -1
- package/dist/sidecar-lock-acquire.js +7 -10
- package/dist/sidecar-lock-policy.d.ts +1 -0
- package/dist/sidecar-lock-policy.js +7 -0
- package/dist/sidecar-lock-stale-admission.js +2 -6
- package/dist/sidecar-lock-types.d.ts +22 -16
- package/dist/temp-workspace-admission.js +1 -3
- package/dist/temp-workspace-child-admission.d.ts +16 -2
- package/dist/temp-workspace-child-admission.js +71 -3
- package/dist/temp-workspace-descriptor.d.ts +1 -1
- package/dist/temp-workspace-descriptor.js +1 -2
- package/dist/text-atomic.js +1 -1
- package/dist/windows-owner.js +1 -1
- package/dist/windows-path-alias.d.ts +1 -0
- package/dist/windows-path-alias.js +5 -0
- package/docs/advanced.md +3 -1
- package/docs/secret-file.md +4 -0
- package/docs/sidecar-lock.md +4 -0
- package/docs/store.md +6 -0
- package/docs/temp.md +8 -1
- package/docs/writing.md +5 -0
- package/package.json +8 -8
- package/dist/archive-native.d.ts +0 -10
- package/dist/archive-native.js +0 -73
- package/dist/archive-options.d.ts +0 -30
- package/dist/archive-policy.d.ts +0 -21
- package/dist/archive-policy.js +0 -38
- package/dist/archive-tar-inspect.d.ts +0 -6
- package/dist/archive-tar-inspect.js +0 -62
- package/dist/archive-tar.d.ts +0 -18
- package/dist/archive-tar.js +0 -21
- package/dist/create-directory.d.ts +0 -18
- package/dist/create-directory.js +0 -111
- package/dist/create-file-async.d.ts +0 -6
- package/dist/create-file-async.js +0 -121
- package/dist/create-file.d.ts +0 -7
- package/dist/create-file.js +0 -179
- package/dist/creation-darwin.d.ts +0 -5
- package/dist/creation-darwin.js +0 -70
- package/dist/creation-file-state.d.ts +0 -18
- package/dist/creation-file-state.js +0 -118
- package/dist/creation-path.d.ts +0 -22
- package/dist/creation-path.js +0 -83
- package/dist/creation-permissions.d.ts +0 -18
- package/dist/creation-permissions.js +0 -115
- package/dist/file-store-copy-source.d.ts +0 -4
- package/dist/file-store-copy-source.js +0 -31
- package/dist/file-store-limit.d.ts +0 -1
- package/dist/file-store-limit.js +0 -7
- package/dist/file-store-path.d.ts +0 -2
- package/dist/file-store-path.js +0 -30
- package/dist/standalone-publication-path.d.ts +0 -1
- package/dist/standalone-publication-path.js +0 -6
- package/dist/temp-workspace-identity.d.ts +0 -14
- package/dist/temp-workspace-identity.js +0 -41
- package/dist/temp-workspace-permissions.d.ts +0 -3
- package/dist/temp-workspace-permissions.js +0 -32
- /package/dist/{archive-options.js → root-public-types.js} +0 -0
|
@@ -1,8 +1,76 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import fsSync, {} from "node:fs";
|
|
2
|
+
import { inspectDirectoryIdentitySync, observeDirectoryIdentitySync } from "./directory-guard.js";
|
|
2
3
|
import { pinNodeDirectoryForMode, pinNodeDirectoryForModeSync } from "./directory-mode-node.js";
|
|
3
4
|
import { FsSafeError } from "./errors.js";
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
5
|
+
import { recordFileObservationFailure } from "./file-observation.js";
|
|
6
|
+
import { inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
7
|
+
export const TEMP_WORKSPACE_NUMERIC_IDENTITY_REPLAY = process.platform === "linux" || process.platform === "darwin";
|
|
8
|
+
export function projectTempWorkspaceNumericIdentity(identity) {
|
|
9
|
+
const dev = Number(identity.dev);
|
|
10
|
+
const ino = Number(identity.ino);
|
|
11
|
+
if (!Number.isSafeInteger(dev) || dev < 0 || BigInt(dev) !== identity.dev ||
|
|
12
|
+
!Number.isSafeInteger(ino) || ino < 0 || BigInt(ino) !== identity.ino) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
return Object.freeze({ dev, ino });
|
|
16
|
+
}
|
|
17
|
+
function identityMismatch() {
|
|
18
|
+
const error = new FsSafeError("path-mismatch", "file identity changed or could not be verified");
|
|
19
|
+
recordFileObservationFailure(error, "identity");
|
|
20
|
+
throw error;
|
|
21
|
+
}
|
|
22
|
+
function inspectNumericIdentity(current, expected) {
|
|
23
|
+
if (!Number.isSafeInteger(current.dev) || current.dev < 0 || current.dev !== expected.dev ||
|
|
24
|
+
!Number.isSafeInteger(current.ino) || current.ino < 0 || current.ino !== expected.ino) {
|
|
25
|
+
identityMismatch();
|
|
26
|
+
}
|
|
27
|
+
return current;
|
|
28
|
+
}
|
|
29
|
+
export function inspectTempWorkspaceDescriptorIdentitySync(fd, expected, numeric) {
|
|
30
|
+
// A malformed or mismatched numeric observation is definite and never
|
|
31
|
+
// retried. Unsafe receipts and other platforms retain exact replay.
|
|
32
|
+
if (TEMP_WORKSPACE_NUMERIC_IDENTITY_REPLAY && numeric) {
|
|
33
|
+
return inspectNumericIdentity(fsSync.fstatSync(fd), numeric);
|
|
34
|
+
}
|
|
35
|
+
return inspectFileIdentitySync(() => fsSync.fstatSync(fd, { bigint: true }), expected);
|
|
36
|
+
}
|
|
37
|
+
export function inspectTempWorkspaceDirectoryIdentitySync(dir, expected, numeric) {
|
|
38
|
+
if (TEMP_WORKSPACE_NUMERIC_IDENTITY_REPLAY && numeric) {
|
|
39
|
+
return inspectNumericIdentity(observeDirectoryIdentitySync(dir), numeric);
|
|
40
|
+
}
|
|
41
|
+
return inspectFileIdentitySync(() => observeDirectoryIdentitySync(dir, { bigint: true }), expected);
|
|
42
|
+
}
|
|
43
|
+
export function validateTempWorkspaceDirMode(mode) {
|
|
44
|
+
if (!Number.isInteger(mode) || mode < 0 || mode > 0o7777) {
|
|
45
|
+
throw new FsSafeError("insecure-permissions", "temp workspace dirMode must be permission bits");
|
|
46
|
+
}
|
|
47
|
+
if (process.platform !== "win32" && (mode & 0o022) !== 0) {
|
|
48
|
+
throw new FsSafeError("insecure-permissions", "temp workspace must not be group/world writable");
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
export function assertTrustedTempWorkspaceDirectory(stat, uid, child = false) {
|
|
52
|
+
if (uid === undefined)
|
|
53
|
+
return;
|
|
54
|
+
const ownedByUser = typeof stat.uid === "bigint" ? stat.uid === BigInt(uid) : stat.uid === uid;
|
|
55
|
+
const ownedByRoot = typeof stat.uid === "bigint" ? stat.uid === 0n : stat.uid === 0;
|
|
56
|
+
if (!ownedByUser && (child || !ownedByRoot)) {
|
|
57
|
+
throw new FsSafeError("not-owned", "temp workspace directory has an untrusted owner");
|
|
58
|
+
}
|
|
59
|
+
// A root/current-user-owned sticky directory protects children owned by us,
|
|
60
|
+
// including the usual shared system temp directory. Never chmod that parent.
|
|
61
|
+
const writable = typeof stat.mode === "bigint"
|
|
62
|
+
? (stat.mode & 18n) !== 0n
|
|
63
|
+
: Number.isSafeInteger(stat.mode) && stat.mode >= 0 && (stat.mode & 0o022) !== 0;
|
|
64
|
+
const sticky = typeof stat.mode === "bigint"
|
|
65
|
+
? (stat.mode & 512n) !== 0n
|
|
66
|
+
: Number.isSafeInteger(stat.mode) && stat.mode >= 0 && (stat.mode & 0o1000) !== 0;
|
|
67
|
+
if (typeof stat.mode !== "bigint" && (!Number.isSafeInteger(stat.mode) || stat.mode < 0)) {
|
|
68
|
+
throw new FsSafeError("insecure-permissions", "temp workspace directory permissions are invalid");
|
|
69
|
+
}
|
|
70
|
+
if (writable && (child || !sticky)) {
|
|
71
|
+
throw new FsSafeError("insecure-permissions", "temp workspace directory is group/world writable without sticky protection");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
6
74
|
const WINDOWS = process.platform === "win32";
|
|
7
75
|
export function assertTempWorkspaceChildState(stat, ownerUid) {
|
|
8
76
|
const exactIdentity = typeof stat.dev === "bigint" && typeof stat.ino === "bigint" &&
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import fsSync from "node:fs";
|
|
2
2
|
import type { FileIdentityStat } from "./file-identity.js";
|
|
3
3
|
import type { TempWorkspaceRootAdmission } from "./temp-workspace-admission.js";
|
|
4
|
-
import { type TempWorkspaceIdentityStat } from "./temp-workspace-
|
|
4
|
+
import { type TempWorkspaceIdentityStat } from "./temp-workspace-child-admission.js";
|
|
5
5
|
type DirectoryDescriptorAccess = "read" | "search";
|
|
6
6
|
export type RetainedDirectory = {
|
|
7
7
|
fd: number;
|
|
@@ -5,8 +5,7 @@ import { assertOwnedDirectory } from "./directory-mode-owner.js";
|
|
|
5
5
|
import { FsSafeError } from "./errors.js";
|
|
6
6
|
import { assertNoWindowsPathAlias, pathForWindowsFilesystem, resolvePathPreservingWindowsRoot, } from "./windows-path-alias.js";
|
|
7
7
|
import { inspectFileIdentitySync } from "./strict-file-identity.js";
|
|
8
|
-
import { assertTempWorkspaceChildState, childHasRequestedMode, validateAdmittedTempWorkspaceChild, } from "./temp-workspace-child-admission.js";
|
|
9
|
-
import { inspectTempWorkspaceDescriptorIdentitySync, inspectTempWorkspaceDirectoryIdentitySync, projectTempWorkspaceNumericIdentity, TEMP_WORKSPACE_NUMERIC_IDENTITY_REPLAY, } from "./temp-workspace-identity.js";
|
|
8
|
+
import { assertTempWorkspaceChildState, childHasRequestedMode, validateAdmittedTempWorkspaceChild, inspectTempWorkspaceDescriptorIdentitySync, inspectTempWorkspaceDirectoryIdentitySync, projectTempWorkspaceNumericIdentity, TEMP_WORKSPACE_NUMERIC_IDENTITY_REPLAY, } from "./temp-workspace-child-admission.js";
|
|
10
9
|
function assertRetainedChildDirectory(stat) {
|
|
11
10
|
if (stat.isSymbolicLink() || !stat.isDirectory()) {
|
|
12
11
|
throw new FsSafeError("not-file", "temp workspace child must be a real directory");
|
package/dist/text-atomic.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { replaceFileAtomic } from "./replace-file.js";
|
|
2
|
-
import { admitStandalonePublicationPath } from "./
|
|
2
|
+
import { admitStandalonePublicationPath } from "./windows-path-alias.js";
|
|
3
3
|
export async function writeTextAtomic(filePath, content, options) {
|
|
4
4
|
const admittedPath = admitStandalonePublicationPath(filePath);
|
|
5
5
|
const payload = options?.trailingNewline && !content.endsWith("\n") ? `${content}\n` : content;
|
package/dist/windows-owner.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { formatCaughtPermissionFailure,
|
|
1
|
+
import { formatCaughtPermissionFailure, getPermissionCommandFailure, } from "./permission-exec.js";
|
|
2
2
|
import { resolveWindowsSystemCommand } from "./windows-command.js";
|
|
3
3
|
import { hasWindowsPathAlias } from "./windows-path-alias.js";
|
|
4
4
|
const SID_RE = /^\*?s-\d+-\d+(-\d+)+$/i;
|
|
@@ -36,3 +36,4 @@ export declare function hasWindowsPathAlias(value: string, kind: WindowsPathAlia
|
|
|
36
36
|
export declare function assertNoWindowsPathAliasForPlatform(value: string, kind: WindowsPathAliasKind, message: string, platform: NodeJS.Platform | string | undefined): void;
|
|
37
37
|
export declare function assertNoWindowsPathAlias(value: string, kind?: WindowsPathAliasKind, message?: string, platform?: NodeJS.Platform | string): void;
|
|
38
38
|
export declare function isWindowsPathAliasError(error: unknown): error is FsSafeError;
|
|
39
|
+
export declare function admitStandalonePublicationPath(value: string, message?: string): string;
|
|
@@ -115,3 +115,8 @@ export function assertNoWindowsPathAlias(value, kind = "filesystem", message = "
|
|
|
115
115
|
export function isWindowsPathAliasError(error) {
|
|
116
116
|
return error instanceof FsSafeError && error.details?.reason === "windows-path-alias";
|
|
117
117
|
}
|
|
118
|
+
export function admitStandalonePublicationPath(value, message) {
|
|
119
|
+
const admittedPath = anchorWindowsDriveRelativePath(value);
|
|
120
|
+
assertNoWindowsPathAlias(admittedPath, "filesystem", message);
|
|
121
|
+
return admittedPath;
|
|
122
|
+
}
|
package/docs/advanced.md
CHANGED
|
@@ -106,7 +106,9 @@ before component traversal. Immediately before transferring descriptor ownership
|
|
|
106
106
|
they check that root, freshly canonicalize the consumed pathname, admit the fresh
|
|
107
107
|
spelling under the captured root, compare a no-follow canonical-leaf observation
|
|
108
108
|
with the retained descriptor, and check the root again. Boundary or identity drift
|
|
109
|
-
is a validation failure and
|
|
109
|
+
is a validation failure and triggers one descriptor-close attempt. If cleanup also
|
|
110
|
+
fails, the selected admission failure result is preserved. Successful opens transfer
|
|
111
|
+
descriptor ownership to the caller. A custom `ioFs` supplies
|
|
110
112
|
these observations; the built-in adapter uses fs-safe's native realpath wrapper.
|
|
111
113
|
On Windows, the built-in adapter binds native root spelling before traversal,
|
|
112
114
|
including supplied `rootRealPath`, and returns that spelling in its root receipt.
|
package/docs/secret-file.md
CHANGED
|
@@ -205,6 +205,10 @@ Both mode options must resolve to integers between `0o0000` and `0o7777`; invali
|
|
|
205
205
|
|
|
206
206
|
After this operation wins directory creation, initialization uses a pinned descriptor bound to the admitted identity and effective user, with ancestor checks before chmod. It does not chmod the caller's pathname. Creation and descriptor admission are separate operations, not an atomic create-and-pin guarantee. A raced directory that has not reached its requested mode yet is rejected rather than repaired; callers may retry after its creator finishes initialization.
|
|
207
207
|
|
|
208
|
+
If initial directory-descriptor admission fails, its original identity, ownership,
|
|
209
|
+
or inspection error is preserved even when closing the rejected descriptor fails.
|
|
210
|
+
Close failures after successful admission remain reportable.
|
|
211
|
+
|
|
208
212
|
Initialization fails closed if the platform cannot safely pin a created directory. In particular, a non-root macOS process cannot pin a new `000` directory produced by `umask(0o777)`; the write fails without repairing that directory or writing a secret. Restrictive masks retaining owner search permission remain usable. Linux x64/arm64 can use the guarded `O_PATH`/procfs descriptor route where available. There is no unguarded pathname-chmod fallback, and a failure may leave a created directory for caller-managed cleanup.
|
|
209
213
|
|
|
210
214
|
### `createSecretFileAtomic(params)`
|
package/docs/sidecar-lock.md
CHANGED
|
@@ -199,6 +199,10 @@ explicit `retry.retries` still applies with `timeoutMs: Infinity`, and zero allo
|
|
|
199
199
|
only the initial attempt. After process defaults are applied, an omitted retry
|
|
200
200
|
count means unlimited retries, and an omitted or infinite timeout means no
|
|
201
201
|
deadline. With neither budget bounded, contention can wait indefinitely.
|
|
202
|
+
Finite deadlines use monotonic elapsed time, so system clock corrections do not
|
|
203
|
+
extend or shorten the retry budget. Payload timestamps, `heldEntries().acquiredAt`,
|
|
204
|
+
and stale-policy `nowMs` remain wall-clock based. Deadlines are checked at retry
|
|
205
|
+
boundaries; they do not interrupt callbacks or filesystem operations.
|
|
202
206
|
`parsePayload` replaces JSON parsing for legacy or custom sidecars. Its `unknown`
|
|
203
207
|
result is passed to `shouldReclaim` and `shouldRemoveStaleLock`, allowing PID,
|
|
204
208
|
process-start, argv, or role schemas to remain application-owned.
|
package/docs/store.md
CHANGED
|
@@ -93,6 +93,12 @@ existing single-load rejection and batch-skip behavior. A caller or migration
|
|
|
93
93
|
error with code `ENOENT` is still a failure, not a missing queue entry; only a
|
|
94
94
|
claim that is absent or disappears before reading returns `null` from a single load.
|
|
95
95
|
|
|
96
|
+
If closing the read descriptor also fails, the original read, validation,
|
|
97
|
+
callback, or migration failure keeps precedence, including non-Error rejection
|
|
98
|
+
values. A close failure without an earlier failure is reported through the same
|
|
99
|
+
loader policy: direct reads and single loads reject, while batch loads skip it
|
|
100
|
+
unless migration has started.
|
|
101
|
+
|
|
96
102
|
On Windows, migration releases its read pin once at this publication boundary
|
|
97
103
|
because an open target can block replacement. It rechecks the exact pathname
|
|
98
104
|
identity after the asynchronous close while still holding the transfer lock.
|
package/docs/temp.md
CHANGED
|
@@ -31,6 +31,8 @@ rejects with `permission-unverified`. Existing supplied directories keep their
|
|
|
31
31
|
permissions. Missing root components are created at `0o700` and initialized
|
|
32
32
|
from their first exact security snapshot; if a restrictive umask changes that
|
|
33
33
|
mode, correction uses a verified directory descriptor.
|
|
34
|
+
An initial mode-descriptor admission error is preserved if closing that rejected
|
|
35
|
+
descriptor also fails; close failures after successful admission remain reportable.
|
|
34
36
|
|
|
35
37
|
For an already existing canonical root, discovery retains only its immutable
|
|
36
38
|
exact identity. Cleanup-parent retention is provisional: after any native
|
|
@@ -645,7 +647,12 @@ recursive-`mkdir` winner is inspected as an untrusted existing directory.
|
|
|
645
647
|
Broad-mode repair also uses a pinned descriptor; there is no pathname chmod.
|
|
646
648
|
|
|
647
649
|
Repair and finalization require a known nonnegative safe-integer UID and exact
|
|
648
|
-
bigint device, inode, owner, mode, and directory-type facts.
|
|
650
|
+
bigint device, inode, owner, mode, and directory-type facts. Device and inode
|
|
651
|
+
identities accept Node's signed 64-bit stat representation, including negative
|
|
652
|
+
values down to `-(1n << 63n)`; inode zero remains invalid. Previously supported
|
|
653
|
+
nonnegative device and positive inode adapter values remain supported without
|
|
654
|
+
an upper cap. Values are compared exactly as received: signed and unsigned
|
|
655
|
+
encodings of the same bits are not treated as equal. They open with
|
|
649
656
|
`O_RDONLY | O_DIRECTORY | O_NOFOLLOW | O_NONBLOCK`, verify the descriptor and
|
|
650
657
|
current directory entry against the initial receipt before `fchmod`, then
|
|
651
658
|
verify identity, permissions, and write/search access again before closing.
|
package/docs/writing.md
CHANGED
|
@@ -198,6 +198,11 @@ existing `durable` file/directory synchronization policy; it does not turn
|
|
|
198
198
|
best-effort synchronization into a strict crash-durability guarantee or strengthen
|
|
199
199
|
JavaScript pathname containment.
|
|
200
200
|
|
|
201
|
+
Wider option objects do not enable create-only `atomic` or `private` behavior on
|
|
202
|
+
`write` or `writeJson`. Buffered Root writes and creates also ignore an extra
|
|
203
|
+
`maxBytes` property; that byte limit belongs to streamed creation and `copyIn`.
|
|
204
|
+
File synchronization follows each method's documented `durable` option.
|
|
205
|
+
|
|
201
206
|
Atomic and streamed creates settle owned cleanup and close operations before
|
|
202
207
|
returning. Failed or unverifiable cleanup is reported rather than silently
|
|
203
208
|
discarded. Errors after publication and incomplete-settlement errors carry the
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@openclaw/fs-safe",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.1",
|
|
4
4
|
"description": "Capability-style filesystem roots for Node.js apps that handle untrusted relative paths.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"filesystem",
|
|
@@ -169,13 +169,13 @@
|
|
|
169
169
|
"archive:producer-smoke": "node scripts/archive-producer-smoke.mjs"
|
|
170
170
|
},
|
|
171
171
|
"optionalDependencies": {
|
|
172
|
-
"@openclaw/fs-safe-darwin-arm64": "0.18.
|
|
173
|
-
"@openclaw/fs-safe-darwin-x64": "0.18.
|
|
174
|
-
"@openclaw/fs-safe-linux-arm64-gnu": "0.18.
|
|
175
|
-
"@openclaw/fs-safe-linux-arm64-musl": "0.18.
|
|
176
|
-
"@openclaw/fs-safe-linux-x64-gnu": "0.18.
|
|
177
|
-
"@openclaw/fs-safe-linux-x64-musl": "0.18.
|
|
178
|
-
"@openclaw/fs-safe-win32-x64-msvc": "0.18.
|
|
172
|
+
"@openclaw/fs-safe-darwin-arm64": "0.18.1",
|
|
173
|
+
"@openclaw/fs-safe-darwin-x64": "0.18.1",
|
|
174
|
+
"@openclaw/fs-safe-linux-arm64-gnu": "0.18.1",
|
|
175
|
+
"@openclaw/fs-safe-linux-arm64-musl": "0.18.1",
|
|
176
|
+
"@openclaw/fs-safe-linux-x64-gnu": "0.18.1",
|
|
177
|
+
"@openclaw/fs-safe-linux-x64-musl": "0.18.1",
|
|
178
|
+
"@openclaw/fs-safe-win32-x64-msvc": "0.18.1",
|
|
179
179
|
"jszip": "^3.10.2"
|
|
180
180
|
},
|
|
181
181
|
"devDependencies": {
|
package/dist/archive-native.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
import type { ArchiveKind } from "./archive-kind.js";
|
|
2
|
-
import { type TarMeterLimits } from "./archive-limits.js";
|
|
3
|
-
import type { StagedArchiveExtractOptions } from "./archive-options.js";
|
|
4
|
-
import type { NativeBinding } from "./native.js";
|
|
5
|
-
export declare function throwMappedNativeArchiveError(error: unknown): never;
|
|
6
|
-
export declare function extractNativeArchive(params: StagedArchiveExtractOptions & {
|
|
7
|
-
binding: NativeBinding;
|
|
8
|
-
kind: ArchiveKind;
|
|
9
|
-
tarLimits: TarMeterLimits;
|
|
10
|
-
}): Promise<void>;
|
package/dist/archive-native.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
import { classifyArchiveParserError } from "./archive-parser-errors.js";
|
|
2
|
-
import { constants as fsConstants } from "node:fs";
|
|
3
|
-
import fs from "node:fs/promises";
|
|
4
|
-
import { ArchiveFormatError, isArchiveFormatErrorMessage, } from "./archive-errors.js";
|
|
5
|
-
import { validateArchiveEntryPath } from "./archive-entry.js";
|
|
6
|
-
import { createArchiveEntryPlanner } from "./archive-plan.js";
|
|
7
|
-
import { assertArchiveEntryCountWithinLimit, } from "./archive-limits.js";
|
|
8
|
-
import { prepareArchiveDestinationGuard } from "./archive-staging.js";
|
|
9
|
-
import { withStagedArchivePublication } from "./archive-merge.js";
|
|
10
|
-
import { admitZipFile } from "./archive-zip-admission.js";
|
|
11
|
-
import { validateNativeZipManifest } from "./archive-zip-manifest.js";
|
|
12
|
-
export function throwMappedNativeArchiveError(error) {
|
|
13
|
-
if (error instanceof Error) {
|
|
14
|
-
const mapped = classifyArchiveParserError(error.message, { cause: error });
|
|
15
|
-
if (mapped)
|
|
16
|
-
throw mapped;
|
|
17
|
-
if (isArchiveFormatErrorMessage(error.message)) {
|
|
18
|
-
throw new ArchiveFormatError(error.message, { cause: error });
|
|
19
|
-
}
|
|
20
|
-
if (error.code === "InvalidArg") {
|
|
21
|
-
throw new ArchiveFormatError(`invalid archive: ${error.message}`, { cause: error });
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
throw error;
|
|
25
|
-
}
|
|
26
|
-
export async function extractNativeArchive(params) {
|
|
27
|
-
const { archivePath, limits, tarLimits, deadline } = params;
|
|
28
|
-
const zipEntries = [];
|
|
29
|
-
if (params.kind === "zip") {
|
|
30
|
-
await admitZipFile(archivePath, limits, deadline, (entry) => { zipEntries.push(entry); });
|
|
31
|
-
}
|
|
32
|
-
const destinationGuard = await prepareArchiveDestinationGuard(params.destDir);
|
|
33
|
-
await withStagedArchivePublication({ ...params, destinationGuard }, async (stagingDir) => {
|
|
34
|
-
deadline.check();
|
|
35
|
-
// N-API retains completed task state on its signal; each pass needs its own.
|
|
36
|
-
const manifest = await params.binding
|
|
37
|
-
.inspectArchiveNative(archivePath, params.kind, tarLimits, AbortSignal.any([deadline.signal]))
|
|
38
|
-
.catch(throwMappedNativeArchiveError);
|
|
39
|
-
deadline.check();
|
|
40
|
-
assertArchiveEntryCountWithinLimit(manifest.length, limits);
|
|
41
|
-
if (params.kind === "zip") {
|
|
42
|
-
validateNativeZipManifest(manifest, zipEntries);
|
|
43
|
-
}
|
|
44
|
-
// Recheck the native manifest before any caller callback observes an entry.
|
|
45
|
-
if (params.kind !== "zip") {
|
|
46
|
-
for (const entry of manifest)
|
|
47
|
-
validateArchiveEntryPath(entry.path);
|
|
48
|
-
}
|
|
49
|
-
const planEntry = createArchiveEntryPlanner({ ...params, rootDir: stagingDir }, params.kind);
|
|
50
|
-
const plan = [];
|
|
51
|
-
for (const entry of manifest) {
|
|
52
|
-
deadline.check();
|
|
53
|
-
const mode = params.kind === "zip"
|
|
54
|
-
? zipEntries[entry.index].creatorSystem === 3
|
|
55
|
-
? zipEntries[entry.index].externalAttributes >>> 16
|
|
56
|
-
: undefined
|
|
57
|
-
: entry.mode;
|
|
58
|
-
const accepted = planEntry({ ...entry, mode });
|
|
59
|
-
if (accepted)
|
|
60
|
-
plan.push({ ...accepted, index: entry.index });
|
|
61
|
-
}
|
|
62
|
-
const directory = await fs.open(stagingDir, fsConstants.O_RDONLY |
|
|
63
|
-
(typeof fsConstants.O_DIRECTORY === "number" ? fsConstants.O_DIRECTORY : 0));
|
|
64
|
-
try {
|
|
65
|
-
deadline.check();
|
|
66
|
-
await params.binding.extractArchiveNative(archivePath, params.kind, directory.fd, plan.map((entry) => ({ ...entry, mode: entry.kind === "directory" ? 0o700 : 0o600 })), tarLimits, AbortSignal.any([deadline.signal])).catch(throwMappedNativeArchiveError);
|
|
67
|
-
}
|
|
68
|
-
finally {
|
|
69
|
-
await directory.close().catch(() => undefined);
|
|
70
|
-
}
|
|
71
|
-
return plan;
|
|
72
|
-
});
|
|
73
|
-
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import type { ArchiveKind } from "./archive-kind.js";
|
|
2
|
-
import type { ExtractionDeadline } from "./archive-deadline.js";
|
|
3
|
-
import type { ArchiveExtractLimits, ResolvedArchiveExtractLimits } from "./archive-limits.js";
|
|
4
|
-
import type { ArchiveEntryFilter, ArchiveEntryModePolicy, ArchiveFilteredEntryPolicy } from "./archive-policy.js";
|
|
5
|
-
export type ArchiveLogger = {
|
|
6
|
-
info?: (message: string) => void;
|
|
7
|
-
warn?: (message: string) => void;
|
|
8
|
-
};
|
|
9
|
-
export type ExtractArchiveOptions = {
|
|
10
|
-
archivePath: string;
|
|
11
|
-
destDir: string;
|
|
12
|
-
timeoutMs: number;
|
|
13
|
-
/** Sync published files and directories before returning. Defaults to false. */
|
|
14
|
-
durable?: boolean;
|
|
15
|
-
kind?: ArchiveKind;
|
|
16
|
-
stripComponents?: number;
|
|
17
|
-
tarGzip?: boolean;
|
|
18
|
-
limits?: ArchiveExtractLimits;
|
|
19
|
-
logger?: ArchiveLogger;
|
|
20
|
-
entryModes?: ArchiveEntryModePolicy;
|
|
21
|
-
/** Remove these rwx bits from final entry modes. Defaults to zero. */
|
|
22
|
-
entryUmask?: number;
|
|
23
|
-
entryFilter?: ArchiveEntryFilter;
|
|
24
|
-
onFiltered?: ArchiveFilteredEntryPolicy;
|
|
25
|
-
};
|
|
26
|
-
/** Private executors receive owned options and an already-staged archive path. */
|
|
27
|
-
export type StagedArchiveExtractOptions = Pick<ExtractArchiveOptions, "archivePath" | "destDir" | "durable" | "stripComponents" | "entryModes" | "entryUmask" | "entryFilter" | "onFiltered"> & {
|
|
28
|
-
limits: ResolvedArchiveExtractLimits;
|
|
29
|
-
deadline: ExtractionDeadline;
|
|
30
|
-
};
|
package/dist/archive-policy.d.ts
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export type ArchiveEntryKind = "file" | "directory" | "symlink" | "other";
|
|
2
|
-
export type ArchiveEntryModePolicy = "clamp" | "preserve";
|
|
3
|
-
export type ArchiveFilteredEntryPolicy = "reject-archive" | "skip-entry";
|
|
4
|
-
export type ArchiveEntryFilter = (entry: {
|
|
5
|
-
/** Validated canonical archive path before stripping: / separators, no empty or . components. */
|
|
6
|
-
path: string;
|
|
7
|
-
kind: ArchiveEntryKind;
|
|
8
|
-
size: number;
|
|
9
|
-
}) => "extract" | "skip";
|
|
10
|
-
export declare function archiveEntryKindFromTarType(type: string): ArchiveEntryKind;
|
|
11
|
-
export declare function resolveArchiveEntryMode(params: {
|
|
12
|
-
kind: "file" | "directory";
|
|
13
|
-
archivedMode?: number | null;
|
|
14
|
-
policy?: ArchiveEntryModePolicy;
|
|
15
|
-
}): number;
|
|
16
|
-
export declare function resolveArchiveFilteredEntryPolicy(value: unknown): ArchiveFilteredEntryPolicy;
|
|
17
|
-
export declare function shouldExtractArchiveEntry(params: {
|
|
18
|
-
filter?: ArchiveEntryFilter;
|
|
19
|
-
onFiltered?: ArchiveFilteredEntryPolicy;
|
|
20
|
-
entry: Parameters<ArchiveEntryFilter>[0];
|
|
21
|
-
}): boolean;
|
package/dist/archive-policy.js
DELETED
|
@@ -1,38 +0,0 @@
|
|
|
1
|
-
import { ArchiveSecurityError } from "./archive-errors.js";
|
|
2
|
-
import { formatErrorDetail } from "./error-detail.js";
|
|
3
|
-
export function archiveEntryKindFromTarType(type) {
|
|
4
|
-
if (type === "Directory" || type === "GNUDumpDir")
|
|
5
|
-
return "directory";
|
|
6
|
-
if (type === "File" || type === "OldFile" || type === "ContiguousFile")
|
|
7
|
-
return "file";
|
|
8
|
-
if (type === "SymbolicLink" || type === "Link")
|
|
9
|
-
return "symlink";
|
|
10
|
-
return "other";
|
|
11
|
-
}
|
|
12
|
-
export function resolveArchiveEntryMode(params) {
|
|
13
|
-
const archivedMode = (params.archivedMode ?? 0) & 0o777;
|
|
14
|
-
if (params.policy === "preserve") {
|
|
15
|
-
return params.archivedMode == null ? (params.kind === "directory" ? 0o755 : 0o644) : archivedMode;
|
|
16
|
-
}
|
|
17
|
-
if (params.kind === "directory") {
|
|
18
|
-
return 0o755;
|
|
19
|
-
}
|
|
20
|
-
return archivedMode & 0o100 ? 0o755 : 0o644;
|
|
21
|
-
}
|
|
22
|
-
export function resolveArchiveFilteredEntryPolicy(value) {
|
|
23
|
-
if (value === undefined || value === "reject-archive")
|
|
24
|
-
return "reject-archive";
|
|
25
|
-
if (value === "skip-entry")
|
|
26
|
-
return "skip-entry";
|
|
27
|
-
throw new RangeError('archive onFiltered must be "reject-archive" or "skip-entry"');
|
|
28
|
-
}
|
|
29
|
-
export function shouldExtractArchiveEntry(params) {
|
|
30
|
-
const onFiltered = resolveArchiveFilteredEntryPolicy(params.onFiltered);
|
|
31
|
-
if (!params.filter || params.filter(params.entry) === "extract") {
|
|
32
|
-
return true;
|
|
33
|
-
}
|
|
34
|
-
if (onFiltered === "reject-archive") {
|
|
35
|
-
throw new ArchiveSecurityError("entry-filtered", `archive entry rejected by filter: ${formatErrorDetail(params.entry.path)}`);
|
|
36
|
-
}
|
|
37
|
-
return false;
|
|
38
|
-
}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import type { ExtractArchiveOptions } from "./archive-options.js";
|
|
2
|
-
import { type ArchivePlanEntry } from "./archive-plan.js";
|
|
3
|
-
export type InspectTarArchiveOptions = Pick<ExtractArchiveOptions, "archivePath" | "timeoutMs" | "limits" | "entryFilter" | "onFiltered">;
|
|
4
|
-
export type InspectedTarEntry = Readonly<Pick<ArchivePlanEntry, "path" | "kind" | "size">>;
|
|
5
|
-
/** Complete TAR/gzip admission and zero-strip extraction policy, without output writes. */
|
|
6
|
-
export declare function inspectTarArchive(params: InspectTarArchiveOptions): Promise<readonly InspectedTarEntry[]>;
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
import { withExtractionDeadline } from "./archive-deadline.js";
|
|
2
|
-
import { validateArchiveEntryPath } from "./archive-entry.js";
|
|
3
|
-
import { stageArchiveFileForExtraction } from "./archive-input.js";
|
|
4
|
-
import { resolveExtractLimits, resolveTarMeterLimits } from "./archive-limits.js";
|
|
5
|
-
import { throwMappedNativeArchiveError } from "./archive-native.js";
|
|
6
|
-
import { createArchiveEntryPlanner } from "./archive-plan.js";
|
|
7
|
-
import { resolveArchiveFilteredEntryPolicy } from "./archive-policy.js";
|
|
8
|
-
import { createTarEntryPlanner } from "./archive-tar.js";
|
|
9
|
-
import { inspectTar } from "./archive-tar-stream.js";
|
|
10
|
-
import { getNativeBinding } from "./native.js";
|
|
11
|
-
import { assertNoWindowsPathAlias } from "./windows-path-alias.js";
|
|
12
|
-
/** Complete TAR/gzip admission and zero-strip extraction policy, without output writes. */
|
|
13
|
-
export async function inspectTarArchive(params) {
|
|
14
|
-
const archivePath = params.archivePath;
|
|
15
|
-
const onFiltered = resolveArchiveFilteredEntryPolicy(params.onFiltered);
|
|
16
|
-
const limits = resolveExtractLimits(params.limits);
|
|
17
|
-
const tarLimits = resolveTarMeterLimits(limits);
|
|
18
|
-
const native = getNativeBinding();
|
|
19
|
-
assertNoWindowsPathAlias(archivePath, "filesystem", "archive source uses a Windows filesystem namespace alias");
|
|
20
|
-
return await withExtractionDeadline(params.timeoutMs, "inspect tar", async (deadline) => {
|
|
21
|
-
const staged = await stageArchiveFileForExtraction({ archivePath, limits, deadline });
|
|
22
|
-
try {
|
|
23
|
-
// Staging closes descriptors asynchronously; expiry there must not start a decoder.
|
|
24
|
-
deadline.check();
|
|
25
|
-
const entries = [];
|
|
26
|
-
const append = (entry) => {
|
|
27
|
-
if (entry)
|
|
28
|
-
entries.push(Object.freeze({ path: entry.path, kind: entry.kind, size: entry.size }));
|
|
29
|
-
};
|
|
30
|
-
const policy = { limits, entryFilter: params.entryFilter, onFiltered };
|
|
31
|
-
if (native) {
|
|
32
|
-
const manifest = await native.inspectArchiveNative(staged.path, "tar", tarLimits, deadline.signal)
|
|
33
|
-
.catch(throwMappedNativeArchiveError);
|
|
34
|
-
deadline.check();
|
|
35
|
-
// Match extraction's whole-manifest validation before caller policy runs.
|
|
36
|
-
for (const entry of manifest)
|
|
37
|
-
validateArchiveEntryPath(entry.path);
|
|
38
|
-
const planEntry = createArchiveEntryPlanner(policy, "tar");
|
|
39
|
-
for (const entry of manifest) {
|
|
40
|
-
deadline.check();
|
|
41
|
-
append(planEntry(entry));
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
else {
|
|
45
|
-
const manifest = [];
|
|
46
|
-
await inspectTar({ archivePath: staged.path, limits: tarLimits, signal: deadline.signal,
|
|
47
|
-
onMember: (entry) => { manifest.push(entry); } });
|
|
48
|
-
const planEntry = createTarEntryPlanner(policy);
|
|
49
|
-
for (const entry of manifest) {
|
|
50
|
-
deadline.check();
|
|
51
|
-
append(planEntry(entry));
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
deadline.check();
|
|
55
|
-
// This is bounded evidence about the staged bytes, not a reusable write plan.
|
|
56
|
-
return Object.freeze(entries);
|
|
57
|
-
}
|
|
58
|
-
finally {
|
|
59
|
-
await staged.cleanup();
|
|
60
|
-
}
|
|
61
|
-
});
|
|
62
|
-
}
|
package/dist/archive-tar.d.ts
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import type { ArchiveExtractLimits } from "./archive-limits.js";
|
|
2
|
-
import { type ArchivePlanEntry, type ArchivePlanOptions } from "./archive-plan.js";
|
|
3
|
-
import { type ArchiveEntryFilter, type ArchiveFilteredEntryPolicy } from "./archive-policy.js";
|
|
4
|
-
export type TarEntryInfo = {
|
|
5
|
-
path: string;
|
|
6
|
-
type: string;
|
|
7
|
-
size: number;
|
|
8
|
-
mode?: number;
|
|
9
|
-
};
|
|
10
|
-
export declare function createTarEntryPlanner(params: ArchivePlanOptions): (entry: TarEntryInfo) => ArchivePlanEntry | null;
|
|
11
|
-
export declare function createTarEntryPreflightChecker(params: {
|
|
12
|
-
rootDir: string;
|
|
13
|
-
stripComponents?: number;
|
|
14
|
-
limits?: ArchiveExtractLimits;
|
|
15
|
-
escapeLabel?: string;
|
|
16
|
-
entryFilter?: ArchiveEntryFilter;
|
|
17
|
-
onFiltered?: ArchiveFilteredEntryPolicy;
|
|
18
|
-
}): (entry: TarEntryInfo) => boolean;
|
package/dist/archive-tar.js
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
import { createArchiveEntryPlanner, } from "./archive-plan.js";
|
|
2
|
-
import { archiveEntryKindFromTarType, } from "./archive-policy.js";
|
|
3
|
-
const BLOCKED_TAR_ENTRY_TYPES = new Set([
|
|
4
|
-
"BlockDevice",
|
|
5
|
-
"CharacterDevice",
|
|
6
|
-
"FIFO",
|
|
7
|
-
"Socket",
|
|
8
|
-
]);
|
|
9
|
-
export function createTarEntryPlanner(params) {
|
|
10
|
-
// Preserve inherited/getter-backed public options and per-entry policy reads.
|
|
11
|
-
const plan = createArchiveEntryPlanner(params, "tar");
|
|
12
|
-
return (entry) => {
|
|
13
|
-
const kind = BLOCKED_TAR_ENTRY_TYPES.has(entry.type) ? "blocked" : archiveEntryKindFromTarType(entry.type);
|
|
14
|
-
// The public checker accepts structural entry objects, including class getters.
|
|
15
|
-
return plan({ path: entry.path, kind, size: entry.size, mode: entry.mode });
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
export function createTarEntryPreflightChecker(params) {
|
|
19
|
-
const plan = createTarEntryPlanner(params);
|
|
20
|
-
return (entry) => plan(entry) !== null;
|
|
21
|
-
}
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
import { type BigIntStats } from "node:fs";
|
|
2
|
-
import { type CreationPermissions } from "./creation-permissions.js";
|
|
3
|
-
import { type CreationParentIdentity } from "./creation-path.js";
|
|
4
|
-
export type CreateDirectoryOptions = CreationPermissions & {
|
|
5
|
-
assertBeforeMutation?: () => void;
|
|
6
|
-
};
|
|
7
|
-
export type CreationAdmission = {
|
|
8
|
-
expectedParentIdentity?: CreationParentIdentity;
|
|
9
|
-
};
|
|
10
|
-
export type CreationDirectoryReceipt = {
|
|
11
|
-
stat: BigIntStats;
|
|
12
|
-
windowsIdentity?: string;
|
|
13
|
-
};
|
|
14
|
-
export declare function createDirectory(targetPath: string, options?: CreateDirectoryOptions): Promise<void>;
|
|
15
|
-
export declare function createDirectoryWithAdmission(targetPath: string, options?: CreateDirectoryOptions, admission?: CreationAdmission): Promise<void>;
|
|
16
|
-
export declare function createDirectoryWithReceipt(targetPath: string, options?: CreateDirectoryOptions, admission?: CreationAdmission): Promise<CreationDirectoryReceipt>;
|
|
17
|
-
export declare function createDirectorySync(targetPath: string, options?: CreateDirectoryOptions): void;
|
|
18
|
-
export declare function createDirectoryWithReceiptSync(targetPath: string, options?: CreateDirectoryOptions, admission?: CreationAdmission): CreationDirectoryReceipt;
|