@poe-platform/safe-fs 0.1.229 → 0.1.231
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 +1 -0
- package/dist/safe-fs/fs/capabilities.js +2 -1
- package/dist/safe-fs/fs/memory/index.d.ts +2 -1
- package/dist/safe-fs/fs/memory/index.js +59 -23
- package/dist/safe-fs/fs/mount/index.js +8 -2
- package/dist/safe-fs/fs/overlay/index.js +1 -0
- package/package.json +1 -1
|
@@ -54,6 +54,7 @@ export interface FileSystemCapabilities {
|
|
|
54
54
|
readonly streamingRead?: boolean;
|
|
55
55
|
readonly retainedRead?: boolean;
|
|
56
56
|
readonly streamingWrite?: boolean;
|
|
57
|
+
readonly descriptorWriteStream?: boolean;
|
|
57
58
|
readonly [capability: string]: boolean | undefined;
|
|
58
59
|
}
|
|
59
60
|
export interface FsOptions {
|
|
@@ -37,6 +37,7 @@ export function readOnlyCapabilities(capabilities) {
|
|
|
37
37
|
mkdir: false, recursiveMkdir: false, remove: false, removeDirectory: false, recursiveRemove: false,
|
|
38
38
|
rename: false, copy: false, exclusiveCopy: false, truncate: false, streamingAppend: false,
|
|
39
39
|
randomAccessWrite: false, hardlinks: false, permissions: false, timestamps: false,
|
|
40
|
+
descriptorWriteStream: false,
|
|
40
41
|
atomicRename: false, streamingWrite: false,
|
|
41
42
|
});
|
|
42
43
|
}
|
|
@@ -44,7 +45,7 @@ export function quotaCapabilities(capabilities) {
|
|
|
44
45
|
const streamingWrite = requireCapabilities(capabilities.write, capabilities.append, !capabilities.readOnly);
|
|
45
46
|
const streamingAppend = requireCapabilities(capabilities.append, !capabilities.readOnly);
|
|
46
47
|
const { streamingWrite: ignoredWrite, streamingAppend: ignoredAppend, ...rest } = capabilities;
|
|
47
|
-
return Object.freeze({ ...rest,
|
|
48
|
+
return Object.freeze({ ...rest, descriptorWriteStream: false,
|
|
48
49
|
...(streamingWrite === undefined ? {} : { streamingWrite }),
|
|
49
50
|
...(streamingAppend === undefined ? {} : { streamingAppend }),
|
|
50
51
|
});
|
|
@@ -33,6 +33,7 @@ export declare class MemoryFileSystem implements FileSystem {
|
|
|
33
33
|
streamingRead: true;
|
|
34
34
|
retainedRead: true;
|
|
35
35
|
streamingWrite: true;
|
|
36
|
+
readonly descriptorWriteStream: boolean;
|
|
36
37
|
}>;
|
|
37
38
|
private readonly identityScope;
|
|
38
39
|
private nextInode;
|
|
@@ -56,7 +57,7 @@ export declare class MemoryFileSystem implements FileSystem {
|
|
|
56
57
|
private bytes;
|
|
57
58
|
private allocate;
|
|
58
59
|
private openWrite;
|
|
59
|
-
private
|
|
60
|
+
private writeAt;
|
|
60
61
|
readFile(path: string, options?: ReadFileOptions): Promise<Uint8Array>;
|
|
61
62
|
writeFile(path: string, data: Uint8Array, options?: WriteFileOptions): Promise<void>;
|
|
62
63
|
appendFile(path: string, data: Uint8Array, options?: AppendFileOptions): Promise<void>;
|
|
@@ -49,22 +49,25 @@ const compareOwnedMemory = async (own, peer, options) => {
|
|
|
49
49
|
return qualified ? "distinct" : "unknown";
|
|
50
50
|
};
|
|
51
51
|
export class MemoryFileSystem {
|
|
52
|
-
capabilities =
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
52
|
+
capabilities = ((filesystem) => {
|
|
53
|
+
return Object.freeze({
|
|
54
|
+
read: true, stat: true, readdir: true, realpath: true, access: true,
|
|
55
|
+
write: true, append: true, exclusiveCreate: true, explicitDirectories: true, implicitDirectories: false,
|
|
56
|
+
mkdir: true, recursiveMkdir: true, remove: true, removeDirectory: true, recursiveRemove: true,
|
|
57
|
+
rename: true, copy: true, exclusiveCopy: true, readlink: true, truncate: true,
|
|
58
|
+
streamingAppend: true, randomAccessWrite: true,
|
|
59
|
+
readOnly: false,
|
|
60
|
+
symlinks: true,
|
|
61
|
+
hardlinks: true,
|
|
62
|
+
permissions: true,
|
|
63
|
+
timestamps: true,
|
|
64
|
+
atomicRename: true,
|
|
65
|
+
streamingRead: true,
|
|
66
|
+
retainedRead: true,
|
|
67
|
+
streamingWrite: true,
|
|
68
|
+
get descriptorWriteStream() { return stockDescriptorWrite(filesystem); },
|
|
69
|
+
});
|
|
70
|
+
})(this);
|
|
68
71
|
identityScope = Symbol();
|
|
69
72
|
nextInode = 1;
|
|
70
73
|
root = this.directory(0o755);
|
|
@@ -72,6 +75,7 @@ export class MemoryFileSystem {
|
|
|
72
75
|
const root = this.root;
|
|
73
76
|
ownedStores.set(this, {
|
|
74
77
|
root,
|
|
78
|
+
capabilities: this.capabilities,
|
|
75
79
|
intact: () => this.root === root,
|
|
76
80
|
});
|
|
77
81
|
if (this.compareEntry === memoryImplementation.compareEntry?.value) {
|
|
@@ -252,8 +256,15 @@ export class MemoryFileSystem {
|
|
|
252
256
|
this.changed(location.parent);
|
|
253
257
|
return node;
|
|
254
258
|
}
|
|
255
|
-
|
|
256
|
-
|
|
259
|
+
writeAt(node, data, position, syscall, path) {
|
|
260
|
+
if (data.byteLength === 0) {
|
|
261
|
+
this.changed(node);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const end = position + data.byteLength;
|
|
265
|
+
if (!Number.isSafeInteger(end))
|
|
266
|
+
this.fail("EFBIG", syscall, path);
|
|
267
|
+
const length = Math.max(node.data.byteLength, end);
|
|
257
268
|
let storage;
|
|
258
269
|
if (length <= node.data.buffer.byteLength) {
|
|
259
270
|
storage = new Uint8Array(node.data.buffer);
|
|
@@ -262,7 +273,9 @@ export class MemoryFileSystem {
|
|
|
262
273
|
storage = this.allocate(Math.max(length, node.data.byteLength * 2, 64), syscall, path);
|
|
263
274
|
storage.set(node.data);
|
|
264
275
|
}
|
|
265
|
-
|
|
276
|
+
if (position > node.data.byteLength)
|
|
277
|
+
storage.fill(0, node.data.byteLength, position);
|
|
278
|
+
storage.set(data, position);
|
|
266
279
|
node.data = storage.subarray(0, length);
|
|
267
280
|
this.changed(node);
|
|
268
281
|
}
|
|
@@ -282,7 +295,7 @@ export class MemoryFileSystem {
|
|
|
282
295
|
const copied = this.bytes(data);
|
|
283
296
|
const node = this.openWrite(path, options, "writeFile");
|
|
284
297
|
if (options.flag === "a" || options.flag === "ax")
|
|
285
|
-
this.
|
|
298
|
+
this.writeAt(node, copied, node.data.byteLength, "writeFile", path);
|
|
286
299
|
else {
|
|
287
300
|
node.data = copied;
|
|
288
301
|
this.changed(node);
|
|
@@ -291,7 +304,8 @@ export class MemoryFileSystem {
|
|
|
291
304
|
async appendFile(path, data, options = {}) {
|
|
292
305
|
options.signal?.throwIfAborted();
|
|
293
306
|
const copied = this.bytes(data);
|
|
294
|
-
this.
|
|
307
|
+
const node = this.openWrite(path, { ...options, flag: "a" }, "appendFile");
|
|
308
|
+
this.writeAt(node, copied, node.data.byteLength, "appendFile", path);
|
|
295
309
|
}
|
|
296
310
|
async stat(path, options = {}) {
|
|
297
311
|
options.signal?.throwIfAborted();
|
|
@@ -589,18 +603,40 @@ export class MemoryFileSystem {
|
|
|
589
603
|
async writeStream(path, source, options = {}) {
|
|
590
604
|
options.signal?.throwIfAborted();
|
|
591
605
|
const node = this.openWrite(path, options, "writeStream");
|
|
592
|
-
|
|
606
|
+
const append = options.flag === "a" || options.flag === "ax";
|
|
607
|
+
let position = 0;
|
|
608
|
+
if (!append) {
|
|
593
609
|
node.data = new Uint8Array();
|
|
594
610
|
this.changed(node);
|
|
595
611
|
}
|
|
596
612
|
for await (const chunk of source) {
|
|
597
613
|
options.signal?.throwIfAborted();
|
|
598
|
-
|
|
614
|
+
if (!(chunk instanceof Uint8Array))
|
|
615
|
+
throw new TypeError("Memory files require Uint8Array data");
|
|
616
|
+
this.writeAt(node, chunk, append ? node.data.byteLength : position, "writeStream", path);
|
|
617
|
+
if (!append)
|
|
618
|
+
position += chunk.byteLength;
|
|
599
619
|
}
|
|
600
620
|
options.signal?.throwIfAborted();
|
|
601
621
|
}
|
|
602
622
|
}
|
|
603
623
|
const memoryImplementation = Object.getOwnPropertyDescriptors(MemoryFileSystem.prototype);
|
|
624
|
+
function stockDescriptorWrite(filesystem) {
|
|
625
|
+
if (Object.getPrototypeOf(filesystem) !== MemoryFileSystem.prototype)
|
|
626
|
+
return false;
|
|
627
|
+
const owner = ownedStores.get(filesystem);
|
|
628
|
+
if (!owner || Object.getOwnPropertyDescriptor(filesystem, "root")?.value !== owner.root
|
|
629
|
+
|| Object.getOwnPropertyDescriptor(filesystem, "capabilities")?.value !== owner.capabilities)
|
|
630
|
+
return false;
|
|
631
|
+
for (const name of ["writeStream", "writeFile", "appendFile", "access", "stat", "lstat", "realpath",
|
|
632
|
+
"openWrite", "resolve", "permission", "validatePath", "mode", "bytes", "allocate", "changed", "metadata", "integer", "writeAt", "fail"]) {
|
|
633
|
+
const descriptor = Object.getOwnPropertyDescriptor(filesystem, name)
|
|
634
|
+
?? Object.getOwnPropertyDescriptor(MemoryFileSystem.prototype, name);
|
|
635
|
+
if (!descriptor || !("value" in descriptor) || descriptor.value !== memoryImplementation[name]?.value)
|
|
636
|
+
return false;
|
|
637
|
+
}
|
|
638
|
+
return true;
|
|
639
|
+
}
|
|
604
640
|
export function createMemoryFileSystem() {
|
|
605
641
|
return new MemoryFileSystem();
|
|
606
642
|
}
|
|
@@ -85,12 +85,15 @@ export class MountFileSystem {
|
|
|
85
85
|
const common = (capability) => {
|
|
86
86
|
const optional = {
|
|
87
87
|
symlinks: ["symlink", "readlink"], hardlinks: ["link"], permissions: ["chmod"], timestamps: ["utimes"], readlink: ["readlink"],
|
|
88
|
+
descriptorWriteStream: ["writeStream"],
|
|
88
89
|
};
|
|
89
90
|
const values = mounts.map(({ backend }) => {
|
|
90
91
|
if (backend.capabilities.readOnly === true
|
|
91
92
|
&& !["read", "stat", "readdir", "realpath", "access", "readlink", "explicitDirectories", "implicitDirectories"].includes(capability))
|
|
92
93
|
return false;
|
|
93
94
|
const declared = backend.capabilities[capability];
|
|
95
|
+
if (capability === "descriptorWriteStream" && backend.capabilities.streamingWrite === false)
|
|
96
|
+
return false;
|
|
94
97
|
return declared === true && optional[capability]?.some(method => typeof backend[method] !== "function") ? false : declared;
|
|
95
98
|
});
|
|
96
99
|
if (["rename", "copy", "exclusiveCopy"].includes(capability) && mounts.length > 1)
|
|
@@ -101,7 +104,7 @@ export class MountFileSystem {
|
|
|
101
104
|
"read", "stat", "readdir", "realpath", "access",
|
|
102
105
|
"write", "append", "exclusiveCreate", "explicitDirectories", "implicitDirectories", "mkdir", "recursiveMkdir",
|
|
103
106
|
"remove", "removeDirectory", "recursiveRemove", "rename", "copy", "exclusiveCopy", "readlink", "truncate",
|
|
104
|
-
"streamingAppend", "randomAccessWrite", "symlinks", "hardlinks", "permissions", "timestamps",
|
|
107
|
+
"streamingAppend", "randomAccessWrite", "descriptorWriteStream", "symlinks", "hardlinks", "permissions", "timestamps",
|
|
105
108
|
].map(capability => [capability, common(capability)]).filter(([, value]) => value !== undefined));
|
|
106
109
|
this.capabilities = Object.freeze({
|
|
107
110
|
get snapshotRmdir() { return mounts.some(({ backend }) => backend.capabilities.snapshotRmdir === true); },
|
|
@@ -120,8 +123,11 @@ export class MountFileSystem {
|
|
|
120
123
|
async capabilitiesFor(path, options = {}) {
|
|
121
124
|
return this.operation("capabilitiesFor", path, options, async () => {
|
|
122
125
|
const location = await this.resolve(path, options, { allowMissing: true });
|
|
123
|
-
const
|
|
126
|
+
const observed = await location.mount.backend.capabilitiesFor?.(location.local, options)
|
|
124
127
|
?? location.mount.backend.capabilities;
|
|
128
|
+
const declared = observed.descriptorWriteStream === true
|
|
129
|
+
&& (typeof location.mount.backend.writeStream !== "function" || observed.readOnly === true || observed.streamingWrite === false)
|
|
130
|
+
? { ...observed, descriptorWriteStream: false } : observed;
|
|
125
131
|
const capabilities = location.synthetic ? { ...declared, retainedRead: false }
|
|
126
132
|
: retainedReadCapabilities(location.mount.backend, declared);
|
|
127
133
|
if (location.synthetic)
|
|
@@ -118,6 +118,7 @@ export class OverlayFileSystem {
|
|
|
118
118
|
...(upper.readOnly === undefined ? {} : { readOnly: upper.readOnly }),
|
|
119
119
|
...(effectiveAppend === undefined ? {} : { append: effectiveAppend }),
|
|
120
120
|
atomicRename: false,
|
|
121
|
+
descriptorWriteStream: false,
|
|
121
122
|
hardlinks: false,
|
|
122
123
|
symlinks: writable && this.#upper.capabilities.symlinks === true
|
|
123
124
|
&& typeof this.#upper.symlink === "function" && typeof this.#upper.readlink === "function"
|