@componentor/fs 3.0.6 → 3.0.8

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 CHANGED
@@ -509,6 +509,18 @@ Make sure `opfsSync` is enabled (it's `true` by default). Files are mirrored to
509
509
 
510
510
  ## Changelog
511
511
 
512
+ ### v3.0.8 (2026)
513
+
514
+ **Improvements:**
515
+ - Add VFS helper functions: `unpackToOPFS`, `loadFromOPFS`, and `repairVFS` for VFS maintenance, migration, and recovery
516
+ - Helpers work in both Worker (sync access handle) and main thread (in-memory buffer + async writable) contexts
517
+ - Remove redundant I/O call in `unpackToOPFS` directory creation
518
+
519
+ ### v3.0.7 (2026)
520
+
521
+ **Fixes:**
522
+ - Fix `fs.watch()` path matching for root `/` watchers — watching `/` now correctly matches all child paths instead of missing them due to an off-by-one boundary check
523
+
512
524
  ### v3.0.6 (2026)
513
525
 
514
526
  **Performance:**
package/dist/index.js CHANGED
@@ -1080,10 +1080,11 @@ function matchWatcher(entry, mutatedPath) {
1080
1080
  if (mutatedPath === absPath) {
1081
1081
  return basename(mutatedPath);
1082
1082
  }
1083
- if (!mutatedPath.startsWith(absPath) || mutatedPath.charAt(absPath.length) !== "/") {
1083
+ const prefix = absPath.endsWith("/") ? absPath : absPath + "/";
1084
+ if (!mutatedPath.startsWith(prefix)) {
1084
1085
  return null;
1085
1086
  }
1086
- const relativePath = mutatedPath.substring(absPath.length + 1);
1087
+ const relativePath = mutatedPath.substring(prefix.length);
1087
1088
  if (recursive) return relativePath;
1088
1089
  return relativePath.indexOf("/") === -1 ? relativePath : null;
1089
1090
  }
@@ -3235,6 +3236,87 @@ var VFSEngine = class {
3235
3236
  };
3236
3237
 
3237
3238
  // src/helpers.ts
3239
+ var MemoryHandle = class {
3240
+ buf;
3241
+ len;
3242
+ constructor(initialData) {
3243
+ if (initialData && initialData.byteLength > 0) {
3244
+ this.buf = new Uint8Array(initialData);
3245
+ this.len = initialData.byteLength;
3246
+ } else {
3247
+ this.buf = new Uint8Array(1024 * 1024);
3248
+ this.len = 0;
3249
+ }
3250
+ }
3251
+ getSize() {
3252
+ return this.len;
3253
+ }
3254
+ read(target, opts) {
3255
+ const offset = opts?.at ?? 0;
3256
+ const dst = new Uint8Array(target.buffer, target.byteOffset, target.byteLength);
3257
+ const bytesToRead = Math.min(dst.length, this.len - offset);
3258
+ if (bytesToRead <= 0) return 0;
3259
+ dst.set(this.buf.subarray(offset, offset + bytesToRead));
3260
+ return bytesToRead;
3261
+ }
3262
+ write(data, opts) {
3263
+ const offset = opts?.at ?? 0;
3264
+ const src = new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
3265
+ const needed = offset + src.length;
3266
+ if (needed > this.buf.length) {
3267
+ this.grow(needed);
3268
+ }
3269
+ this.buf.set(src, offset);
3270
+ if (needed > this.len) this.len = needed;
3271
+ return src.length;
3272
+ }
3273
+ truncate(size) {
3274
+ if (size > this.buf.length) {
3275
+ this.grow(size);
3276
+ }
3277
+ if (size > this.len) {
3278
+ this.buf.fill(0, this.len, size);
3279
+ }
3280
+ this.len = size;
3281
+ }
3282
+ flush() {
3283
+ }
3284
+ close() {
3285
+ }
3286
+ /** Get the current data as an ArrayBuffer (trimmed to actual size) */
3287
+ getBuffer() {
3288
+ return this.buf.buffer.slice(0, this.len);
3289
+ }
3290
+ grow(minSize) {
3291
+ const newSize = Math.max(minSize, this.buf.length * 2);
3292
+ const newBuf = new Uint8Array(newSize);
3293
+ newBuf.set(this.buf.subarray(0, this.len));
3294
+ this.buf = newBuf;
3295
+ }
3296
+ };
3297
+ async function openVFSHandle(fileHandle) {
3298
+ try {
3299
+ const handle = await fileHandle.createSyncAccessHandle();
3300
+ return { handle, isMemory: false };
3301
+ } catch {
3302
+ const file = await fileHandle.getFile();
3303
+ const data = await file.arrayBuffer();
3304
+ return { handle: new MemoryHandle(data), isMemory: true };
3305
+ }
3306
+ }
3307
+ async function openFreshVFSHandle(fileHandle) {
3308
+ try {
3309
+ const handle = await fileHandle.createSyncAccessHandle();
3310
+ return { handle, isMemory: false };
3311
+ } catch {
3312
+ return { handle: new MemoryHandle(), isMemory: true };
3313
+ }
3314
+ }
3315
+ async function saveMemoryHandle(fileHandle, memHandle) {
3316
+ const writable = await fileHandle.createWritable();
3317
+ await writable.write(memHandle.getBuffer());
3318
+ await writable.close();
3319
+ }
3238
3320
  async function navigateToRoot(root) {
3239
3321
  let dir = await navigator.storage.getDirectory();
3240
3322
  if (root && root !== "/") {
@@ -3261,15 +3343,21 @@ async function writeOPFSFile(rootDir, path, data) {
3261
3343
  const parentDir = await ensureParentDirs(rootDir, path);
3262
3344
  const name = basename2(path);
3263
3345
  const fileHandle = await parentDir.getFileHandle(name, { create: true });
3264
- const syncHandle = await fileHandle.createSyncAccessHandle();
3265
3346
  try {
3266
- syncHandle.truncate(0);
3267
- if (data.byteLength > 0) {
3268
- syncHandle.write(data, { at: 0 });
3347
+ const syncHandle = await fileHandle.createSyncAccessHandle();
3348
+ try {
3349
+ syncHandle.truncate(0);
3350
+ if (data.byteLength > 0) {
3351
+ syncHandle.write(data, { at: 0 });
3352
+ }
3353
+ syncHandle.flush();
3354
+ } finally {
3355
+ syncHandle.close();
3269
3356
  }
3270
- syncHandle.flush();
3271
- } finally {
3272
- syncHandle.close();
3357
+ } catch {
3358
+ const writable = await fileHandle.createWritable();
3359
+ await writable.write(data);
3360
+ await writable.close();
3273
3361
  }
3274
3362
  }
3275
3363
  async function clearDirectory(dir, skip) {
@@ -3301,7 +3389,7 @@ async function readOPFSRecursive(dir, prefix, skip) {
3301
3389
  async function unpackToOPFS(root = "/") {
3302
3390
  const rootDir = await navigateToRoot(root);
3303
3391
  const vfsFileHandle = await rootDir.getFileHandle(".vfs.bin");
3304
- const handle = await vfsFileHandle.createSyncAccessHandle();
3392
+ const { handle } = await openVFSHandle(vfsFileHandle);
3305
3393
  let entries;
3306
3394
  try {
3307
3395
  const engine = new VFSEngine();
@@ -3339,7 +3427,7 @@ async function loadFromOPFS(root = "/") {
3339
3427
  } catch (_) {
3340
3428
  }
3341
3429
  const vfsFileHandle = await rootDir.getFileHandle(".vfs.bin", { create: true });
3342
- const handle = await vfsFileHandle.createSyncAccessHandle();
3430
+ const { handle, isMemory } = await openFreshVFSHandle(vfsFileHandle);
3343
3431
  try {
3344
3432
  const engine = new VFSEngine();
3345
3433
  engine.init(handle);
@@ -3356,6 +3444,9 @@ async function loadFromOPFS(root = "/") {
3356
3444
  files++;
3357
3445
  }
3358
3446
  engine.flush();
3447
+ if (isMemory) {
3448
+ await saveMemoryHandle(vfsFileHandle, handle);
3449
+ }
3359
3450
  return { files, directories };
3360
3451
  } finally {
3361
3452
  handle.close();
@@ -3457,7 +3548,7 @@ async function repairVFS(root = "/") {
3457
3548
  }
3458
3549
  await rootDir.removeEntry(".vfs.bin");
3459
3550
  const newFileHandle = await rootDir.getFileHandle(".vfs.bin", { create: true });
3460
- const handle = await newFileHandle.createSyncAccessHandle();
3551
+ const { handle, isMemory } = await openFreshVFSHandle(newFileHandle);
3461
3552
  try {
3462
3553
  const engine = new VFSEngine();
3463
3554
  engine.init(handle);
@@ -3478,6 +3569,9 @@ async function repairVFS(root = "/") {
3478
3569
  if (result.status !== 0) lost++;
3479
3570
  }
3480
3571
  engine.flush();
3572
+ if (isMemory) {
3573
+ await saveMemoryHandle(newFileHandle, handle);
3574
+ }
3481
3575
  } finally {
3482
3576
  handle.close();
3483
3577
  }