@poe-platform/safe-fs 0.1.657 → 0.1.659
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/dist/safe-fs/contracts/filesystem.d.ts +2 -0
- package/dist/safe-fs/fs/capabilities.d.ts +1 -1
- package/dist/safe-fs/fs/capabilities.js +3 -1
- package/dist/safe-fs/fs/devices/index.d.ts +1 -0
- package/dist/safe-fs/fs/devices/index.js +9 -2
- package/dist/safe-fs/fs/memory/index.d.ts +3 -0
- package/dist/safe-fs/fs/memory/index.js +19 -7
- package/dist/safe-fs/fs/mount/index.d.ts +1 -0
- package/dist/safe-fs/fs/mount/index.js +15 -2
- package/dist/safe-fs/fs/scoped.js +3 -3
- package/dist/safe-fs/python/filesystem.d.ts +1 -1
- package/dist/safe-fs/python/filesystem.js +20 -1
- package/dist/safe-fs/python/request.js +1 -1
- package/package.json +1 -1
|
@@ -62,6 +62,7 @@ export interface FileSystemCapabilities {
|
|
|
62
62
|
readonly atomicFileStaging?: boolean;
|
|
63
63
|
readonly atomicFileMutation?: boolean;
|
|
64
64
|
readonly atomicEntryRemoval?: boolean;
|
|
65
|
+
readonly atomicTreeRemoval?: boolean;
|
|
65
66
|
readonly atomicDirectoryMetadata?: boolean;
|
|
66
67
|
readonly atomicRenameNoReplace?: boolean;
|
|
67
68
|
readonly snapshotRmdir?: boolean;
|
|
@@ -200,6 +201,7 @@ export interface PrepareDirectoryOptions extends FsOptions {
|
|
|
200
201
|
export interface FileSystem {
|
|
201
202
|
writeFileConditional?(path: string, data: Uint8Array, options: ConditionalWriteFileOptions): Promise<FileStat>;
|
|
202
203
|
removeEntryConditional?(path: string, options: ConditionalRemoveEntryOptions): Promise<void>;
|
|
204
|
+
removeTreeConditional?(path: string, options: ConditionalRemoveEntryOptions): Promise<void>;
|
|
203
205
|
removeFileConditional?(path: string, options: ConditionalRemoveFileOptions): Promise<void>;
|
|
204
206
|
readonly capabilities: FileSystemCapabilities;
|
|
205
207
|
open?(path: string, options: OpenFileOptions): Promise<FileDescriptor>;
|
|
@@ -7,4 +7,4 @@ export declare function openRetainedResizeFile(filesystem: FileSystem, path: str
|
|
|
7
7
|
export declare function readOnlyCapabilities(capabilities: FileSystemCapabilities): FileSystemCapabilities;
|
|
8
8
|
export declare function quotaCapabilities(capabilities: FileSystemCapabilities): FileSystemCapabilities;
|
|
9
9
|
export declare function ownedMutationCapabilities(filesystem: FileSystem, capabilities?: FileSystemCapabilities): FileSystemCapabilities;
|
|
10
|
-
export declare function requireOwnedMutation(filesystem: FileSystem, path: string, capability: "atomicEntryRemoval" | "atomicFileMutation" | "atomicFileStaging" | "atomicDirectoryMetadata", options: FsOptions, create?: boolean): Promise<void>;
|
|
10
|
+
export declare function requireOwnedMutation(filesystem: FileSystem, path: string, capability: "atomicEntryRemoval" | "atomicTreeRemoval" | "atomicFileMutation" | "atomicFileStaging" | "atomicDirectoryMetadata", options: FsOptions, create?: boolean): Promise<void>;
|
|
@@ -108,7 +108,7 @@ export function readOnlyCapabilities(capabilities) {
|
|
|
108
108
|
mkdir: false, recursiveMkdir: false, remove: false, removeDirectory: false, recursiveRemove: false,
|
|
109
109
|
rename: false, copy: false, exclusiveCopy: false, truncate: false, streamingAppend: false,
|
|
110
110
|
randomAccessWrite: false, hardlinks: false, permissions: false, timestamps: false,
|
|
111
|
-
descriptorWriteStream: false, atomicResize: false, retainedResize: false, atomicFileMutation: false, atomicEntryRemoval: false, atomicFileStaging: false, atomicDirectoryMetadata: false,
|
|
111
|
+
descriptorWriteStream: false, atomicResize: false, retainedResize: false, atomicFileMutation: false, atomicEntryRemoval: false, atomicTreeRemoval: false, atomicFileStaging: false, atomicDirectoryMetadata: false,
|
|
112
112
|
atomicRename: false, atomicRenameNoReplace: false, streamingWrite: false,
|
|
113
113
|
});
|
|
114
114
|
}
|
|
@@ -125,6 +125,8 @@ export function ownedMutationCapabilities(filesystem, capabilities = filesystem.
|
|
|
125
125
|
const unavailable = {};
|
|
126
126
|
if (capabilities.atomicEntryRemoval === true && (capabilities.readOnly === true || typeof filesystem.removeEntryConditional !== "function"))
|
|
127
127
|
unavailable.atomicEntryRemoval = false;
|
|
128
|
+
if (capabilities.atomicTreeRemoval === true && (capabilities.readOnly === true || typeof filesystem.removeTreeConditional !== "function"))
|
|
129
|
+
unavailable.atomicTreeRemoval = false;
|
|
128
130
|
if (capabilities.atomicFileMutation === true && (capabilities.readOnly === true || typeof filesystem.writeFileConditional !== "function" || typeof filesystem.removeFileConditional !== "function"))
|
|
129
131
|
unavailable.atomicFileMutation = false;
|
|
130
132
|
if (capabilities.atomicFileStaging === true && (capabilities.readOnly === true
|
|
@@ -28,6 +28,7 @@ export declare class DeviceFileSystem implements FileSystem {
|
|
|
28
28
|
writeFileConditional(path: string, data: Uint8Array, options: ConditionalWriteFileOptions): Promise<FileStat>;
|
|
29
29
|
removeEntryConditional(path: string, options: ConditionalRemoveEntryOptions): Promise<void>;
|
|
30
30
|
removeFileConditional(path: string, options: ConditionalRemoveFileOptions): Promise<void>;
|
|
31
|
+
removeTreeConditional(path: string, options: ConditionalRemoveEntryOptions): Promise<void>;
|
|
31
32
|
createStagedFile(path: string, name: string, content: StagedFileContent, options: CreateStagedFileOptions): Promise<FileStaging>;
|
|
32
33
|
publishStagedFile(staging: FileStaging, destination: string, options: PublishStagedFileOptions): Promise<void>;
|
|
33
34
|
removeStagedFile(staging: FileStaging, options?: FsOptions): Promise<void>;
|
|
@@ -14,14 +14,14 @@ const deviceCapabilities = Object.freeze({
|
|
|
14
14
|
remove: false, removeDirectory: false, recursiveRemove: false, rename: false,
|
|
15
15
|
mkdir: false, recursiveMkdir: false, symlinks: false, hardlinks: false, readlink: false,
|
|
16
16
|
permissions: false, timestamps: false, truncate: false, randomAccessWrite: false,
|
|
17
|
-
open: false, atomicFileMutation: false, atomicEntryRemoval: false, atomicFileStaging: false, atomicDirectoryMetadata: false,
|
|
17
|
+
open: false, atomicFileMutation: false, atomicEntryRemoval: false, atomicTreeRemoval: false, atomicFileStaging: false, atomicDirectoryMetadata: false,
|
|
18
18
|
atomicRename: false, atomicRenameNoReplace: false, descriptorWriteStream: true, retainedResize: true, atomicResize: false,
|
|
19
19
|
});
|
|
20
20
|
function globalCapabilities(filesystem) {
|
|
21
21
|
const capabilities = { readOnly: false };
|
|
22
22
|
const optional = {
|
|
23
23
|
open: ["open"],
|
|
24
|
-
atomicEntryRemoval: ["removeEntryConditional"], atomicFileMutation: ["writeFileConditional", "removeFileConditional"], atomicFileStaging: ["createStagedFile", "publishStagedFile", "removeStagedFile"], atomicDirectoryMetadata: ["prepareDirectory"],
|
|
24
|
+
atomicEntryRemoval: ["removeEntryConditional"], atomicTreeRemoval: ["removeTreeConditional"], atomicFileMutation: ["writeFileConditional", "removeFileConditional"], atomicFileStaging: ["createStagedFile", "publishStagedFile", "removeStagedFile"], atomicDirectoryMetadata: ["prepareDirectory"],
|
|
25
25
|
streamingRead: ["readStream"], streamingWrite: ["writeStream"], retainedRead: ["openReadFile"],
|
|
26
26
|
streamingAppend: ["writeStream"], descriptorWriteStream: ["writeStream"], retainedResize: ["openResizeFile"], atomicResize: ["resizeFile"],
|
|
27
27
|
symlinks: ["symlink", "readlink"], hardlinks: ["link"], permissions: ["chmod"],
|
|
@@ -416,6 +416,13 @@ export class DeviceFileSystem {
|
|
|
416
416
|
throw new FsError("ENOTSUP", { path });
|
|
417
417
|
await this.#filesystem.removeFileConditional(path, options);
|
|
418
418
|
}
|
|
419
|
+
async removeTreeConditional(path, options) {
|
|
420
|
+
await this.#mutable(path, options, false);
|
|
421
|
+
await requireOwnedMutation(this.#filesystem, path, "atomicTreeRemoval", options);
|
|
422
|
+
if (!this.#filesystem.removeTreeConditional)
|
|
423
|
+
throw new FsError("ENOTSUP", { path });
|
|
424
|
+
await this.#filesystem.removeTreeConditional(path, options);
|
|
425
|
+
}
|
|
419
426
|
async createStagedFile(path, name, content, options) {
|
|
420
427
|
await this.#mutable(path, options, false);
|
|
421
428
|
await requireOwnedMutation(this.#filesystem, path, "atomicFileStaging", options, true);
|
|
@@ -37,6 +37,7 @@ export declare class MemoryFileSystem implements FileSystem {
|
|
|
37
37
|
atomicFileStaging: true;
|
|
38
38
|
atomicFileMutation: true;
|
|
39
39
|
atomicEntryRemoval: true;
|
|
40
|
+
atomicTreeRemoval: true;
|
|
40
41
|
atomicDirectoryMetadata: true;
|
|
41
42
|
streamingRead: true;
|
|
42
43
|
retainedRead: true;
|
|
@@ -87,6 +88,7 @@ export declare class MemoryFileSystem implements FileSystem {
|
|
|
87
88
|
writeFileConditional(path: string, data: Uint8Array, options: ConditionalWriteFileOptions): Promise<FileStat>;
|
|
88
89
|
removeEntryConditional(path: string, options: ConditionalRemoveEntryOptions): Promise<void>;
|
|
89
90
|
removeFileConditional(path: string, options: ConditionalRemoveFileOptions): Promise<void>;
|
|
91
|
+
removeTreeConditional(path: string, options: ConditionalRemoveEntryOptions): Promise<void>;
|
|
90
92
|
writeFile(path: string, data: Uint8Array, options?: WriteFileOptions): Promise<void>;
|
|
91
93
|
appendFile(path: string, data: Uint8Array, options?: AppendFileOptions): Promise<void>;
|
|
92
94
|
stat(path: string, options?: FsOptions): Promise<FileStat>;
|
|
@@ -95,6 +97,7 @@ export declare class MemoryFileSystem implements FileSystem {
|
|
|
95
97
|
mkdir(path: string, options?: MkdirOptions): Promise<void>;
|
|
96
98
|
rmdir(path: string, options?: FsOptions): Promise<void>;
|
|
97
99
|
rm(path: string, options?: RemoveOptions): Promise<void>;
|
|
100
|
+
private removeLocation;
|
|
98
101
|
unlink(path: string, options?: FsOptions): Promise<void>;
|
|
99
102
|
rename(source: string, destination: string, options?: RenameOptions): Promise<void>;
|
|
100
103
|
copyFile(source: string, destination: string, options?: CopyFileOptions): Promise<void>;
|
|
@@ -68,7 +68,7 @@ export class MemoryFileSystem {
|
|
|
68
68
|
permissions: true,
|
|
69
69
|
timestamps: true,
|
|
70
70
|
atomicRename: true,
|
|
71
|
-
atomicFileStaging: true, atomicFileMutation: true, atomicEntryRemoval: true,
|
|
71
|
+
atomicFileStaging: true, atomicFileMutation: true, atomicEntryRemoval: true, atomicTreeRemoval: true,
|
|
72
72
|
atomicDirectoryMetadata: true,
|
|
73
73
|
streamingRead: true,
|
|
74
74
|
retainedRead: true,
|
|
@@ -696,6 +696,15 @@ export class MemoryFileSystem {
|
|
|
696
696
|
this.releaseNode(node);
|
|
697
697
|
this.changed(location.parent);
|
|
698
698
|
}
|
|
699
|
+
async removeTreeConditional(path, options) {
|
|
700
|
+
options.signal?.throwIfAborted();
|
|
701
|
+
const location = this.entry(path, "removeTreeConditional", true);
|
|
702
|
+
this.expectEntry(location.parent, options.parent, path, false);
|
|
703
|
+
this.expectEntry(location.node, options.expected, path, false);
|
|
704
|
+
if (location.node?.type !== "directory")
|
|
705
|
+
this.fail("ENOTDIR", "removeTreeConditional", path);
|
|
706
|
+
this.removeLocation(location, path, "removeTreeConditional", true);
|
|
707
|
+
}
|
|
699
708
|
async writeFile(path, data, options = {}) {
|
|
700
709
|
options.signal?.throwIfAborted();
|
|
701
710
|
this.writeData(path, data, options, "writeFile");
|
|
@@ -773,19 +782,22 @@ export class MemoryFileSystem {
|
|
|
773
782
|
return;
|
|
774
783
|
throw error;
|
|
775
784
|
}
|
|
785
|
+
this.removeLocation(location, path, "rm", options.recursive === true);
|
|
786
|
+
}
|
|
787
|
+
removeLocation(location, path, syscall, recursive) {
|
|
776
788
|
const node = location.node;
|
|
777
789
|
if (this.terminalDot(path))
|
|
778
|
-
this.fail("EINVAL",
|
|
790
|
+
this.fail("EINVAL", syscall, path);
|
|
779
791
|
if (node === this.root)
|
|
780
|
-
this.fail("EBUSY",
|
|
781
|
-
this.permission(location.parent, 3,
|
|
782
|
-
if (node.type === "directory" && !
|
|
783
|
-
this.fail("EISDIR",
|
|
792
|
+
this.fail("EBUSY", syscall, path);
|
|
793
|
+
this.permission(location.parent, 3, syscall, path);
|
|
794
|
+
if (node.type === "directory" && !recursive)
|
|
795
|
+
this.fail("EISDIR", syscall, path);
|
|
784
796
|
const removed = [node];
|
|
785
797
|
for (let index = 0; index < removed.length; index++) {
|
|
786
798
|
const entry = removed[index];
|
|
787
799
|
if (entry.type === "directory" && entry.entries.size > 0) {
|
|
788
|
-
this.permission(entry, 7,
|
|
800
|
+
this.permission(entry, 7, syscall, path);
|
|
789
801
|
for (const child of entry.entries.values())
|
|
790
802
|
removed.push(child);
|
|
791
803
|
}
|
|
@@ -38,6 +38,7 @@ export declare class MountFileSystem implements FileSystem {
|
|
|
38
38
|
writeFileConditional(path: string, data: Uint8Array, options: ConditionalWriteFileOptions): Promise<FileStat>;
|
|
39
39
|
removeEntryConditional(path: string, options: ConditionalRemoveEntryOptions): Promise<void>;
|
|
40
40
|
removeFileConditional(path: string, options: ConditionalRemoveFileOptions): Promise<void>;
|
|
41
|
+
removeTreeConditional(path: string, options: ConditionalRemoveEntryOptions): Promise<void>;
|
|
41
42
|
createStagedFile(path: string, name: string, content: StagedFileContent, options: CreateStagedFileOptions): Promise<FileStaging>;
|
|
42
43
|
publishStagedFile(staging: FileStaging, destination: string, options: PublishStagedFileOptions): Promise<void>;
|
|
43
44
|
removeStagedFile(staging: FileStaging, options?: FsOptions): Promise<void>;
|
|
@@ -96,7 +96,7 @@ export class MountFileSystem {
|
|
|
96
96
|
const common = (capability) => {
|
|
97
97
|
const optional = {
|
|
98
98
|
open: ["open"],
|
|
99
|
-
atomicEntryRemoval: ["removeEntryConditional"], atomicFileMutation: ["writeFileConditional", "removeFileConditional"], atomicFileStaging: ["createStagedFile", "publishStagedFile", "removeStagedFile"], atomicDirectoryMetadata: ["prepareDirectory"],
|
|
99
|
+
atomicEntryRemoval: ["removeEntryConditional"], atomicTreeRemoval: ["removeTreeConditional"], atomicFileMutation: ["writeFileConditional", "removeFileConditional"], atomicFileStaging: ["createStagedFile", "publishStagedFile", "removeStagedFile"], atomicDirectoryMetadata: ["prepareDirectory"],
|
|
100
100
|
symlinks: ["symlink", "readlink"], hardlinks: ["link"], permissions: ["chmod"], timestamps: ["utimes"], readlink: ["readlink"],
|
|
101
101
|
descriptorWriteStream: ["writeStream"], retainedResize: ["openResizeFile"], atomicResize: ["resizeFile"],
|
|
102
102
|
};
|
|
@@ -114,7 +114,7 @@ export class MountFileSystem {
|
|
|
114
114
|
return values.every(value => value === true) ? true : values.every(value => value === false) ? false : undefined;
|
|
115
115
|
};
|
|
116
116
|
const semantics = Object.fromEntries([
|
|
117
|
-
"atomicEntryRemoval", "atomicFileMutation", "atomicFileStaging", "atomicDirectoryMetadata", "read", "stat", "readdir", "realpath", "access", "open",
|
|
117
|
+
"atomicEntryRemoval", "atomicTreeRemoval", "atomicFileMutation", "atomicFileStaging", "atomicDirectoryMetadata", "read", "stat", "readdir", "realpath", "access", "open",
|
|
118
118
|
"write", "append", "exclusiveCreate", "explicitDirectories", "implicitDirectories", "mkdir", "recursiveMkdir",
|
|
119
119
|
"remove", "removeDirectory", "recursiveRemove", "rename", "atomicRenameNoReplace", "copy", "exclusiveCopy", "readlink", "truncate",
|
|
120
120
|
"streamingAppend", "randomAccessWrite", "descriptorWriteStream", "retainedResize", "atomicResize", "symlinks", "hardlinks", "permissions", "timestamps",
|
|
@@ -620,6 +620,19 @@ export class MountFileSystem {
|
|
|
620
620
|
await backend.removeFileConditional(location.local, options);
|
|
621
621
|
});
|
|
622
622
|
}
|
|
623
|
+
removeTreeConditional(path, options) {
|
|
624
|
+
return this.operation("removeTreeConditional", path, options, async () => {
|
|
625
|
+
const location = await this.resolve(path, options, { followFinal: false, entry: true, allowMissing: true });
|
|
626
|
+
if (this.protected(location.path))
|
|
627
|
+
fail("EBUSY");
|
|
628
|
+
this.mutable(location);
|
|
629
|
+
const backend = location.mount.backend;
|
|
630
|
+
await requireOwnedMutation(backend, location.local, "atomicTreeRemoval", options);
|
|
631
|
+
if (!backend.removeTreeConditional)
|
|
632
|
+
fail("ENOTSUP");
|
|
633
|
+
await backend.removeTreeConditional(location.local, options);
|
|
634
|
+
});
|
|
635
|
+
}
|
|
623
636
|
createStagedFile(path, name, content, options) {
|
|
624
637
|
return this.operation("createStagedFile", path, options, async () => {
|
|
625
638
|
const location = await this.resolve(path, options, { followFinal: false, entry: true, allowMissing: true, missingDirectory: true });
|
|
@@ -4,7 +4,7 @@ import { registerEntryView } from "./mount/comparison.js";
|
|
|
4
4
|
import { openRetainedResizeFile, retainedResizeCapabilities, ownedMutationCapabilities, requireOwnedMutation } from "./capabilities.js";
|
|
5
5
|
const originals = new WeakMap();
|
|
6
6
|
const operations = new Set([
|
|
7
|
-
"removeEntryConditional", "writeFileConditional", "removeFileConditional", "createStagedFile", "publishStagedFile", "removeStagedFile", "prepareDirectory",
|
|
7
|
+
"removeEntryConditional", "removeTreeConditional", "writeFileConditional", "removeFileConditional", "createStagedFile", "publishStagedFile", "removeStagedFile", "prepareDirectory",
|
|
8
8
|
"access", "appendFile", "canonicalizeMissingTarget", "capabilitiesFor", "chmod", "compareEntry",
|
|
9
9
|
"copyFile", "link", "lstat", "mkdir", "openReadFile", "openResizeFile", "readFile", "readStream", "readdir",
|
|
10
10
|
"readlink", "realpath", "rename", "resizeFile", "rm", "rmdir", "unlink", "stat", "symlink", "truncate", "utimes",
|
|
@@ -198,12 +198,12 @@ export function scopeFileSystem(filesystem, charge, signal, cleanupCharge = char
|
|
|
198
198
|
const options = args.at(-1);
|
|
199
199
|
admit(options && typeof options === "object" && "signal" in options ? options : undefined);
|
|
200
200
|
}
|
|
201
|
-
if (["removeEntryConditional", "writeFileConditional", "removeFileConditional", "createStagedFile", "publishStagedFile", "removeStagedFile", "prepareDirectory"].includes(String(property)))
|
|
201
|
+
if (["removeEntryConditional", "removeTreeConditional", "writeFileConditional", "removeFileConditional", "createStagedFile", "publishStagedFile", "removeStagedFile", "prepareDirectory"].includes(String(property)))
|
|
202
202
|
return (async () => {
|
|
203
203
|
const path = typeof args[0] === "string" ? args[0] : args[0].directory.path;
|
|
204
204
|
const options = args[property === "createStagedFile" ? 3 : property === "publishStagedFile" || property === "writeFileConditional" ? 2 : 1];
|
|
205
205
|
const create = property === "createStagedFile" || (property === "writeFileConditional" || property === "prepareDirectory") && options !== undefined && "expected" in options && options.expected === null;
|
|
206
|
-
await requireOwnedMutation(original, path, property === "removeEntryConditional" ? "atomicEntryRemoval" : property === "prepareDirectory" ? "atomicDirectoryMetadata" : property === "writeFileConditional" || property === "removeFileConditional" ? "atomicFileMutation" : "atomicFileStaging", options ?? {}, create);
|
|
206
|
+
await requireOwnedMutation(original, path, property === "removeEntryConditional" ? "atomicEntryRemoval" : property === "removeTreeConditional" ? "atomicTreeRemoval" : property === "prepareDirectory" ? "atomicDirectoryMetadata" : property === "writeFileConditional" || property === "removeFileConditional" ? "atomicFileMutation" : "atomicFileStaging", options ?? {}, create);
|
|
207
207
|
assertOpen(options);
|
|
208
208
|
return Reflect.apply(method, original, args);
|
|
209
209
|
})();
|
|
@@ -18,7 +18,7 @@ export type PythonFsRequest = {
|
|
|
18
18
|
readonly op: "sync";
|
|
19
19
|
readonly args: readonly [number, boolean];
|
|
20
20
|
} | {
|
|
21
|
-
readonly op: "stat" | "lstat" | "readdir" | "realpath" | "readlink" | "rm" | "rmdir";
|
|
21
|
+
readonly op: "stat" | "lstat" | "readdir" | "realpath" | "readlink" | "rm" | "rmdir" | "rmtree" | "rmtreeSupported";
|
|
22
22
|
readonly args: readonly [string];
|
|
23
23
|
} | {
|
|
24
24
|
readonly op: "rename" | "symlink" | "link";
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { parsePythonFsRequest } from "./request.js";
|
|
2
2
|
import { composeAbortSignals } from "../contracts/abort.js";
|
|
3
3
|
import { FsError } from "../contracts/errors.js";
|
|
4
|
-
import { validatePath } from "../contracts/virtual-path.js";
|
|
4
|
+
import { dirname, validatePath } from "../contracts/virtual-path.js";
|
|
5
5
|
/** Async half of a worker RPC filesystem. Never run this on an Atomics.wait-blocked interpreter thread. */
|
|
6
6
|
export class PythonFileSystem {
|
|
7
7
|
#fs;
|
|
@@ -176,6 +176,25 @@ export class PythonFileSystem {
|
|
|
176
176
|
throw new FsError("ENOTSUP", { syscall: "rmdir", path });
|
|
177
177
|
return fs.rmdir(path, options);
|
|
178
178
|
}
|
|
179
|
+
case "rmtreeSupported":
|
|
180
|
+
case "rmtree": {
|
|
181
|
+
const path = this.#path(request.args[0]);
|
|
182
|
+
const capabilities = fs.capabilitiesFor ? await fs.capabilitiesFor(path, options) : fs.capabilities;
|
|
183
|
+
signal.throwIfAborted();
|
|
184
|
+
if (request.op === "rmtreeSupported")
|
|
185
|
+
return capabilities.readOnly === true || capabilities.atomicTreeRemoval === true && typeof fs.removeTreeConditional === "function";
|
|
186
|
+
if (capabilities.readOnly === true)
|
|
187
|
+
throw new FsError("EROFS", { syscall: "rmtree", path });
|
|
188
|
+
if (capabilities.atomicTreeRemoval !== true || !fs.removeTreeConditional)
|
|
189
|
+
throw new FsError("ENOTSUP", { syscall: "rmtree", path });
|
|
190
|
+
const expected = await fs.lstat(path, options);
|
|
191
|
+
signal.throwIfAborted();
|
|
192
|
+
if (expected.type !== "directory")
|
|
193
|
+
throw new FsError("ENOTDIR", { syscall: "rmtree", path });
|
|
194
|
+
const parent = await fs.stat(dirname(path), options);
|
|
195
|
+
signal.throwIfAborted();
|
|
196
|
+
return fs.removeTreeConditional(path, { ...options, parent, expected });
|
|
197
|
+
}
|
|
179
198
|
case "rename": return fs.rename(this.#path(request.args[0]), this.#path(request.args[1]), options);
|
|
180
199
|
case "readlink":
|
|
181
200
|
if (fs.readlink)
|
|
@@ -2,7 +2,7 @@ import { FsError } from "../contracts/errors.js";
|
|
|
2
2
|
const argumentKinds = {
|
|
3
3
|
open: ["string", "object"], read: ["number", "number", "position"], write: ["number", "bytes", "position"],
|
|
4
4
|
descriptorCapabilities: ["number"], fstat: ["number"], close: ["number"], position: ["number"], ftruncate: ["number", "number"], sync: ["number", "boolean"],
|
|
5
|
-
stat: ["string"], lstat: ["string"], readdir: ["string"], realpath: ["string"], readlink: ["string"], rm: ["string"], rmdir: ["string"],
|
|
5
|
+
stat: ["string"], lstat: ["string"], readdir: ["string"], realpath: ["string"], readlink: ["string"], rm: ["string"], rmdir: ["string"], rmtree: ["string"], rmtreeSupported: ["string"],
|
|
6
6
|
rename: ["string", "string"], symlink: ["string", "string"], link: ["string", "string"], mkdir: ["string", "optionalObject"],
|
|
7
7
|
chmod: ["string", "number"], truncate: ["string", "number"], access: ["string", "number"], utimes: ["string", "number", "number"],
|
|
8
8
|
};
|