@poe-platform/safe-fs 0.1.732 → 0.1.734
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.
|
@@ -1,6 +1,44 @@
|
|
|
1
1
|
import { FsError, toFsError } from "../contracts/errors.js";
|
|
2
2
|
import { finishCleanup } from "../contracts/cleanup.js";
|
|
3
3
|
import { inspectStagingBindings } from "./staging-ancestry.js";
|
|
4
|
+
const signalWaiters = new WeakMap();
|
|
5
|
+
function getSignalWaiters(signal) {
|
|
6
|
+
let entry = signalWaiters.get(signal);
|
|
7
|
+
if (!entry) {
|
|
8
|
+
const waiters = new Set();
|
|
9
|
+
const listener = () => {
|
|
10
|
+
signalWaiters.delete(signal);
|
|
11
|
+
signal.removeEventListener("abort", listener);
|
|
12
|
+
const pending = [...waiters];
|
|
13
|
+
waiters.clear();
|
|
14
|
+
for (const fn of pending)
|
|
15
|
+
fn();
|
|
16
|
+
};
|
|
17
|
+
entry = { waiters, listener };
|
|
18
|
+
signalWaiters.set(signal, entry);
|
|
19
|
+
signal.addEventListener("abort", listener, { once: true });
|
|
20
|
+
}
|
|
21
|
+
return entry;
|
|
22
|
+
}
|
|
23
|
+
function releaseSignalWaiter(signal, entry, abort) {
|
|
24
|
+
entry.waiters.delete(abort);
|
|
25
|
+
if (entry.waiters.size === 0 && signalWaiters.get(signal) === entry) {
|
|
26
|
+
signalWaiters.delete(signal);
|
|
27
|
+
signal.removeEventListener("abort", entry.listener);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function awaitWithSignal(promise, signal) {
|
|
31
|
+
if (!signal)
|
|
32
|
+
return promise;
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
const entry = getSignalWaiters(signal);
|
|
35
|
+
const abort = () => { releaseSignalWaiter(signal, entry, abort); reject(signal.reason); };
|
|
36
|
+
entry.waiters.add(abort);
|
|
37
|
+
promise.then(value => { releaseSignalWaiter(signal, entry, abort); resolve(value); }, error => { releaseSignalWaiter(signal, entry, abort); reject(error); });
|
|
38
|
+
if (signal.aborted)
|
|
39
|
+
abort();
|
|
40
|
+
});
|
|
41
|
+
}
|
|
4
42
|
export function retainedReadCapabilities(filesystem, capabilities = filesystem.capabilities) {
|
|
5
43
|
return typeof filesystem.openReadFile === "function" ? capabilities : { ...capabilities, retainedRead: false };
|
|
6
44
|
}
|
|
@@ -16,13 +54,7 @@ export async function openRetainedReadFile(filesystem, path, options) {
|
|
|
16
54
|
const query = filesystem.capabilitiesFor;
|
|
17
55
|
signal?.throwIfAborted();
|
|
18
56
|
const metadata = Promise.resolve(query == null ? undefined : Reflect.apply(query, filesystem, [path, options]));
|
|
19
|
-
const queried = await (
|
|
20
|
-
const abort = () => { signal.removeEventListener("abort", abort); reject(signal.reason); };
|
|
21
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
22
|
-
metadata.then(value => { signal.removeEventListener("abort", abort); resolve(value); }, error => { signal.removeEventListener("abort", abort); reject(error); });
|
|
23
|
-
if (signal.aborted)
|
|
24
|
-
abort();
|
|
25
|
-
}) : metadata);
|
|
57
|
+
const queried = await awaitWithSignal(metadata, signal);
|
|
26
58
|
signal?.throwIfAborted();
|
|
27
59
|
const capabilities = queried ?? filesystem.capabilities;
|
|
28
60
|
signal?.throwIfAborted();
|
|
@@ -68,13 +100,7 @@ export async function openRetainedResizeFile(filesystem, path, options) {
|
|
|
68
100
|
const intent = { ...(signal === undefined ? {} : { signal }), create: options.create === true };
|
|
69
101
|
signal?.throwIfAborted();
|
|
70
102
|
const metadata = Promise.resolve(query === undefined ? filesystem.capabilities : Reflect.apply(query, filesystem, [path, intent]));
|
|
71
|
-
const capabilities = await (
|
|
72
|
-
const abort = () => { signal.removeEventListener("abort", abort); reject(signal.reason); };
|
|
73
|
-
signal.addEventListener("abort", abort, { once: true });
|
|
74
|
-
metadata.then(value => { signal.removeEventListener("abort", abort); resolve(value); }, error => { signal.removeEventListener("abort", abort); reject(error); });
|
|
75
|
-
if (signal.aborted)
|
|
76
|
-
abort();
|
|
77
|
-
}) : metadata);
|
|
103
|
+
const capabilities = await awaitWithSignal(metadata, signal);
|
|
78
104
|
signal?.throwIfAborted();
|
|
79
105
|
const readOnly = capabilities.readOnly;
|
|
80
106
|
signal?.throwIfAborted();
|
|
@@ -257,8 +257,8 @@ class FastDirectoryEntriesMap {
|
|
|
257
257
|
table[slot] = -2;
|
|
258
258
|
keys[idx] = "";
|
|
259
259
|
this._vals[idx] = DUMMY_POOL_FILE_NODE;
|
|
260
|
-
|
|
261
|
-
|
|
260
|
+
// Keep deleted indices until compaction: reusing the tail would let
|
|
261
|
+
// tombstones fill the table without reaching set()'s rebuild threshold.
|
|
262
262
|
this.size--;
|
|
263
263
|
return true;
|
|
264
264
|
}
|
|
@@ -81,9 +81,20 @@ export function scopeFileSystem(filesystem, charge, signal, cleanupCharge = char
|
|
|
81
81
|
close() { closed = true; return close(); },
|
|
82
82
|
};
|
|
83
83
|
};
|
|
84
|
+
let cachedCallerSignal;
|
|
85
|
+
let cachedCombinedSignal;
|
|
86
|
+
const combineSignal = (callerSignal) => {
|
|
87
|
+
if (!callerSignal || callerSignal === signal)
|
|
88
|
+
return signal;
|
|
89
|
+
if (callerSignal === cachedCallerSignal && cachedCombinedSignal)
|
|
90
|
+
return cachedCombinedSignal;
|
|
91
|
+
cachedCallerSignal = callerSignal;
|
|
92
|
+
cachedCombinedSignal = AbortSignal.any([signal, callerSignal]);
|
|
93
|
+
return cachedCombinedSignal;
|
|
94
|
+
};
|
|
84
95
|
const resizeOptions = (options) => {
|
|
85
96
|
const scopedOptions = {
|
|
86
|
-
...options, signal:
|
|
97
|
+
...options, signal: combineSignal(options.signal),
|
|
87
98
|
};
|
|
88
99
|
scopedOptions.signal.throwIfAborted();
|
|
89
100
|
return scopedOptions;
|
|
@@ -253,7 +264,7 @@ export function scopeFileSystem(filesystem, charge, signal, cleanupCharge = char
|
|
|
253
264
|
if (property === "createStagedFile")
|
|
254
265
|
args[3] = snapshotStagingCreation(args[3], args[0]);
|
|
255
266
|
const callerSignal = args[property === "createStagedFile" ? 3 : 2]?.signal;
|
|
256
|
-
stagingSignal =
|
|
267
|
+
stagingSignal = combineSignal(callerSignal);
|
|
257
268
|
stagingSignal.throwIfAborted();
|
|
258
269
|
}
|
|
259
270
|
if (operations.has(property)) {
|
|
@@ -496,6 +507,10 @@ export function scopeFileSystem(filesystem, charge, signal, cleanupCharge = char
|
|
|
496
507
|
originals.set(view, originalRecord);
|
|
497
508
|
retargeters.set(view, (nextCharge, nextSignal, nextCleanupCharge, nextMaxPathComponents) => {
|
|
498
509
|
charge = nextCharge;
|
|
510
|
+
if (signal !== nextSignal) {
|
|
511
|
+
cachedCallerSignal = undefined;
|
|
512
|
+
cachedCombinedSignal = undefined;
|
|
513
|
+
}
|
|
499
514
|
signal = nextSignal;
|
|
500
515
|
cleanupCharge = nextCleanupCharge;
|
|
501
516
|
maxPathComponents = nextMaxPathComponents ?? originals.get(filesystem)?.maxPathComponents;
|