@openclaw/fs-safe 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +16 -0
- package/dist/archive-utils.d.ts +3 -0
- package/dist/archive-utils.d.ts.map +1 -0
- package/dist/archive-utils.js +26 -0
- package/dist/boundary-file-read.d.ts +44 -0
- package/dist/boundary-file-read.d.ts.map +1 -0
- package/dist/boundary-file-read.js +129 -0
- package/dist/boundary-path.d.ts +39 -0
- package/dist/boundary-path.d.ts.map +1 -0
- package/dist/boundary-path.js +598 -0
- package/dist/fs-pinned-path-helper.d.ts +7 -0
- package/dist/fs-pinned-path-helper.d.ts.map +1 -0
- package/dist/fs-pinned-path-helper.js +182 -0
- package/dist/fs-pinned-write-helper.d.ts +21 -0
- package/dist/fs-pinned-write-helper.d.ts.map +1 -0
- package/dist/fs-pinned-write-helper.js +263 -0
- package/dist/hardlink-guards.d.ts +7 -0
- package/dist/hardlink-guards.d.ts.map +1 -0
- package/dist/hardlink-guards.js +30 -0
- package/dist/home-dir.d.ts.map +1 -1
- package/dist/home-dir.js +38 -33
- package/dist/install-safe-path.d.ts +20 -0
- package/dist/install-safe-path.d.ts.map +1 -0
- package/dist/install-safe-path.js +94 -0
- package/dist/json-file.d.ts +3 -0
- package/dist/json-file.d.ts.map +1 -0
- package/dist/json-file.js +123 -0
- package/dist/json-files.d.ts +20 -0
- package/dist/json-files.d.ts.map +1 -0
- package/dist/json-files.js +153 -0
- package/dist/path-alias-guards.d.ts +19 -0
- package/dist/path-alias-guards.d.ts.map +1 -0
- package/dist/path-alias-guards.js +21 -0
- package/dist/path-guards.d.ts +7 -0
- package/dist/path-guards.d.ts.map +1 -0
- package/dist/path-guards.js +49 -0
- package/dist/path-safety.d.ts +12 -0
- package/dist/path-safety.d.ts.map +1 -0
- package/dist/path-safety.js +50 -0
- package/dist/path.d.ts.map +1 -1
- package/dist/path.js +4 -3
- package/dist/pinned-python-config.d.ts.map +1 -1
- package/dist/pinned-python-config.js +2 -3
- package/dist/private-file-store.d.ts +7 -5
- package/dist/private-file-store.d.ts.map +1 -1
- package/dist/private-file-store.js +34 -21
- package/dist/root-impl.js +5 -2
- package/dist/safe-open-sync.d.ts +24 -0
- package/dist/safe-open-sync.d.ts.map +1 -0
- package/dist/safe-open-sync.js +71 -0
- package/dist/safe-root.d.ts +123 -0
- package/dist/safe-root.d.ts.map +1 -0
- package/dist/safe-root.js +1060 -0
- package/dist/secret-file.d.ts.map +1 -1
- package/dist/secret-file.js +22 -6
- package/dist/secure-temp-workspace.d.ts +25 -0
- package/dist/secure-temp-workspace.d.ts.map +1 -0
- package/dist/secure-temp-workspace.js +136 -0
- package/dist/sibling-temp-file.d.ts +16 -0
- package/dist/sibling-temp-file.d.ts.map +1 -0
- package/dist/sibling-temp-file.js +73 -0
- package/dist/sibling-temp-write.d.ts +8 -0
- package/dist/sibling-temp-write.d.ts.map +1 -0
- package/dist/sibling-temp-write.js +40 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"private-file-store.d.ts","sourceRoot":"","sources":["../src/private-file-store.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"private-file-store.d.ts","sourceRoot":"","sources":["../src/private-file-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -2,10 +2,11 @@ import { randomUUID } from "node:crypto";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import { FsSafeError } from "./errors.js";
|
|
5
|
+
import { fileStore } from "./file-store.js";
|
|
5
6
|
import { isPathInside } from "./path.js";
|
|
6
7
|
import { readRegularFileSync } from "./regular-file.js";
|
|
7
8
|
import { root } from "./root.js";
|
|
8
|
-
import {
|
|
9
|
+
import { writeSecretFileAtomic } from "./secret-file.js";
|
|
9
10
|
function resolvePrivateStorePath(rootDir, relativePath) {
|
|
10
11
|
const root = path.resolve(rootDir);
|
|
11
12
|
const raw = relativePath.trim();
|
|
@@ -20,7 +21,7 @@ function resolvePrivateStorePath(rootDir, relativePath) {
|
|
|
20
21
|
return resolved;
|
|
21
22
|
}
|
|
22
23
|
export async function writePrivateTextAtomic(params) {
|
|
23
|
-
await
|
|
24
|
+
await writeSecretFileAtomic(params);
|
|
24
25
|
}
|
|
25
26
|
export async function readPrivateText(params) {
|
|
26
27
|
const rootDir = path.resolve(params.rootDir);
|
|
@@ -172,7 +173,7 @@ export function writePrivateTextAtomicSync(params) {
|
|
|
172
173
|
}
|
|
173
174
|
export async function writePrivateJsonAtomic(params) {
|
|
174
175
|
const json = JSON.stringify(params.value, null, 2);
|
|
175
|
-
await
|
|
176
|
+
await writeSecretFileAtomic({
|
|
176
177
|
rootDir: params.rootDir,
|
|
177
178
|
filePath: params.filePath,
|
|
178
179
|
content: params.trailingNewline && !json.endsWith("\n") ? `${json}\n` : json,
|
|
@@ -186,35 +187,47 @@ export function writePrivateJsonAtomicSync(params) {
|
|
|
186
187
|
content: params.trailingNewline && !json.endsWith("\n") ? `${json}\n` : json,
|
|
187
188
|
});
|
|
188
189
|
}
|
|
189
|
-
export function
|
|
190
|
-
const
|
|
190
|
+
export function privateStateStore(options) {
|
|
191
|
+
const rootDir = path.resolve(options.rootDir);
|
|
192
|
+
const store = fileStore({ rootDir, private: true });
|
|
191
193
|
return {
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
194
|
+
...store,
|
|
195
|
+
rootDir,
|
|
196
|
+
path: (relativePath) => resolvePrivateStorePath(rootDir, relativePath),
|
|
197
|
+
readText: async (relativePath, options) => {
|
|
198
|
+
const safePath = resolvePrivateStorePath(rootDir, relativePath);
|
|
199
|
+
return await readPrivateText({
|
|
200
|
+
rootDir,
|
|
201
|
+
filePath: safePath,
|
|
202
|
+
maxBytes: options?.maxBytes,
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
readJson: async (relativePath, options) => {
|
|
206
|
+
const safePath = resolvePrivateStorePath(rootDir, relativePath);
|
|
207
|
+
return await readPrivateJson({
|
|
208
|
+
rootDir,
|
|
209
|
+
filePath: safePath,
|
|
210
|
+
maxBytes: options?.maxBytes,
|
|
211
|
+
});
|
|
212
|
+
},
|
|
204
213
|
writeText: async (relativePath, content) => {
|
|
214
|
+
const safePath = resolvePrivateStorePath(rootDir, relativePath);
|
|
205
215
|
await writePrivateTextAtomic({
|
|
206
|
-
rootDir
|
|
207
|
-
filePath:
|
|
216
|
+
rootDir,
|
|
217
|
+
filePath: safePath,
|
|
208
218
|
content,
|
|
209
219
|
});
|
|
220
|
+
return safePath;
|
|
210
221
|
},
|
|
211
222
|
writeJson: async (relativePath, value, options) => {
|
|
223
|
+
const safePath = resolvePrivateStorePath(rootDir, relativePath);
|
|
212
224
|
await writePrivateJsonAtomic({
|
|
213
|
-
rootDir
|
|
214
|
-
filePath:
|
|
225
|
+
rootDir,
|
|
226
|
+
filePath: safePath,
|
|
215
227
|
value,
|
|
216
228
|
trailingNewline: options?.trailingNewline,
|
|
217
229
|
});
|
|
230
|
+
return safePath;
|
|
218
231
|
},
|
|
219
232
|
};
|
|
220
233
|
}
|
package/dist/root-impl.js
CHANGED
|
@@ -856,8 +856,10 @@ async function resolvePinnedWriteTargetInRoot(root, relativePath, requestedMode)
|
|
|
856
856
|
catch (err) {
|
|
857
857
|
throw new FsSafeError("path-alias", "path alias escape blocked", { cause: err });
|
|
858
858
|
}
|
|
859
|
+
// resolvePathInRoot already enforces isPathInside, so any actual escape
|
|
860
|
+
// is rejected upstream.
|
|
859
861
|
const relativeResolved = path.relative(rootReal, resolved);
|
|
860
|
-
if (
|
|
862
|
+
if (path.isAbsolute(relativeResolved)) {
|
|
861
863
|
throw new FsSafeError("outside-workspace", "file is outside workspace root");
|
|
862
864
|
}
|
|
863
865
|
const relativePosix = relativeResolved
|
|
@@ -919,9 +921,10 @@ async function resolvePinnedOperationPathInRoot(root, params) {
|
|
|
919
921
|
if ((relativeResolved === "" || relativeResolved === ".") && params.allowRoot === true) {
|
|
920
922
|
return { rootReal: resolved.rootReal, resolved: resolved.canonicalPath, relativePosix: "" };
|
|
921
923
|
}
|
|
924
|
+
const firstSegment = relativeResolved.split(path.sep)[0];
|
|
922
925
|
if (relativeResolved === "" ||
|
|
923
926
|
relativeResolved === "." ||
|
|
924
|
-
|
|
927
|
+
firstSegment === ".." ||
|
|
925
928
|
path.isAbsolute(relativeResolved)) {
|
|
926
929
|
throw new FsSafeError("outside-workspace", "file is outside workspace root");
|
|
927
930
|
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
export type SafeOpenSyncFailureReason = "path" | "validation" | "io";
|
|
3
|
+
export type SafeOpenSyncResult = {
|
|
4
|
+
ok: true;
|
|
5
|
+
path: string;
|
|
6
|
+
fd: number;
|
|
7
|
+
stat: fs.Stats;
|
|
8
|
+
} | {
|
|
9
|
+
ok: false;
|
|
10
|
+
reason: SafeOpenSyncFailureReason;
|
|
11
|
+
error?: unknown;
|
|
12
|
+
};
|
|
13
|
+
export type SafeOpenSyncAllowedType = "file" | "directory";
|
|
14
|
+
export type SafeOpenSyncFs = Pick<typeof fs, "constants" | "lstatSync" | "realpathSync" | "openSync" | "fstatSync" | "closeSync">;
|
|
15
|
+
export declare function openVerifiedFileSync(params: {
|
|
16
|
+
filePath: string;
|
|
17
|
+
resolvedPath?: string;
|
|
18
|
+
rejectPathSymlink?: boolean;
|
|
19
|
+
rejectHardlinks?: boolean;
|
|
20
|
+
maxBytes?: number;
|
|
21
|
+
allowedType?: SafeOpenSyncAllowedType;
|
|
22
|
+
ioFs?: SafeOpenSyncFs;
|
|
23
|
+
}): SafeOpenSyncResult;
|
|
24
|
+
//# sourceMappingURL=safe-open-sync.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-open-sync.d.ts","sourceRoot":"","sources":["../src/safe-open-sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AAGzB,MAAM,MAAM,yBAAyB,GAAG,MAAM,GAAG,YAAY,GAAG,IAAI,CAAC;AAErE,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,EAAE,CAAC,KAAK,CAAA;CAAE,GACtD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,yBAAyB,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AAEtE,MAAM,MAAM,uBAAuB,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D,MAAM,MAAM,cAAc,GAAG,IAAI,CAC/B,OAAO,EAAE,EACT,WAAW,GAAG,WAAW,GAAG,cAAc,GAAG,UAAU,GAAG,WAAW,GAAG,WAAW,CACpF,CAAC;AAYF,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAC3C,QAAQ,EAAE,MAAM,CAAC;IACjB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iBAAiB,CAAC,EAAE,OAAO,CAAC;IAC5B,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,uBAAuB,CAAC;IACtC,IAAI,CAAC,EAAE,cAAc,CAAC;CACvB,GAAG,kBAAkB,CA2DrB"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { sameFileIdentity as hasSameFileIdentity } from "./file-identity.js";
|
|
3
|
+
function isExpectedPathError(error) {
|
|
4
|
+
const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "";
|
|
5
|
+
return code === "ENOENT" || code === "ENOTDIR" || code === "ELOOP";
|
|
6
|
+
}
|
|
7
|
+
function sameFileIdentity(left, right) {
|
|
8
|
+
return hasSameFileIdentity(left, right);
|
|
9
|
+
}
|
|
10
|
+
export function openVerifiedFileSync(params) {
|
|
11
|
+
const ioFs = params.ioFs ?? fs;
|
|
12
|
+
const allowedType = params.allowedType ?? "file";
|
|
13
|
+
const openReadFlags = ioFs.constants.O_RDONLY |
|
|
14
|
+
(typeof ioFs.constants.O_NOFOLLOW === "number" ? ioFs.constants.O_NOFOLLOW : 0);
|
|
15
|
+
let fd = null;
|
|
16
|
+
try {
|
|
17
|
+
if (params.rejectPathSymlink) {
|
|
18
|
+
const candidateStat = ioFs.lstatSync(params.filePath);
|
|
19
|
+
if (candidateStat.isSymbolicLink()) {
|
|
20
|
+
return { ok: false, reason: "validation" };
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
const realPath = params.resolvedPath ?? ioFs.realpathSync(params.filePath);
|
|
24
|
+
const preOpenStat = ioFs.lstatSync(realPath);
|
|
25
|
+
if (!isAllowedType(preOpenStat, allowedType)) {
|
|
26
|
+
return { ok: false, reason: "validation" };
|
|
27
|
+
}
|
|
28
|
+
if (params.rejectHardlinks && preOpenStat.isFile() && preOpenStat.nlink > 1) {
|
|
29
|
+
return { ok: false, reason: "validation" };
|
|
30
|
+
}
|
|
31
|
+
if (params.maxBytes !== undefined &&
|
|
32
|
+
preOpenStat.isFile() &&
|
|
33
|
+
preOpenStat.size > params.maxBytes) {
|
|
34
|
+
return { ok: false, reason: "validation" };
|
|
35
|
+
}
|
|
36
|
+
fd = ioFs.openSync(realPath, openReadFlags);
|
|
37
|
+
const openedStat = ioFs.fstatSync(fd);
|
|
38
|
+
if (!isAllowedType(openedStat, allowedType)) {
|
|
39
|
+
return { ok: false, reason: "validation" };
|
|
40
|
+
}
|
|
41
|
+
if (params.rejectHardlinks && openedStat.isFile() && openedStat.nlink > 1) {
|
|
42
|
+
return { ok: false, reason: "validation" };
|
|
43
|
+
}
|
|
44
|
+
if (params.maxBytes !== undefined && openedStat.isFile() && openedStat.size > params.maxBytes) {
|
|
45
|
+
return { ok: false, reason: "validation" };
|
|
46
|
+
}
|
|
47
|
+
if (!sameFileIdentity(preOpenStat, openedStat)) {
|
|
48
|
+
return { ok: false, reason: "validation" };
|
|
49
|
+
}
|
|
50
|
+
const opened = { ok: true, path: realPath, fd, stat: openedStat };
|
|
51
|
+
fd = null;
|
|
52
|
+
return opened;
|
|
53
|
+
}
|
|
54
|
+
catch (error) {
|
|
55
|
+
if (isExpectedPathError(error)) {
|
|
56
|
+
return { ok: false, reason: "path", error };
|
|
57
|
+
}
|
|
58
|
+
return { ok: false, reason: "io", error };
|
|
59
|
+
}
|
|
60
|
+
finally {
|
|
61
|
+
if (fd !== null) {
|
|
62
|
+
ioFs.closeSync(fd);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
function isAllowedType(stat, allowedType) {
|
|
67
|
+
if (allowedType === "directory") {
|
|
68
|
+
return stat.isDirectory();
|
|
69
|
+
}
|
|
70
|
+
return stat.isFile();
|
|
71
|
+
}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import type { Stats } from "node:fs";
|
|
2
|
+
import type { FileHandle } from "node:fs/promises";
|
|
3
|
+
import type { SafeDirEntry, SafePathStat } from "./types.js";
|
|
4
|
+
export type SafeOpenResult = {
|
|
5
|
+
handle: FileHandle;
|
|
6
|
+
realPath: string;
|
|
7
|
+
stat: Stats;
|
|
8
|
+
};
|
|
9
|
+
export type SafeLocalReadResult = {
|
|
10
|
+
buffer: Buffer;
|
|
11
|
+
realPath: string;
|
|
12
|
+
stat: Stats;
|
|
13
|
+
};
|
|
14
|
+
export type SafeRootContext = {
|
|
15
|
+
rootDir: string;
|
|
16
|
+
rootReal: string;
|
|
17
|
+
rootWithSep: string;
|
|
18
|
+
};
|
|
19
|
+
export type SafeRootOptions = {
|
|
20
|
+
rootDir: string;
|
|
21
|
+
defaults?: SafeRootDefaults;
|
|
22
|
+
};
|
|
23
|
+
export type SafeRootDefaults = {
|
|
24
|
+
append?: SafeRootAppendOptions;
|
|
25
|
+
copy?: SafeRootCopyOptions;
|
|
26
|
+
/** openFile merges read defaults first, then open defaults, then call options. */
|
|
27
|
+
open?: SafeRootOpenOptions;
|
|
28
|
+
openWritable?: SafeRootOpenWritableOptions;
|
|
29
|
+
read?: SafeRootReadOptions;
|
|
30
|
+
write?: SafeRootWriteOptions;
|
|
31
|
+
};
|
|
32
|
+
export type SafeRootSymlinkPolicy = "reject" | "follow-within-root";
|
|
33
|
+
export type SafeRootReadOptions = {
|
|
34
|
+
rejectHardlinks?: boolean;
|
|
35
|
+
nonBlockingRead?: boolean;
|
|
36
|
+
symlinkPolicy?: SafeRootSymlinkPolicy;
|
|
37
|
+
maxBytes?: number;
|
|
38
|
+
};
|
|
39
|
+
export type SafeRootOpenOptions = Omit<SafeRootReadOptions, "maxBytes">;
|
|
40
|
+
export type SafeRootWriteOptions = {
|
|
41
|
+
encoding?: BufferEncoding;
|
|
42
|
+
mkdir?: boolean;
|
|
43
|
+
};
|
|
44
|
+
export type SafeRootOpenWritableOptions = {
|
|
45
|
+
mkdir?: boolean;
|
|
46
|
+
mode?: number;
|
|
47
|
+
truncateExisting?: boolean;
|
|
48
|
+
append?: boolean;
|
|
49
|
+
};
|
|
50
|
+
export type SafeRootCopyOptions = {
|
|
51
|
+
maxBytes?: number;
|
|
52
|
+
mkdir?: boolean;
|
|
53
|
+
rejectSourceHardlinks?: boolean;
|
|
54
|
+
};
|
|
55
|
+
export type SafeRootWriteJsonOptions = SafeRootWriteOptions & {
|
|
56
|
+
replacer?: Parameters<typeof JSON.stringify>[1];
|
|
57
|
+
space?: Parameters<typeof JSON.stringify>[2];
|
|
58
|
+
trailingNewline?: boolean;
|
|
59
|
+
};
|
|
60
|
+
export type SafeRootCreateOptions = SafeRootWriteOptions;
|
|
61
|
+
export type SafeRootCreateJsonOptions = SafeRootWriteJsonOptions;
|
|
62
|
+
export type SafeRootAppendOptions = {
|
|
63
|
+
encoding?: BufferEncoding;
|
|
64
|
+
mkdir?: boolean;
|
|
65
|
+
prependNewlineIfNeeded?: boolean;
|
|
66
|
+
};
|
|
67
|
+
export declare class SafeRoot {
|
|
68
|
+
readonly rootDir: string;
|
|
69
|
+
readonly rootReal: string;
|
|
70
|
+
readonly rootWithSep: string;
|
|
71
|
+
readonly defaults: SafeRootDefaults;
|
|
72
|
+
constructor(context: SafeRootContext, defaults?: SafeRootDefaults);
|
|
73
|
+
private get context();
|
|
74
|
+
resolve(relativePath: string): Promise<string>;
|
|
75
|
+
openFile(relativePath: string, options?: SafeRootOpenOptions): Promise<SafeOpenResult>;
|
|
76
|
+
readFile(relativePath: string, options?: SafeRootReadOptions): Promise<SafeLocalReadResult>;
|
|
77
|
+
readFileBytes(relativePath: string, options?: SafeRootReadOptions): Promise<Buffer>;
|
|
78
|
+
readText(relativePath: string, options?: SafeRootReadOptions & {
|
|
79
|
+
encoding?: BufferEncoding;
|
|
80
|
+
}): Promise<string>;
|
|
81
|
+
readJson<T = unknown>(relativePath: string, options?: SafeRootReadOptions & {
|
|
82
|
+
encoding?: BufferEncoding;
|
|
83
|
+
}): Promise<T>;
|
|
84
|
+
readPath(filePath: string, options?: Pick<SafeRootReadOptions, "rejectHardlinks" | "maxBytes">): Promise<SafeLocalReadResult>;
|
|
85
|
+
reader(options?: Pick<SafeRootReadOptions, "rejectHardlinks" | "maxBytes">): (filePath: string) => Promise<Buffer>;
|
|
86
|
+
openWritable(relativePath: string, options?: SafeRootOpenWritableOptions): Promise<SafeWritableOpenResult>;
|
|
87
|
+
append(relativePath: string, data: string | Buffer, options?: SafeRootAppendOptions): Promise<void>;
|
|
88
|
+
removeFile(relativePath: string): Promise<void>;
|
|
89
|
+
mkdirp(relativePath: string, options?: {
|
|
90
|
+
allowRoot?: boolean;
|
|
91
|
+
}): Promise<void>;
|
|
92
|
+
writeFile(relativePath: string, data: string | Buffer, options?: SafeRootWriteOptions): Promise<void>;
|
|
93
|
+
createFile(relativePath: string, data: string | Buffer, options?: SafeRootCreateOptions): Promise<boolean>;
|
|
94
|
+
writeJson(relativePath: string, data: unknown, options?: SafeRootWriteJsonOptions): Promise<void>;
|
|
95
|
+
createJson(relativePath: string, data: unknown, options?: SafeRootCreateJsonOptions): Promise<boolean>;
|
|
96
|
+
copyFileFrom(sourcePath: string, relativePath: string, options?: SafeRootCopyOptions): Promise<void>;
|
|
97
|
+
statPath(relativePath: string): Promise<SafePathStat>;
|
|
98
|
+
listDir(relativePath: string, options?: {
|
|
99
|
+
withFileTypes?: false;
|
|
100
|
+
}): Promise<string[]>;
|
|
101
|
+
listDir(relativePath: string, options: {
|
|
102
|
+
withFileTypes: true;
|
|
103
|
+
}): Promise<SafeDirEntry[]>;
|
|
104
|
+
move(from: string, to: string, options?: {
|
|
105
|
+
overwrite?: boolean;
|
|
106
|
+
}): Promise<void>;
|
|
107
|
+
}
|
|
108
|
+
export declare function safeRoot(rootDir: string, defaults?: SafeRootDefaults): Promise<SafeRoot>;
|
|
109
|
+
export declare function readLocalFileSafely(params: {
|
|
110
|
+
filePath: string;
|
|
111
|
+
maxBytes?: number;
|
|
112
|
+
}): Promise<SafeLocalReadResult>;
|
|
113
|
+
export declare function openLocalFileSafely(params: {
|
|
114
|
+
filePath: string;
|
|
115
|
+
}): Promise<SafeOpenResult>;
|
|
116
|
+
export type SafeWritableOpenResult = {
|
|
117
|
+
handle: FileHandle;
|
|
118
|
+
createdForWrite: boolean;
|
|
119
|
+
realPath: string;
|
|
120
|
+
stat: Stats;
|
|
121
|
+
};
|
|
122
|
+
export declare function resolveOpenedFileRealPathForHandle(handle: FileHandle, ioPath: string): Promise<string>;
|
|
123
|
+
//# sourceMappingURL=safe-root.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"safe-root.d.ts","sourceRoot":"","sources":["../src/safe-root.ts"],"names":[],"mappings":"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"}
|