@volter/tabnode 0.5.32 → 0.5.34
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/assets/{runtime-worker-g2I0tMiC.js → runtime-worker-DsSAHE2C.js} +64 -14
- package/dist/index.mjs +61 -4
- package/dist/runtime.d.ts +2 -1
- package/dist/virtual-fs.d.ts +9 -0
- package/package.json +1 -1
- package/src/node-lib/binding/fs.ts +24 -0
- package/src/runtime.ts +7 -6
- package/src/virtual-fs.ts +42 -0
|
@@ -8639,6 +8639,7 @@ function createFsBindings(Bytes = () => Buffer$9) {
|
|
|
8639
8639
|
// ---- writing ------------------------------------------------------------
|
|
8640
8640
|
writeBuffer(fd2, buffer2, offset2, length, position, req) {
|
|
8641
8641
|
return answer(req, () => {
|
|
8642
|
+
var _a3;
|
|
8642
8643
|
const stream = handleForFd(fd2);
|
|
8643
8644
|
if (stream instanceof LibuvStreamWrap) {
|
|
8644
8645
|
if (position !== null && position !== void 0 && position >= 0) throw createNodeError$1("ESPIPE", "write", String(fd2));
|
|
@@ -8651,6 +8652,23 @@ function createFsBindings(Bytes = () => Buffer$9) {
|
|
|
8651
8652
|
if ((file.flags & 3) === 0) throw createNodeError$1("EBADF", "write", file.path);
|
|
8652
8653
|
const tree2 = file.tree;
|
|
8653
8654
|
const slice = buffer2.subarray(offset2, offset2 + length);
|
|
8655
|
+
const held = ((_a3 = file.cached) == null ? void 0 : _a3.length) ?? (tree2.existsSync(file.path) ? tree2.statSync(file.path).size : 0);
|
|
8656
|
+
const end = (file.flags & flagBits().append) !== 0 ? held : position === null || position === void 0 || position < 0 ? file.position : position;
|
|
8657
|
+
if (end === held && typeof tree2.appendFileSync === "function") {
|
|
8658
|
+
tree2.appendFileSync(file.path, slice);
|
|
8659
|
+
if (file.cached !== void 0) {
|
|
8660
|
+
let room = file.room;
|
|
8661
|
+
if (!room || file.cached.buffer !== room.buffer || file.cached.byteOffset !== 0 || room.byteLength < held + slice.length) {
|
|
8662
|
+
room = new Uint8Array(Math.max(held + slice.length, held * 2, 4096));
|
|
8663
|
+
room.set(file.cached, 0);
|
|
8664
|
+
}
|
|
8665
|
+
room.set(slice, held);
|
|
8666
|
+
file.cached = room.subarray(0, held + slice.length);
|
|
8667
|
+
file.room = room;
|
|
8668
|
+
}
|
|
8669
|
+
if (position === null || position === void 0 || position < 0) file.position = end + slice.length;
|
|
8670
|
+
return slice.length;
|
|
8671
|
+
}
|
|
8654
8672
|
const existing = file.cached ?? (tree2.existsSync(file.path) ? tree2.readFileSync(file.path) : new Uint8Array(0));
|
|
8655
8673
|
const at3 = (file.flags & flagBits().append) !== 0 ? existing.length : position === null || position === void 0 || position < 0 ? file.position : position;
|
|
8656
8674
|
const size = Math.max(existing.length, at3 + slice.length);
|
|
@@ -21136,17 +21154,10 @@ var implementation$1 = function bind2(that) {
|
|
|
21136
21154
|
var implementation = implementation$1;
|
|
21137
21155
|
var functionBind = Function.prototype.bind || implementation;
|
|
21138
21156
|
var functionCall = Function.prototype.call;
|
|
21139
|
-
var functionApply;
|
|
21140
|
-
var hasRequiredFunctionApply;
|
|
21141
|
-
function requireFunctionApply() {
|
|
21142
|
-
if (hasRequiredFunctionApply) return functionApply;
|
|
21143
|
-
hasRequiredFunctionApply = 1;
|
|
21144
|
-
functionApply = Function.prototype.apply;
|
|
21145
|
-
return functionApply;
|
|
21146
|
-
}
|
|
21157
|
+
var functionApply = Function.prototype.apply;
|
|
21147
21158
|
var reflectApply = typeof Reflect !== "undefined" && Reflect && Reflect.apply;
|
|
21148
21159
|
var bind$2 = functionBind;
|
|
21149
|
-
var $apply$1 =
|
|
21160
|
+
var $apply$1 = functionApply;
|
|
21150
21161
|
var $call$2 = functionCall;
|
|
21151
21162
|
var $reflectApply = reflectApply;
|
|
21152
21163
|
var actualApply = $reflectApply || bind$2.call($call$2, $apply$1);
|
|
@@ -21266,7 +21277,7 @@ var hasSymbols = requireHasSymbols()();
|
|
|
21266
21277
|
var getProto = requireGetProto();
|
|
21267
21278
|
var $ObjectGPO = requireObject_getPrototypeOf();
|
|
21268
21279
|
var $ReflectGPO = requireReflect_getPrototypeOf();
|
|
21269
|
-
var $apply =
|
|
21280
|
+
var $apply = functionApply;
|
|
21270
21281
|
var $call = functionCall;
|
|
21271
21282
|
var needsEval = {};
|
|
21272
21283
|
var TypedArray = typeof Uint8Array === "undefined" || !getProto ? undefined$1 : getProto(Uint8Array);
|
|
@@ -21907,7 +21918,7 @@ function requireApplyBind() {
|
|
|
21907
21918
|
if (hasRequiredApplyBind) return applyBind;
|
|
21908
21919
|
hasRequiredApplyBind = 1;
|
|
21909
21920
|
var bind3 = functionBind;
|
|
21910
|
-
var $apply2 =
|
|
21921
|
+
var $apply2 = functionApply;
|
|
21911
21922
|
var actualApply$1 = actualApply;
|
|
21912
21923
|
applyBind = function applyBind2() {
|
|
21913
21924
|
return actualApply$1(bind3, $apply2, arguments);
|
|
@@ -31697,6 +31708,47 @@ class VirtualFS {
|
|
|
31697
31708
|
writeFileSync(path2, data2) {
|
|
31698
31709
|
this.writeFileSyncInternal(path2, data2, true);
|
|
31699
31710
|
}
|
|
31711
|
+
/**
|
|
31712
|
+
* Adds bytes at a file's end, creating it. A file grown a piece at a time
|
|
31713
|
+
* (a write stream's chunks) is kept in a buffer with room to grow, so an
|
|
31714
|
+
* append copies only what it adds: writing the whole file again for each
|
|
31715
|
+
* 1 KB chunk of a 50 MB file copied about a terabyte. A tree that keeps its
|
|
31716
|
+
* files elsewhere (its own writeFileSync) is appended to through its own
|
|
31717
|
+
* read and write unless it answers appends itself.
|
|
31718
|
+
*/
|
|
31719
|
+
appendFileSync(path2, data2) {
|
|
31720
|
+
var _a3;
|
|
31721
|
+
const bytes2 = typeof data2 === "string" ? this.encoder.encode(data2) : data2;
|
|
31722
|
+
if (this.writeFileSync !== VirtualFS.prototype.writeFileSync) {
|
|
31723
|
+
const existing = this.existsSync(path2) ? this.readFileSync(path2) : new Uint8Array(0);
|
|
31724
|
+
const whole = new Uint8Array(existing.byteLength + bytes2.byteLength);
|
|
31725
|
+
whole.set(existing, 0);
|
|
31726
|
+
whole.set(bytes2, existing.byteLength);
|
|
31727
|
+
this.writeFileSync(path2, whole);
|
|
31728
|
+
return;
|
|
31729
|
+
}
|
|
31730
|
+
const normalized = this.normalizePath(path2);
|
|
31731
|
+
const node = this.getNode(normalized);
|
|
31732
|
+
if (!node) {
|
|
31733
|
+
this.writeFileSyncInternal(normalized, bytes2.slice(), true);
|
|
31734
|
+
return;
|
|
31735
|
+
}
|
|
31736
|
+
if (node.type !== "file") throw createNodeError("EISDIR", "open", path2);
|
|
31737
|
+
this.assertWritable(normalized, "open");
|
|
31738
|
+
const current3 = node.content ?? new Uint8Array(0);
|
|
31739
|
+
const length = current3.byteLength + bytes2.byteLength;
|
|
31740
|
+
let room = node.room;
|
|
31741
|
+
if (!room || current3.buffer !== room.buffer || current3.byteOffset !== 0 || room.byteLength < length) {
|
|
31742
|
+
room = new Uint8Array(Math.max(length, current3.byteLength * 2, 4096));
|
|
31743
|
+
room.set(current3, 0);
|
|
31744
|
+
}
|
|
31745
|
+
room.set(bytes2, current3.byteLength);
|
|
31746
|
+
node.content = room.subarray(0, length);
|
|
31747
|
+
node.room = room;
|
|
31748
|
+
node.mtime = Date.now();
|
|
31749
|
+
this.notifyWatchers(normalized, "change");
|
|
31750
|
+
if ((_a3 = this.eventListeners.get("change")) == null ? void 0 : _a3.size) this.emit("change", normalized, this.decoder.decode(node.content));
|
|
31751
|
+
}
|
|
31700
31752
|
/**
|
|
31701
31753
|
* Create directory, optionally with recursive parent creation
|
|
31702
31754
|
*/
|
|
@@ -79675,9 +79727,7 @@ function createRequire(vfs2, fsShim, process2, currentDir, moduleCache, options,
|
|
|
79675
79727
|
if (prepared) {
|
|
79676
79728
|
const body = __substrateScopeGlobalCalls(prepareModuleCode(rawCode, resolvedPath));
|
|
79677
79729
|
try {
|
|
79678
|
-
|
|
79679
|
-
vfs2.writeFileSync(partial, body);
|
|
79680
|
-
vfs2.renameSync(partial, prepared);
|
|
79730
|
+
vfs2.writeFileSync(prepared, body);
|
|
79681
79731
|
} catch {
|
|
79682
79732
|
}
|
|
79683
79733
|
runModuleBody(module, body, resolvedPath, dirname$1, true);
|
package/dist/index.mjs
CHANGED
|
@@ -8767,6 +8767,7 @@ function createFsBindings(Bytes = () => Buffer$9) {
|
|
|
8767
8767
|
// ---- writing ------------------------------------------------------------
|
|
8768
8768
|
writeBuffer(fd, buffer2, offset2, length, position, req) {
|
|
8769
8769
|
return answer(req, () => {
|
|
8770
|
+
var _a2;
|
|
8770
8771
|
const stream = handleForFd(fd);
|
|
8771
8772
|
if (stream instanceof LibuvStreamWrap) {
|
|
8772
8773
|
if (position !== null && position !== void 0 && position >= 0) throw createNodeError$1("ESPIPE", "write", String(fd));
|
|
@@ -8779,6 +8780,23 @@ function createFsBindings(Bytes = () => Buffer$9) {
|
|
|
8779
8780
|
if ((file.flags & 3) === 0) throw createNodeError$1("EBADF", "write", file.path);
|
|
8780
8781
|
const tree2 = file.tree;
|
|
8781
8782
|
const slice = buffer2.subarray(offset2, offset2 + length);
|
|
8783
|
+
const held = ((_a2 = file.cached) == null ? void 0 : _a2.length) ?? (tree2.existsSync(file.path) ? tree2.statSync(file.path).size : 0);
|
|
8784
|
+
const end = (file.flags & flagBits().append) !== 0 ? held : position === null || position === void 0 || position < 0 ? file.position : position;
|
|
8785
|
+
if (end === held && typeof tree2.appendFileSync === "function") {
|
|
8786
|
+
tree2.appendFileSync(file.path, slice);
|
|
8787
|
+
if (file.cached !== void 0) {
|
|
8788
|
+
let room = file.room;
|
|
8789
|
+
if (!room || file.cached.buffer !== room.buffer || file.cached.byteOffset !== 0 || room.byteLength < held + slice.length) {
|
|
8790
|
+
room = new Uint8Array(Math.max(held + slice.length, held * 2, 4096));
|
|
8791
|
+
room.set(file.cached, 0);
|
|
8792
|
+
}
|
|
8793
|
+
room.set(slice, held);
|
|
8794
|
+
file.cached = room.subarray(0, held + slice.length);
|
|
8795
|
+
file.room = room;
|
|
8796
|
+
}
|
|
8797
|
+
if (position === null || position === void 0 || position < 0) file.position = end + slice.length;
|
|
8798
|
+
return slice.length;
|
|
8799
|
+
}
|
|
8782
8800
|
const existing = file.cached ?? (tree2.existsSync(file.path) ? tree2.readFileSync(file.path) : new Uint8Array(0));
|
|
8783
8801
|
const at2 = (file.flags & flagBits().append) !== 0 ? existing.length : position === null || position === void 0 || position < 0 ? file.position : position;
|
|
8784
8802
|
const size = Math.max(existing.length, at2 + slice.length);
|
|
@@ -31851,6 +31869,47 @@ class VirtualFS {
|
|
|
31851
31869
|
writeFileSync(path2, data2) {
|
|
31852
31870
|
this.writeFileSyncInternal(path2, data2, true);
|
|
31853
31871
|
}
|
|
31872
|
+
/**
|
|
31873
|
+
* Adds bytes at a file's end, creating it. A file grown a piece at a time
|
|
31874
|
+
* (a write stream's chunks) is kept in a buffer with room to grow, so an
|
|
31875
|
+
* append copies only what it adds: writing the whole file again for each
|
|
31876
|
+
* 1 KB chunk of a 50 MB file copied about a terabyte. A tree that keeps its
|
|
31877
|
+
* files elsewhere (its own writeFileSync) is appended to through its own
|
|
31878
|
+
* read and write unless it answers appends itself.
|
|
31879
|
+
*/
|
|
31880
|
+
appendFileSync(path2, data2) {
|
|
31881
|
+
var _a2;
|
|
31882
|
+
const bytes2 = typeof data2 === "string" ? this.encoder.encode(data2) : data2;
|
|
31883
|
+
if (this.writeFileSync !== VirtualFS.prototype.writeFileSync) {
|
|
31884
|
+
const existing = this.existsSync(path2) ? this.readFileSync(path2) : new Uint8Array(0);
|
|
31885
|
+
const whole = new Uint8Array(existing.byteLength + bytes2.byteLength);
|
|
31886
|
+
whole.set(existing, 0);
|
|
31887
|
+
whole.set(bytes2, existing.byteLength);
|
|
31888
|
+
this.writeFileSync(path2, whole);
|
|
31889
|
+
return;
|
|
31890
|
+
}
|
|
31891
|
+
const normalized = this.normalizePath(path2);
|
|
31892
|
+
const node = this.getNode(normalized);
|
|
31893
|
+
if (!node) {
|
|
31894
|
+
this.writeFileSyncInternal(normalized, bytes2.slice(), true);
|
|
31895
|
+
return;
|
|
31896
|
+
}
|
|
31897
|
+
if (node.type !== "file") throw createNodeError("EISDIR", "open", path2);
|
|
31898
|
+
this.assertWritable(normalized, "open");
|
|
31899
|
+
const current3 = node.content ?? new Uint8Array(0);
|
|
31900
|
+
const length = current3.byteLength + bytes2.byteLength;
|
|
31901
|
+
let room = node.room;
|
|
31902
|
+
if (!room || current3.buffer !== room.buffer || current3.byteOffset !== 0 || room.byteLength < length) {
|
|
31903
|
+
room = new Uint8Array(Math.max(length, current3.byteLength * 2, 4096));
|
|
31904
|
+
room.set(current3, 0);
|
|
31905
|
+
}
|
|
31906
|
+
room.set(bytes2, current3.byteLength);
|
|
31907
|
+
node.content = room.subarray(0, length);
|
|
31908
|
+
node.room = room;
|
|
31909
|
+
node.mtime = Date.now();
|
|
31910
|
+
this.notifyWatchers(normalized, "change");
|
|
31911
|
+
if ((_a2 = this.eventListeners.get("change")) == null ? void 0 : _a2.size) this.emit("change", normalized, this.decoder.decode(node.content));
|
|
31912
|
+
}
|
|
31854
31913
|
/**
|
|
31855
31914
|
* Create directory, optionally with recursive parent creation
|
|
31856
31915
|
*/
|
|
@@ -39336,9 +39395,7 @@ function createRequire(vfs2, fsShim, process2, currentDir, moduleCache, options,
|
|
|
39336
39395
|
if (prepared) {
|
|
39337
39396
|
const body = __substrateScopeGlobalCalls(prepareModuleCode(rawCode, resolvedPath));
|
|
39338
39397
|
try {
|
|
39339
|
-
|
|
39340
|
-
vfs2.writeFileSync(partial, body);
|
|
39341
|
-
vfs2.renameSync(partial, prepared);
|
|
39398
|
+
vfs2.writeFileSync(prepared, body);
|
|
39342
39399
|
} catch {
|
|
39343
39400
|
}
|
|
39344
39401
|
runModuleBody(module, body, resolvedPath, dirname$1, true);
|
|
@@ -40236,7 +40293,7 @@ class WorkerRuntime {
|
|
|
40236
40293
|
this.worker = new Worker(
|
|
40237
40294
|
new URL(
|
|
40238
40295
|
/* @vite-ignore */
|
|
40239
|
-
"/assets/runtime-worker-
|
|
40296
|
+
"/assets/runtime-worker-DsSAHE2C.js",
|
|
40240
40297
|
import.meta.url
|
|
40241
40298
|
),
|
|
40242
40299
|
{ type: "module" }
|
package/dist/runtime.d.ts
CHANGED
|
@@ -14,7 +14,8 @@ export { pendingGuestTimers, stopGuestTimers } from './guest-timers';
|
|
|
14
14
|
* playwright-core's, measured in a tab, against 0.9 s under Node on the same
|
|
15
15
|
* machine. The image carries each body under the hash of the file it was made
|
|
16
16
|
* from, in this directory of the tab's filesystem, and the loader takes it in
|
|
17
|
-
* place of the passes when the file it read hashes to one.
|
|
17
|
+
* place of the passes when the file it read hashes to one. A body the loader
|
|
18
|
+
* prepares for a file the image has none for is kept there the same way.
|
|
18
19
|
*/
|
|
19
20
|
export declare const PREPARED_MODULES_DIR = "/.tabnode/prepared";
|
|
20
21
|
/** The name a prepared body goes under: the hash of the file as read, and how it is compiled. Undefined for a file no body is prepared for. */
|
package/dist/virtual-fs.d.ts
CHANGED
|
@@ -195,6 +195,15 @@ export declare class VirtualFS {
|
|
|
195
195
|
* Write data to file, creating parent directories as needed
|
|
196
196
|
*/
|
|
197
197
|
writeFileSync(path: string, data: string | Uint8Array): void;
|
|
198
|
+
/**
|
|
199
|
+
* Adds bytes at a file's end, creating it. A file grown a piece at a time
|
|
200
|
+
* (a write stream's chunks) is kept in a buffer with room to grow, so an
|
|
201
|
+
* append copies only what it adds: writing the whole file again for each
|
|
202
|
+
* 1 KB chunk of a 50 MB file copied about a terabyte. A tree that keeps its
|
|
203
|
+
* files elsewhere (its own writeFileSync) is appended to through its own
|
|
204
|
+
* read and write unless it answers appends itself.
|
|
205
|
+
*/
|
|
206
|
+
appendFileSync(path: string, data: string | Uint8Array): void;
|
|
198
207
|
/**
|
|
199
208
|
* Create directory, optionally with recursive parent creation
|
|
200
209
|
*/
|
package/package.json
CHANGED
|
@@ -157,6 +157,8 @@ interface OpenFile {
|
|
|
157
157
|
* it could send ready.
|
|
158
158
|
*/
|
|
159
159
|
cached?: Uint8Array;
|
|
160
|
+
/** The buffer `cached` views when writes have grown it a piece at a time. */
|
|
161
|
+
room?: Uint8Array;
|
|
160
162
|
}
|
|
161
163
|
|
|
162
164
|
/** Every descriptor this engine has open, and the next number to hand out. */
|
|
@@ -606,6 +608,28 @@ const fsBinding = {
|
|
|
606
608
|
if ((file.flags & 3) === 0) throw createNodeError('EBADF', 'write', file.path);
|
|
607
609
|
const tree = file.tree;
|
|
608
610
|
const slice = buffer.subarray(offset, offset + length);
|
|
611
|
+
// A write at the file's end is an append: the tree is given only the
|
|
612
|
+
// bytes it adds, and this descriptor's copy grows in a buffer with room.
|
|
613
|
+
// Writing the whole file again for each chunk made a 50 MB stream of
|
|
614
|
+
// 1 KB writes copy about a terabyte.
|
|
615
|
+
const held = file.cached?.length ?? (tree.existsSync(file.path) ? tree.statSync(file.path).size : 0);
|
|
616
|
+
const end = (file.flags & flagBits().append) !== 0 ? held
|
|
617
|
+
: position === null || position === undefined || position < 0 ? file.position : position;
|
|
618
|
+
if (end === held && typeof (tree as { appendFileSync?: unknown }).appendFileSync === 'function') {
|
|
619
|
+
(tree as { appendFileSync(path: string, data: Uint8Array): void }).appendFileSync(file.path, slice);
|
|
620
|
+
if (file.cached !== undefined) {
|
|
621
|
+
let room = file.room;
|
|
622
|
+
if (!room || file.cached.buffer !== room.buffer || file.cached.byteOffset !== 0 || room.byteLength < held + slice.length) {
|
|
623
|
+
room = new Uint8Array(Math.max(held + slice.length, held * 2, 4096));
|
|
624
|
+
room.set(file.cached, 0);
|
|
625
|
+
}
|
|
626
|
+
room.set(slice, held);
|
|
627
|
+
file.cached = room.subarray(0, held + slice.length);
|
|
628
|
+
file.room = room;
|
|
629
|
+
}
|
|
630
|
+
if (position === null || position === undefined || position < 0) file.position = end + slice.length;
|
|
631
|
+
return slice.length;
|
|
632
|
+
}
|
|
609
633
|
const existing = file.cached ?? (tree.existsSync(file.path) ? tree.readFileSync(file.path) as Uint8Array : new Uint8Array(0));
|
|
610
634
|
const at = (file.flags & flagBits().append) !== 0 ? existing.length
|
|
611
635
|
: position === null || position === undefined || position < 0 ? file.position : position;
|
package/src/runtime.ts
CHANGED
|
@@ -587,7 +587,8 @@ function __substratePrepareBody(rawCode: string, resolvedPath: string, format: s
|
|
|
587
587
|
* playwright-core's, measured in a tab, against 0.9 s under Node on the same
|
|
588
588
|
* machine. The image carries each body under the hash of the file it was made
|
|
589
589
|
* from, in this directory of the tab's filesystem, and the loader takes it in
|
|
590
|
-
* place of the passes when the file it read hashes to one.
|
|
590
|
+
* place of the passes when the file it read hashes to one. A body the loader
|
|
591
|
+
* prepares for a file the image has none for is kept there the same way.
|
|
591
592
|
*/
|
|
592
593
|
export const PREPARED_MODULES_DIR = '/.tabnode/prepared';
|
|
593
594
|
const PREPARED_MODULES_FORMAT = 'tabnode-prepared-2';
|
|
@@ -2003,11 +2004,11 @@ function createRequire(
|
|
|
2003
2004
|
// playwright-core afresh, 28 s each, measured in a tab.
|
|
2004
2005
|
if (prepared) {
|
|
2005
2006
|
const body = __substrateScopeGlobalCalls(prepareModuleCode(rawCode, resolvedPath));
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2007
|
+
// One write is one step of the tab's filesystem, so no reader sees
|
|
2008
|
+
// half a body; a rename after it failed in a forked process's realm
|
|
2009
|
+
// and left the half-named file behind.
|
|
2010
|
+
try { vfs.writeFileSync(prepared, body); }
|
|
2011
|
+
catch { /* a process that may not write there prepares its own */ }
|
|
2011
2012
|
runModuleBody(module, body, resolvedPath, dirname, true);
|
|
2012
2013
|
return module;
|
|
2013
2014
|
}
|
package/src/virtual-fs.ts
CHANGED
|
@@ -656,6 +656,48 @@ export class VirtualFS {
|
|
|
656
656
|
this.writeFileSyncInternal(path, data, true);
|
|
657
657
|
}
|
|
658
658
|
|
|
659
|
+
/**
|
|
660
|
+
* Adds bytes at a file's end, creating it. A file grown a piece at a time
|
|
661
|
+
* (a write stream's chunks) is kept in a buffer with room to grow, so an
|
|
662
|
+
* append copies only what it adds: writing the whole file again for each
|
|
663
|
+
* 1 KB chunk of a 50 MB file copied about a terabyte. A tree that keeps its
|
|
664
|
+
* files elsewhere (its own writeFileSync) is appended to through its own
|
|
665
|
+
* read and write unless it answers appends itself.
|
|
666
|
+
*/
|
|
667
|
+
appendFileSync(path: string, data: string | Uint8Array): void {
|
|
668
|
+
const bytes = typeof data === 'string' ? this.encoder.encode(data) : data;
|
|
669
|
+
if (this.writeFileSync !== VirtualFS.prototype.writeFileSync) {
|
|
670
|
+
const existing = this.existsSync(path) ? this.readFileSync(path) as Uint8Array : new Uint8Array(0);
|
|
671
|
+
const whole = new Uint8Array(existing.byteLength + bytes.byteLength);
|
|
672
|
+
whole.set(existing, 0);
|
|
673
|
+
whole.set(bytes, existing.byteLength);
|
|
674
|
+
this.writeFileSync(path, whole);
|
|
675
|
+
return;
|
|
676
|
+
}
|
|
677
|
+
const normalized = this.normalizePath(path);
|
|
678
|
+
const node = this.getNode(normalized);
|
|
679
|
+
if (!node) {
|
|
680
|
+
this.writeFileSyncInternal(normalized, bytes.slice(), true);
|
|
681
|
+
return;
|
|
682
|
+
}
|
|
683
|
+
if (node.type !== 'file') throw createNodeError('EISDIR', 'open', path);
|
|
684
|
+
this.assertWritable(normalized, 'open');
|
|
685
|
+
const current = node.content ?? new Uint8Array(0);
|
|
686
|
+
const length = current.byteLength + bytes.byteLength;
|
|
687
|
+
let room = (node as FSNode & { room?: Uint8Array }).room;
|
|
688
|
+
if (!room || current.buffer !== room.buffer || current.byteOffset !== 0 || room.byteLength < length) {
|
|
689
|
+
room = new Uint8Array(Math.max(length, current.byteLength * 2, 4096));
|
|
690
|
+
room.set(current, 0);
|
|
691
|
+
}
|
|
692
|
+
room.set(bytes, current.byteLength);
|
|
693
|
+
node.content = room.subarray(0, length);
|
|
694
|
+
(node as FSNode & { room?: Uint8Array }).room = room;
|
|
695
|
+
node.mtime = Date.now();
|
|
696
|
+
this.notifyWatchers(normalized, 'change');
|
|
697
|
+
// The change event carries the file's text; it is made only for a listener.
|
|
698
|
+
if (this.eventListeners.get('change')?.size) this.emit('change', normalized, this.decoder.decode(node.content));
|
|
699
|
+
}
|
|
700
|
+
|
|
659
701
|
/**
|
|
660
702
|
* Create directory, optionally with recursive parent creation
|
|
661
703
|
*/
|