@volter/tabnode 0.3.0 → 0.3.1

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.
@@ -7913,11 +7913,11 @@ function answerFilesystem(method, args) {
7913
7913
  return answer2.apply(hostVfs, plain) ?? null;
7914
7914
  }
7915
7915
  const inodes = /* @__PURE__ */ new Map();
7916
- let nextInode = 1;
7916
+ let nextInode$1 = 1;
7917
7917
  function inodeOf(path2) {
7918
7918
  let inode = inodes.get(path2);
7919
7919
  if (inode === void 0) {
7920
- inode = nextInode++;
7920
+ inode = nextInode$1++;
7921
7921
  inodes.set(path2, inode);
7922
7922
  }
7923
7923
  return inode;
@@ -9431,6 +9431,8 @@ const fsBinding = {
9431
9431
  if ((flags & bits.excl) !== 0 && (flags & bits.create) !== 0 && exists) throw createNodeError$1("EEXIST", "open", name);
9432
9432
  if (!exists) {
9433
9433
  if ((flags & bits.create) === 0) throw createNodeError$1("ENOENT", "open", name);
9434
+ const parent = name.slice(0, name.lastIndexOf("/")) || "/";
9435
+ if (!tree2.statSync(parent).isDirectory()) throw createNodeError$1("ENOTDIR", "open", name);
9434
9436
  tree2.writeFileSync(name, "");
9435
9437
  } else if ((flags & bits.truncate) !== 0) {
9436
9438
  tree2.writeFileSync(name, "");
@@ -9443,6 +9445,7 @@ const fsBinding = {
9443
9445
  },
9444
9446
  close(fd2, req) {
9445
9447
  return answer(req, () => {
9448
+ fileFor(fd2);
9446
9449
  openFiles.delete(fd2);
9447
9450
  return void 0;
9448
9451
  });
@@ -9451,6 +9454,7 @@ const fsBinding = {
9451
9454
  read(fd2, buffer2, offset2, length, position, req) {
9452
9455
  return answer(req, () => {
9453
9456
  const file = fileFor(fd2);
9457
+ if ((file.flags & 3) === 1) throw createNodeError$1("EBADF", "read", file.path);
9454
9458
  if (file.cached === void 0) file.cached = file.tree.readFileSync(file.path);
9455
9459
  const bytes2 = file.cached;
9456
9460
  const from = position === null || position === void 0 || position < 0 ? file.position : position;
@@ -9473,17 +9477,27 @@ const fsBinding = {
9473
9477
  });
9474
9478
  },
9475
9479
  /** Node's fast path for `readFile` with an encoding it can decode itself. */
9476
- readFileUtf8(path2, _flags) {
9477
- return vfs$1().readFileSync(asPath(path2), "utf8");
9480
+ readFileUtf8(path2, flags) {
9481
+ const owned = typeof path2 !== "number";
9482
+ const fd2 = owned ? fsBinding.open(path2, flags, 438) : path2;
9483
+ try {
9484
+ const file = fileFor(fd2);
9485
+ const bytes2 = new Uint8Array(file.tree.statSync(file.path).size);
9486
+ const length = fsBinding.read(fd2, bytes2, 0, bytes2.length, -1);
9487
+ return new TextDecoder().decode(bytes2.subarray(0, length));
9488
+ } finally {
9489
+ if (owned) fsBinding.close(fd2);
9490
+ }
9478
9491
  },
9479
9492
  // ---- writing ------------------------------------------------------------
9480
9493
  writeBuffer(fd2, buffer2, offset2, length, position, req) {
9481
9494
  return answer(req, () => {
9482
9495
  const file = fileFor(fd2);
9496
+ if ((file.flags & 3) === 0) throw createNodeError$1("EBADF", "write", file.path);
9483
9497
  const tree2 = file.tree;
9484
9498
  const slice = buffer2.subarray(offset2, offset2 + length);
9485
9499
  const existing = file.cached ?? (tree2.existsSync(file.path) ? tree2.readFileSync(file.path) : new Uint8Array(0));
9486
- const at3 = position === null || position === void 0 || position < 0 ? file.position : position;
9500
+ const at3 = (file.flags & flagBits().append) !== 0 ? existing.length : position === null || position === void 0 || position < 0 ? file.position : position;
9487
9501
  const size = Math.max(existing.length, at3 + slice.length);
9488
9502
  const next2 = new Uint8Array(size);
9489
9503
  next2.set(existing, 0);
@@ -9513,15 +9527,14 @@ const fsBinding = {
9513
9527
  const bytes2 = encoding === "utf8" || encoding === void 0 || encoding === "utf-8" ? new TextEncoder().encode(value) : Uint8Array.from(value, (character) => character.charCodeAt(0) & 255);
9514
9528
  return fsBinding.writeBuffer(fd2, bytes2, 0, bytes2.length, position ?? null, req);
9515
9529
  },
9516
- writeFileUtf8(path2, data2, flags, _mode) {
9517
- const tree2 = vfs$1();
9518
- const name = asPath(path2);
9519
- if ((flags & flagBits().append) !== 0 && tree2.existsSync(name)) {
9520
- const existing = tree2.readFileSync(name, "utf8");
9521
- tree2.writeFileSync(name, existing + data2);
9522
- return void 0;
9530
+ writeFileUtf8(path2, data2, flags, mode) {
9531
+ const owned = typeof path2 !== "number";
9532
+ const fd2 = owned ? fsBinding.open(path2, flags, mode) : path2;
9533
+ try {
9534
+ fsBinding.writeString(fd2, data2, null, "utf8");
9535
+ } finally {
9536
+ if (owned) fsBinding.close(fd2);
9523
9537
  }
9524
- tree2.writeFileSync(name, data2);
9525
9538
  return void 0;
9526
9539
  },
9527
9540
  // ---- metadata -----------------------------------------------------------
@@ -9802,6 +9815,7 @@ const fsBinding = {
9802
9815
  ftruncate(fd2, length, req) {
9803
9816
  return answer(req, () => {
9804
9817
  const file = fileFor(fd2);
9818
+ if ((file.flags & 3) === 0) throw createNodeError$1("EBADF", "ftruncate", file.path);
9805
9819
  const tree2 = file.tree;
9806
9820
  const bytes2 = file.cached ?? tree2.readFileSync(file.path);
9807
9821
  const next2 = new Uint8Array(length);
@@ -26098,6 +26112,16 @@ lazyExport("stream", "pipeline");
26098
26112
  lazyExport("stream", "finished");
26099
26113
  lazyModule("stream/promises");
26100
26114
  const streamPromisesModule = lazyModule("stream/promises");
26115
+ const nodeInodes = /* @__PURE__ */ new WeakMap();
26116
+ let nextInode = 1;
26117
+ function inodeFor(node) {
26118
+ let inode = nodeInodes.get(node);
26119
+ if (inode === void 0) {
26120
+ inode = nextInode++;
26121
+ nodeInodes.set(node, inode);
26122
+ }
26123
+ return inode;
26124
+ }
26101
26125
  function createNodeError(code2, syscall, path2, message) {
26102
26126
  const errno = {
26103
26127
  ENOENT: -2,
@@ -26414,7 +26438,7 @@ class VirtualFS {
26414
26438
  uid: 1e3,
26415
26439
  gid: 1e3,
26416
26440
  dev: 0,
26417
- ino: 0,
26441
+ ino: inodeFor(node),
26418
26442
  rdev: 0,
26419
26443
  blksize: 4096,
26420
26444
  blocks: Math.ceil(size / 512)
@@ -26453,7 +26477,7 @@ class VirtualFS {
26453
26477
  uid: 1e3,
26454
26478
  gid: 1e3,
26455
26479
  dev: 0,
26456
- ino: 0,
26480
+ ino: inodeFor(node),
26457
26481
  rdev: 0,
26458
26482
  blksize: 4096,
26459
26483
  blocks: 0
package/dist/index.mjs CHANGED
@@ -7626,11 +7626,11 @@ function answerFilesystem(method, args) {
7626
7626
  return answer2.apply(hostVfs, plain) ?? null;
7627
7627
  }
7628
7628
  const inodes = /* @__PURE__ */ new Map();
7629
- let nextInode = 1;
7629
+ let nextInode$1 = 1;
7630
7630
  function inodeOf(path2) {
7631
7631
  let inode = inodes.get(path2);
7632
7632
  if (inode === void 0) {
7633
- inode = nextInode++;
7633
+ inode = nextInode$1++;
7634
7634
  inodes.set(path2, inode);
7635
7635
  }
7636
7636
  return inode;
@@ -9144,6 +9144,8 @@ const fsBinding = {
9144
9144
  if ((flags & bits.excl) !== 0 && (flags & bits.create) !== 0 && exists) throw createNodeError$1("EEXIST", "open", name);
9145
9145
  if (!exists) {
9146
9146
  if ((flags & bits.create) === 0) throw createNodeError$1("ENOENT", "open", name);
9147
+ const parent = name.slice(0, name.lastIndexOf("/")) || "/";
9148
+ if (!tree2.statSync(parent).isDirectory()) throw createNodeError$1("ENOTDIR", "open", name);
9147
9149
  tree2.writeFileSync(name, "");
9148
9150
  } else if ((flags & bits.truncate) !== 0) {
9149
9151
  tree2.writeFileSync(name, "");
@@ -9156,6 +9158,7 @@ const fsBinding = {
9156
9158
  },
9157
9159
  close(fd, req) {
9158
9160
  return answer(req, () => {
9161
+ fileFor(fd);
9159
9162
  openFiles.delete(fd);
9160
9163
  return void 0;
9161
9164
  });
@@ -9164,6 +9167,7 @@ const fsBinding = {
9164
9167
  read(fd, buffer2, offset2, length, position, req) {
9165
9168
  return answer(req, () => {
9166
9169
  const file = fileFor(fd);
9170
+ if ((file.flags & 3) === 1) throw createNodeError$1("EBADF", "read", file.path);
9167
9171
  if (file.cached === void 0) file.cached = file.tree.readFileSync(file.path);
9168
9172
  const bytes2 = file.cached;
9169
9173
  const from = position === null || position === void 0 || position < 0 ? file.position : position;
@@ -9186,17 +9190,27 @@ const fsBinding = {
9186
9190
  });
9187
9191
  },
9188
9192
  /** Node's fast path for `readFile` with an encoding it can decode itself. */
9189
- readFileUtf8(path2, _flags) {
9190
- return vfs().readFileSync(asPath(path2), "utf8");
9193
+ readFileUtf8(path2, flags) {
9194
+ const owned = typeof path2 !== "number";
9195
+ const fd = owned ? fsBinding.open(path2, flags, 438) : path2;
9196
+ try {
9197
+ const file = fileFor(fd);
9198
+ const bytes2 = new Uint8Array(file.tree.statSync(file.path).size);
9199
+ const length = fsBinding.read(fd, bytes2, 0, bytes2.length, -1);
9200
+ return new TextDecoder().decode(bytes2.subarray(0, length));
9201
+ } finally {
9202
+ if (owned) fsBinding.close(fd);
9203
+ }
9191
9204
  },
9192
9205
  // ---- writing ------------------------------------------------------------
9193
9206
  writeBuffer(fd, buffer2, offset2, length, position, req) {
9194
9207
  return answer(req, () => {
9195
9208
  const file = fileFor(fd);
9209
+ if ((file.flags & 3) === 0) throw createNodeError$1("EBADF", "write", file.path);
9196
9210
  const tree2 = file.tree;
9197
9211
  const slice = buffer2.subarray(offset2, offset2 + length);
9198
9212
  const existing = file.cached ?? (tree2.existsSync(file.path) ? tree2.readFileSync(file.path) : new Uint8Array(0));
9199
- const at2 = position === null || position === void 0 || position < 0 ? file.position : position;
9213
+ const at2 = (file.flags & flagBits().append) !== 0 ? existing.length : position === null || position === void 0 || position < 0 ? file.position : position;
9200
9214
  const size = Math.max(existing.length, at2 + slice.length);
9201
9215
  const next = new Uint8Array(size);
9202
9216
  next.set(existing, 0);
@@ -9226,15 +9240,14 @@ const fsBinding = {
9226
9240
  const bytes2 = encoding === "utf8" || encoding === void 0 || encoding === "utf-8" ? new TextEncoder().encode(value) : Uint8Array.from(value, (character) => character.charCodeAt(0) & 255);
9227
9241
  return fsBinding.writeBuffer(fd, bytes2, 0, bytes2.length, position ?? null, req);
9228
9242
  },
9229
- writeFileUtf8(path2, data2, flags, _mode) {
9230
- const tree2 = vfs();
9231
- const name = asPath(path2);
9232
- if ((flags & flagBits().append) !== 0 && tree2.existsSync(name)) {
9233
- const existing = tree2.readFileSync(name, "utf8");
9234
- tree2.writeFileSync(name, existing + data2);
9235
- return void 0;
9243
+ writeFileUtf8(path2, data2, flags, mode) {
9244
+ const owned = typeof path2 !== "number";
9245
+ const fd = owned ? fsBinding.open(path2, flags, mode) : path2;
9246
+ try {
9247
+ fsBinding.writeString(fd, data2, null, "utf8");
9248
+ } finally {
9249
+ if (owned) fsBinding.close(fd);
9236
9250
  }
9237
- tree2.writeFileSync(name, data2);
9238
9251
  return void 0;
9239
9252
  },
9240
9253
  // ---- metadata -----------------------------------------------------------
@@ -9515,6 +9528,7 @@ const fsBinding = {
9515
9528
  ftruncate(fd, length, req) {
9516
9529
  return answer(req, () => {
9517
9530
  const file = fileFor(fd);
9531
+ if ((file.flags & 3) === 0) throw createNodeError$1("EBADF", "ftruncate", file.path);
9518
9532
  const tree2 = file.tree;
9519
9533
  const bytes2 = file.cached ?? tree2.readFileSync(file.path);
9520
9534
  const next = new Uint8Array(length);
@@ -25831,6 +25845,16 @@ lazyExport("stream", "pipeline");
25831
25845
  lazyExport("stream", "finished");
25832
25846
  lazyModule("stream/promises");
25833
25847
  const streamPromisesModule = lazyModule("stream/promises");
25848
+ const nodeInodes = /* @__PURE__ */ new WeakMap();
25849
+ let nextInode = 1;
25850
+ function inodeFor(node) {
25851
+ let inode = nodeInodes.get(node);
25852
+ if (inode === void 0) {
25853
+ inode = nextInode++;
25854
+ nodeInodes.set(node, inode);
25855
+ }
25856
+ return inode;
25857
+ }
25834
25858
  function createNodeError(code2, syscall, path2, message) {
25835
25859
  const errno = {
25836
25860
  ENOENT: -2,
@@ -26147,7 +26171,7 @@ class VirtualFS {
26147
26171
  uid: 1e3,
26148
26172
  gid: 1e3,
26149
26173
  dev: 0,
26150
- ino: 0,
26174
+ ino: inodeFor(node),
26151
26175
  rdev: 0,
26152
26176
  blksize: 4096,
26153
26177
  blocks: Math.ceil(size / 512)
@@ -26186,7 +26210,7 @@ class VirtualFS {
26186
26210
  uid: 1e3,
26187
26211
  gid: 1e3,
26188
26212
  dev: 0,
26189
- ino: 0,
26213
+ ino: inodeFor(node),
26190
26214
  rdev: 0,
26191
26215
  blksize: 4096,
26192
26216
  blocks: 0
@@ -33786,7 +33810,7 @@ class WorkerRuntime {
33786
33810
  this.worker = new Worker(
33787
33811
  new URL(
33788
33812
  /* @vite-ignore */
33789
- "/assets/runtime-worker-BcDCBbhG.js",
33813
+ "/assets/runtime-worker-CUO9SNFs.js",
33790
33814
  import.meta.url
33791
33815
  ),
33792
33816
  { type: "module" }
@@ -95,11 +95,11 @@ export declare const fsBinding: {
95
95
  read(fd: number, buffer: Uint8Array, offset: number, length: number, position: number, req?: FSReq): number | undefined;
96
96
  readBuffers(fd: number, buffers: Uint8Array[], position: number, req?: FSReq): number | undefined;
97
97
  /** Node's fast path for `readFile` with an encoding it can decode itself. */
98
- readFileUtf8(path: unknown, _flags: number): string;
98
+ readFileUtf8(path: unknown, flags: number): string;
99
99
  writeBuffer(fd: number, buffer: Uint8Array, offset: number, length: number, position: number | null, req?: FSReq): number | undefined;
100
100
  writeBuffers(fd: number, buffers: Uint8Array[], position: number | null, req?: FSReq): number | undefined;
101
101
  writeString(fd: number, value: string, position: number | null, encoding: string | undefined, req?: FSReq): number | undefined;
102
- writeFileUtf8(path: unknown, data: string, flags: number, _mode: number): undefined;
102
+ writeFileUtf8(path: unknown, data: string, flags: number, mode: number): undefined;
103
103
  stat(path: unknown, bigint: boolean, req?: FSReq, throwIfNoEntry?: boolean): Float64Array | BigInt64Array | undefined;
104
104
  lstat(path: unknown, bigint: boolean, req?: FSReq, throwIfNoEntry?: boolean): Float64Array | BigInt64Array | undefined;
105
105
  fstat(fd: number, bigint: boolean, req?: FSReq): Float64Array | BigInt64Array | undefined;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@volter/tabnode",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "Node's own library, in a browser tab.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -469,6 +469,9 @@ export const fsBinding = {
469
469
  if ((flags & bits.excl) !== 0 && (flags & bits.create) !== 0 && exists) throw createNodeError('EEXIST', 'open', name);
470
470
  if (!exists) {
471
471
  if ((flags & bits.create) === 0) throw createNodeError('ENOENT', 'open', name);
472
+ // VirtualFS writes create parents for image loading; open(2) does not.
473
+ const parent = name.slice(0, name.lastIndexOf('/')) || '/';
474
+ if (!tree.statSync(parent).isDirectory()) throw createNodeError('ENOTDIR', 'open', name);
472
475
  tree.writeFileSync(name, '');
473
476
  } else if ((flags & bits.truncate) !== 0) {
474
477
  tree.writeFileSync(name, '');
@@ -481,13 +484,14 @@ export const fsBinding = {
481
484
  },
482
485
 
483
486
  close(fd: number, req?: FSReq): undefined {
484
- return answer(req, () => { openFiles.delete(fd); return undefined; });
487
+ return answer(req, () => { fileFor(fd); openFiles.delete(fd); return undefined; });
485
488
  },
486
489
 
487
490
  // ---- reading ------------------------------------------------------------
488
491
  read(fd: number, buffer: Uint8Array, offset: number, length: number, position: number, req?: FSReq): number | undefined {
489
492
  return answer(req, () => {
490
493
  const file = fileFor(fd);
494
+ if ((file.flags & 3) === 1) throw createNodeError('EBADF', 'read', file.path);
491
495
  if (file.cached === undefined) file.cached = file.tree.readFileSync(file.path) as Uint8Array;
492
496
  const bytes = file.cached;
493
497
  const from = position === null || position === undefined || position < 0 ? file.position : position;
@@ -512,18 +516,27 @@ export const fsBinding = {
512
516
  },
513
517
 
514
518
  /** Node's fast path for `readFile` with an encoding it can decode itself. */
515
- readFileUtf8(path: unknown, _flags: number): string {
516
- return vfs().readFileSync(asPath(path), 'utf8') as string;
519
+ readFileUtf8(path: unknown, flags: number): string {
520
+ const owned = typeof path !== 'number';
521
+ const fd = owned ? fsBinding.open(path, flags, 0o666) as number : path as number;
522
+ try {
523
+ const file = fileFor(fd);
524
+ const bytes = new Uint8Array(file.tree.statSync(file.path).size);
525
+ const length = fsBinding.read(fd, bytes, 0, bytes.length, -1) as number;
526
+ return new TextDecoder().decode(bytes.subarray(0, length));
527
+ } finally { if (owned) fsBinding.close(fd); }
517
528
  },
518
529
 
519
530
  // ---- writing ------------------------------------------------------------
520
531
  writeBuffer(fd: number, buffer: Uint8Array, offset: number, length: number, position: number | null, req?: FSReq): number | undefined {
521
532
  return answer(req, () => {
522
533
  const file = fileFor(fd);
534
+ if ((file.flags & 3) === 0) throw createNodeError('EBADF', 'write', file.path);
523
535
  const tree = file.tree;
524
536
  const slice = buffer.subarray(offset, offset + length);
525
537
  const existing = file.cached ?? (tree.existsSync(file.path) ? tree.readFileSync(file.path) as Uint8Array : new Uint8Array(0));
526
- const at = position === null || position === undefined || position < 0 ? file.position : position;
538
+ const at = (file.flags & flagBits().append) !== 0 ? existing.length
539
+ : position === null || position === undefined || position < 0 ? file.position : position;
527
540
  const size = Math.max(existing.length, at + slice.length);
528
541
  const next = new Uint8Array(size);
529
542
  next.set(existing, 0);
@@ -553,15 +566,13 @@ export const fsBinding = {
553
566
  return fsBinding.writeBuffer(fd, bytes, 0, bytes.length, position ?? null, req);
554
567
  },
555
568
 
556
- writeFileUtf8(path: unknown, data: string, flags: number, _mode: number): undefined {
557
- const tree = vfs();
558
- const name = asPath(path);
559
- if ((flags & flagBits().append) !== 0 && tree.existsSync(name)) {
560
- const existing = tree.readFileSync(name, 'utf8') as string;
561
- tree.writeFileSync(name, existing + data);
562
- return undefined;
563
- }
564
- tree.writeFileSync(name, data);
569
+ writeFileUtf8(path: unknown, data: string, flags: number, mode: number): undefined {
570
+ // Node's UTF-8 fast path accepts a path OR an already-open descriptor.
571
+ // Reuse open/write so flags, offsets and errors match the buffer path.
572
+ const owned = typeof path !== 'number';
573
+ const fd = owned ? fsBinding.open(path, flags, mode) as number : path as number;
574
+ try { fsBinding.writeString(fd, data, null, 'utf8'); }
575
+ finally { if (owned) fsBinding.close(fd); }
565
576
  return undefined;
566
577
  },
567
578
 
@@ -843,6 +854,7 @@ export const fsBinding = {
843
854
  ftruncate(fd: number, length: number, req?: FSReq): undefined {
844
855
  return answer(req, () => {
845
856
  const file = fileFor(fd);
857
+ if ((file.flags & 3) === 0) throw createNodeError('EBADF', 'ftruncate', file.path);
846
858
  const tree = file.tree;
847
859
  const bytes = file.cached ?? (tree.readFileSync(file.path) as Uint8Array);
848
860
  const next = new Uint8Array(length);
package/src/virtual-fs.ts CHANGED
@@ -45,6 +45,17 @@ export interface FSNode {
45
45
  atime?: number;
46
46
  }
47
47
 
48
+ // Identity belongs to the node, so writes and renames preserve it while an
49
+ // unlink followed by recreation receives a different inode. Weak keys do not
50
+ // retain deleted trees, and snapshots receive new identities when restored.
51
+ const nodeInodes = new WeakMap<FSNode, number>();
52
+ let nextInode = 1;
53
+ function inodeFor(node: FSNode): number {
54
+ let inode = nodeInodes.get(node);
55
+ if (inode === undefined) { inode = nextInode++; nodeInodes.set(node, inode); }
56
+ return inode;
57
+ }
58
+
48
59
  // Simple EventEmitter for VFS change notifications
49
60
  type VFSChangeListener = (path: string, content: string) => void;
50
61
  type VFSDeleteListener = (path: string) => void;
@@ -491,7 +502,7 @@ export class VirtualFS {
491
502
  uid: 1000,
492
503
  gid: 1000,
493
504
  dev: 0,
494
- ino: 0,
505
+ ino: inodeFor(node),
495
506
  rdev: 0,
496
507
  blksize: 4096,
497
508
  blocks: Math.ceil(size / 512),
@@ -531,7 +542,7 @@ export class VirtualFS {
531
542
  uid: 1000,
532
543
  gid: 1000,
533
544
  dev: 0,
534
- ino: 0,
545
+ ino: inodeFor(node),
535
546
  rdev: 0,
536
547
  blksize: 4096,
537
548
  blocks: 0