@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.
@@ -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 = requireFunctionApply();
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 = requireFunctionApply();
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 = requireFunctionApply();
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
- const partial = `${prepared}.${process2.pid}.partial`;
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
- const partial = `${prepared}.${process2.pid}.partial`;
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-g2I0tMiC.js",
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. */
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/tabnode",
3
- "version": "0.5.32",
3
+ "version": "0.5.34",
4
4
  "description": "Node's own library, in a browser tab.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -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
- try {
2007
- const partial = `${prepared}.${process.pid}.partial`;
2008
- vfs.writeFileSync(partial, body);
2009
- vfs.renameSync(partial, prepared);
2010
- } catch { /* a process that may not write there prepares its own */ }
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
  */