@poe-platform/safe-fs 0.1.728 → 0.1.729
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/README.md +7 -7
- package/dist/safe-fs/contracts/io.js +140 -114
- package/dist/safe-fs/fs/memory/index.d.ts +36 -0
- package/dist/safe-fs/fs/memory/index.js +563 -127
- package/dist/safe-fs/fs/memory/ledger.d.ts +6 -1
- package/dist/safe-fs/fs/memory/ledger.js +31 -3
- package/dist/safe-fs/fs/overlay/index.js +5 -3
- package/dist/safe-fs/fs/quota/index.js +4 -4
- package/dist/safe-fs/fs/s3/filesystem.js +10 -10
- package/dist/safe-fs/fs/webdav/webdav.js +7 -5
- package/package.json +1 -1
|
@@ -3,17 +3,22 @@ export declare class MemoryLedger {
|
|
|
3
3
|
readonly limits: Readonly<MemoryFileSystemLimits>;
|
|
4
4
|
private retainedBytes;
|
|
5
5
|
private metadataUnits;
|
|
6
|
+
readonly maxFileBytesSmi: number;
|
|
7
|
+
readonly maxRetainedBytesSmi: number;
|
|
8
|
+
readonly maxMetadataUnitsSmi: number;
|
|
9
|
+
readonly hasInfiniteRetained: boolean;
|
|
6
10
|
constructor(limits: Readonly<MemoryFileSystemLimits>);
|
|
7
11
|
get availableBytes(): number;
|
|
12
|
+
canPreallocate64(nameBytes: number): boolean;
|
|
8
13
|
fileSize(length: number, syscall: string, path: string): void;
|
|
9
14
|
check(bytes: number, units: number, syscall: string, path: string): void;
|
|
10
15
|
reserve(bytes: number, units: number, syscall: string, path: string): void;
|
|
11
16
|
release(bytes: number, units: number): void;
|
|
12
17
|
}
|
|
13
18
|
export declare class MemoryAllocation {
|
|
19
|
+
private references;
|
|
14
20
|
readonly data: Uint8Array;
|
|
15
21
|
private readonly ledger;
|
|
16
|
-
private references;
|
|
17
22
|
constructor(data: Uint8Array, ledger: MemoryLedger);
|
|
18
23
|
retain(): void;
|
|
19
24
|
release(): void;
|
|
@@ -1,21 +1,41 @@
|
|
|
1
1
|
import { FsError } from "../../contracts/errors.js";
|
|
2
|
+
const MAX_SMI = 1073741823;
|
|
2
3
|
export class MemoryLedger {
|
|
3
4
|
limits;
|
|
4
5
|
retainedBytes = 0;
|
|
5
6
|
metadataUnits = 0;
|
|
7
|
+
maxFileBytesSmi;
|
|
8
|
+
maxRetainedBytesSmi;
|
|
9
|
+
maxMetadataUnitsSmi;
|
|
10
|
+
hasInfiniteRetained;
|
|
6
11
|
constructor(limits) {
|
|
7
12
|
this.limits = limits;
|
|
13
|
+
this.maxFileBytesSmi = limits.maxFileBytes >= MAX_SMI ? MAX_SMI : (limits.maxFileBytes | 0);
|
|
14
|
+
this.maxRetainedBytesSmi = limits.maxRetainedBytes >= MAX_SMI ? MAX_SMI : (limits.maxRetainedBytes | 0);
|
|
15
|
+
this.maxMetadataUnitsSmi = limits.maxMetadataUnits >= MAX_SMI ? MAX_SMI : (limits.maxMetadataUnits | 0);
|
|
16
|
+
this.hasInfiniteRetained = limits.maxRetainedBytes === Infinity;
|
|
8
17
|
}
|
|
9
18
|
get availableBytes() {
|
|
10
19
|
const max = this.limits.maxRetainedBytes;
|
|
11
20
|
return max === Infinity ? Infinity : max - this.retainedBytes;
|
|
12
21
|
}
|
|
22
|
+
canPreallocate64(nameBytes) {
|
|
23
|
+
return this.maxFileBytesSmi >= 64 && (this.hasInfiniteRetained || this.maxRetainedBytesSmi - this.retainedBytes > 65536 + nameBytes);
|
|
24
|
+
}
|
|
13
25
|
fileSize(length, syscall, path) {
|
|
26
|
+
if ((length | 0) === length && length >= 0 && length <= this.maxFileBytesSmi)
|
|
27
|
+
return;
|
|
14
28
|
if (!Number.isSafeInteger(length) || length < 0 || length > this.limits.maxFileBytes) {
|
|
15
29
|
throw new FsError("EFBIG", { syscall, path });
|
|
16
30
|
}
|
|
17
31
|
}
|
|
18
32
|
check(bytes, units, syscall, path) {
|
|
33
|
+
if ((bytes | 0) === bytes && bytes >= 0 &&
|
|
34
|
+
(units | 0) === units && units >= 0 &&
|
|
35
|
+
this.retainedBytes <= this.maxRetainedBytesSmi - bytes &&
|
|
36
|
+
this.metadataUnits <= this.maxMetadataUnitsSmi - units) {
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
19
39
|
const maxRetained = this.limits.maxRetainedBytes;
|
|
20
40
|
const maxUnits = this.limits.maxMetadataUnits;
|
|
21
41
|
if (!Number.isSafeInteger(bytes) || bytes < 0 ||
|
|
@@ -26,6 +46,14 @@ export class MemoryLedger {
|
|
|
26
46
|
}
|
|
27
47
|
}
|
|
28
48
|
reserve(bytes, units, syscall, path) {
|
|
49
|
+
if ((bytes | 0) === bytes && bytes >= 0 &&
|
|
50
|
+
(units | 0) === units && units >= 0 &&
|
|
51
|
+
this.retainedBytes <= this.maxRetainedBytesSmi - bytes &&
|
|
52
|
+
this.metadataUnits <= this.maxMetadataUnitsSmi - units) {
|
|
53
|
+
this.retainedBytes = (this.retainedBytes + bytes) | 0;
|
|
54
|
+
this.metadataUnits = (this.metadataUnits + units) | 0;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
29
57
|
this.check(bytes, units, syscall, path);
|
|
30
58
|
this.retainedBytes += bytes;
|
|
31
59
|
this.metadataUnits += units;
|
|
@@ -39,9 +67,6 @@ export class MemoryLedger {
|
|
|
39
67
|
}
|
|
40
68
|
}
|
|
41
69
|
export class MemoryAllocation {
|
|
42
|
-
data;
|
|
43
|
-
ledger;
|
|
44
|
-
references = 1;
|
|
45
70
|
constructor(data, ledger) {
|
|
46
71
|
this.data = data;
|
|
47
72
|
this.ledger = ledger;
|
|
@@ -63,3 +88,6 @@ export class MemoryAllocation {
|
|
|
63
88
|
this.data.fill(0);
|
|
64
89
|
}
|
|
65
90
|
}
|
|
91
|
+
Object.assign(MemoryAllocation.prototype, {
|
|
92
|
+
references: 1,
|
|
93
|
+
});
|
|
@@ -36,7 +36,9 @@ function hasCode(error, code) {
|
|
|
36
36
|
function fail(code, path, message) {
|
|
37
37
|
throw new FsError(code, { syscall: "overlay", path, ...(message === undefined ? {} : { message }) });
|
|
38
38
|
}
|
|
39
|
-
function integer(value, path) {
|
|
39
|
+
function integer(value, path, unlimited = false) {
|
|
40
|
+
if (unlimited && value === Infinity)
|
|
41
|
+
return;
|
|
40
42
|
if (!Number.isSafeInteger(value) || value < 0)
|
|
41
43
|
fail("EINVAL", path);
|
|
42
44
|
}
|
|
@@ -86,7 +88,7 @@ export class OverlayFileSystem {
|
|
|
86
88
|
}, false));
|
|
87
89
|
this.maxBufferBytes = options.maxBufferBytes ?? Infinity;
|
|
88
90
|
if (options.maxBufferBytes !== undefined)
|
|
89
|
-
integer(options.maxBufferBytes, "/");
|
|
91
|
+
integer(options.maxBufferBytes, "/", true);
|
|
90
92
|
const writable = !this.#upper.capabilities.readOnly && this.#upper.capabilities.atomicRename === true;
|
|
91
93
|
const readable = [this.#upper, this.#lower].map((backend) => retainedReadCapabilities(backend).retainedRead);
|
|
92
94
|
const streamingRead = readable.every((capability) => capability === true) ? true
|
|
@@ -520,7 +522,7 @@ export class OverlayFileSystem {
|
|
|
520
522
|
fail("EISDIR", entry.path);
|
|
521
523
|
this.permission(entry, 4);
|
|
522
524
|
if (options.maxBytes !== undefined)
|
|
523
|
-
integer(options.maxBytes, entry.path);
|
|
525
|
+
integer(options.maxBytes, entry.path, true);
|
|
524
526
|
const limit = Math.min(options.maxBytes ?? this.maxBufferBytes, this.maxBufferBytes);
|
|
525
527
|
if (entry.stat.size > limit)
|
|
526
528
|
fail("EFBIG", entry.path);
|
|
@@ -153,15 +153,15 @@ async function existingBytes(fs, path, options) {
|
|
|
153
153
|
}
|
|
154
154
|
}
|
|
155
155
|
export function withFileSystemQuota(fs, options) {
|
|
156
|
-
if (!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 0)
|
|
157
|
-
throw new RangeError("maxBytes must be a nonnegative safe integer");
|
|
156
|
+
if (options.maxBytes !== Infinity && (!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 0))
|
|
157
|
+
throw new RangeError("maxBytes must be a nonnegative safe integer or Infinity");
|
|
158
158
|
const scanLimits = {
|
|
159
159
|
maxScanEntries: options.maxScanEntries ?? Infinity,
|
|
160
160
|
maxScanDepth: options.maxScanDepth ?? Infinity,
|
|
161
161
|
};
|
|
162
162
|
for (const [name, value] of Object.entries({ maxScanEntries: options.maxScanEntries, maxScanDepth: options.maxScanDepth })) {
|
|
163
|
-
if (value !== undefined && (!Number.isSafeInteger(value) || value < 0))
|
|
164
|
-
throw new RangeError(`${name} must be a nonnegative safe integer`);
|
|
163
|
+
if (value !== undefined && value !== Infinity && (!Number.isSafeInteger(value) || value < 0))
|
|
164
|
+
throw new RangeError(`${name} must be a nonnegative safe integer or Infinity`);
|
|
165
165
|
}
|
|
166
166
|
let queue = Promise.resolve();
|
|
167
167
|
let reservedBytes = 0;
|
|
@@ -59,7 +59,7 @@ function fail(code, syscall, path, message) {
|
|
|
59
59
|
throw new FsError(code, { syscall, path, ...(message === undefined ? {} : { message }) });
|
|
60
60
|
}
|
|
61
61
|
function validateLimit(value, name, minimum, maximum = Number.MAX_SAFE_INTEGER) {
|
|
62
|
-
if (!Number.isSafeInteger(value) || value < minimum || value > maximum) {
|
|
62
|
+
if ((value !== Infinity || maximum !== Infinity) && (!Number.isSafeInteger(value) || value < minimum || value > maximum)) {
|
|
63
63
|
throw new FsError("EINVAL", { message: `${name} must be an integer from ${minimum} to ${maximum}` });
|
|
64
64
|
}
|
|
65
65
|
return value;
|
|
@@ -130,14 +130,14 @@ export class S3FileSystem {
|
|
|
130
130
|
this.bucket = options.bucket;
|
|
131
131
|
this.allowRename = options.allowNonAtomicRename ?? true;
|
|
132
132
|
this.pageSize = validateLimit(options.pageSize ?? 1000, "pageSize", 1, 1000);
|
|
133
|
-
this.maxReadBytes = options.maxReadBytes === undefined ? Infinity : validateLimit(options.maxReadBytes, "maxReadBytes", 0);
|
|
134
|
-
this.maxStreamBytes = options.maxStreamBytes === undefined ? Infinity : validateLimit(options.maxStreamBytes, "maxStreamBytes", 0);
|
|
135
|
-
this.maxListEntries = options.maxListEntries === undefined ? Infinity : validateLimit(options.maxListEntries, "maxListEntries", 1);
|
|
136
|
-
this.maxRequests = options.maxRequests === undefined ? Infinity : validateLimit(options.maxRequests, "maxRequests", 1);
|
|
133
|
+
this.maxReadBytes = options.maxReadBytes === undefined ? Infinity : validateLimit(options.maxReadBytes, "maxReadBytes", 0, Infinity);
|
|
134
|
+
this.maxStreamBytes = options.maxStreamBytes === undefined ? Infinity : validateLimit(options.maxStreamBytes, "maxStreamBytes", 0, Infinity);
|
|
135
|
+
this.maxListEntries = options.maxListEntries === undefined ? Infinity : validateLimit(options.maxListEntries, "maxListEntries", 1, Infinity);
|
|
136
|
+
this.maxRequests = options.maxRequests === undefined ? Infinity : validateLimit(options.maxRequests, "maxRequests", 1, Infinity);
|
|
137
137
|
this.removalLimits = {
|
|
138
|
-
maxRequests: validateLimit(options.removalLimits?.maxRequests ??
|
|
139
|
-
maxListEntries: validateLimit(options.removalLimits?.maxListEntries ??
|
|
140
|
-
maxDeleteObjects: validateLimit(options.removalLimits?.maxDeleteObjects ??
|
|
138
|
+
maxRequests: validateLimit(options.removalLimits?.maxRequests ?? Infinity, "removalLimits.maxRequests", 1, Infinity),
|
|
139
|
+
maxListEntries: validateLimit(options.removalLimits?.maxListEntries ?? Infinity, "removalLimits.maxListEntries", 1, Infinity),
|
|
140
|
+
maxDeleteObjects: validateLimit(options.removalLimits?.maxDeleteObjects ?? Infinity, "removalLimits.maxDeleteObjects", 1, Infinity),
|
|
141
141
|
};
|
|
142
142
|
this.capabilities = Object.freeze({
|
|
143
143
|
open: false,
|
|
@@ -427,7 +427,7 @@ export class S3FileSystem {
|
|
|
427
427
|
return this.stat(path, options);
|
|
428
428
|
}
|
|
429
429
|
async body(output, path, options) {
|
|
430
|
-
const limit = Math.min(this.maxReadBytes, options.maxBytes === undefined ? this.maxReadBytes : validateLimit(options.maxBytes, "maxBytes", 0));
|
|
430
|
+
const limit = Math.min(this.maxReadBytes, options.maxBytes === undefined ? this.maxReadBytes : validateLimit(options.maxBytes, "maxBytes", 0, Infinity));
|
|
431
431
|
let iterator;
|
|
432
432
|
return this.call("readFile", path, options, async () => {
|
|
433
433
|
if (output.Body && Symbol.asyncIterator in output.Body)
|
|
@@ -473,7 +473,7 @@ export class S3FileSystem {
|
|
|
473
473
|
async readFile(input, options = {}) {
|
|
474
474
|
const path = this.path(input);
|
|
475
475
|
if (options.maxBytes !== undefined)
|
|
476
|
-
validateLimit(options.maxBytes, "maxBytes", 0);
|
|
476
|
+
validateLimit(options.maxBytes, "maxBytes", 0, Infinity);
|
|
477
477
|
const info = await this.stat(input, options);
|
|
478
478
|
if (info.type === "directory")
|
|
479
479
|
fail("EISDIR", "readFile", path);
|
|
@@ -86,7 +86,9 @@ const resourceIdBody = '<?xml version="1.0" encoding="utf-8"?>'
|
|
|
86
86
|
function fail(code, syscall, path, message) {
|
|
87
87
|
throw new FsError(code, { syscall, path, ...(message === undefined ? {} : { message }) });
|
|
88
88
|
}
|
|
89
|
-
function positive(value, name, zero = false) {
|
|
89
|
+
function positive(value, name, zero = false, unlimited = false) {
|
|
90
|
+
if (unlimited && value === Infinity)
|
|
91
|
+
return value;
|
|
90
92
|
if (!Number.isSafeInteger(value) || value < (zero ? 0 : 1)) {
|
|
91
93
|
throw new FsError("EINVAL", { message: `${name} must be a ${zero ? "nonnegative" : "positive"} safe integer` });
|
|
92
94
|
}
|
|
@@ -286,9 +288,9 @@ export class WebDavFileSystem {
|
|
|
286
288
|
removeDirectory: this.atomicEmptyDirectory !== undefined,
|
|
287
289
|
streamingAppend: this.requestStreamSupport !== false,
|
|
288
290
|
});
|
|
289
|
-
this.maxResponseBytes = options.maxResponseBytes === undefined ? 16 * 1024 * 1024 : positive(options.maxResponseBytes, "maxResponseBytes");
|
|
290
|
-
this.maxXmlBytes = options.maxXmlBytes === undefined ? 1024 * 1024 : positive(options.maxXmlBytes, "maxXmlBytes");
|
|
291
|
-
this.maxEntries = options.maxEntries === undefined ? Infinity : positive(options.maxEntries, "maxEntries");
|
|
291
|
+
this.maxResponseBytes = options.maxResponseBytes === undefined ? 16 * 1024 * 1024 : positive(options.maxResponseBytes, "maxResponseBytes", false, true);
|
|
292
|
+
this.maxXmlBytes = options.maxXmlBytes === undefined ? 1024 * 1024 : positive(options.maxXmlBytes, "maxXmlBytes", false, true);
|
|
293
|
+
this.maxEntries = options.maxEntries === undefined ? Infinity : positive(options.maxEntries, "maxEntries", false, true);
|
|
292
294
|
this.timeoutMs = options.timeoutMs === undefined ? undefined : positive(options.timeoutMs, "timeoutMs");
|
|
293
295
|
this.overwritePolicy = options.overwritePolicy ?? "lock";
|
|
294
296
|
this.configuredComparison = options.compareEntry !== undefined;
|
|
@@ -867,7 +869,7 @@ export class WebDavFileSystem {
|
|
|
867
869
|
}
|
|
868
870
|
async readFile(path, options = {}) {
|
|
869
871
|
const normalized = normalize(path);
|
|
870
|
-
const limit = Math.min(this.maxResponseBytes, options.maxBytes === undefined ? this.maxResponseBytes : positive(options.maxBytes, "maxBytes", true));
|
|
872
|
+
const limit = Math.min(this.maxResponseBytes, options.maxBytes === undefined ? this.maxResponseBytes : positive(options.maxBytes, "maxBytes", true, true));
|
|
871
873
|
if ((await this.stat(path, options)).type === "directory")
|
|
872
874
|
fail("EISDIR", "readFile", path);
|
|
873
875
|
return this.request("GET", normalized, options, { headers: { "Accept-Encoding": "identity" } }, async (response, signal) => {
|