@componentor/fs 3.0.7 → 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,13 @@ 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
+
512
519
  ### v3.0.7 (2026)
513
520
 
514
521
  **Fixes:**
package/dist/index.js CHANGED
@@ -3236,6 +3236,87 @@ var VFSEngine = class {
3236
3236
  };
3237
3237
 
3238
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
+ }
3239
3320
  async function navigateToRoot(root) {
3240
3321
  let dir = await navigator.storage.getDirectory();
3241
3322
  if (root && root !== "/") {
@@ -3262,15 +3343,21 @@ async function writeOPFSFile(rootDir, path, data) {
3262
3343
  const parentDir = await ensureParentDirs(rootDir, path);
3263
3344
  const name = basename2(path);
3264
3345
  const fileHandle = await parentDir.getFileHandle(name, { create: true });
3265
- const syncHandle = await fileHandle.createSyncAccessHandle();
3266
3346
  try {
3267
- syncHandle.truncate(0);
3268
- if (data.byteLength > 0) {
3269
- 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();
3270
3356
  }
3271
- syncHandle.flush();
3272
- } finally {
3273
- syncHandle.close();
3357
+ } catch {
3358
+ const writable = await fileHandle.createWritable();
3359
+ await writable.write(data);
3360
+ await writable.close();
3274
3361
  }
3275
3362
  }
3276
3363
  async function clearDirectory(dir, skip) {
@@ -3302,7 +3389,7 @@ async function readOPFSRecursive(dir, prefix, skip) {
3302
3389
  async function unpackToOPFS(root = "/") {
3303
3390
  const rootDir = await navigateToRoot(root);
3304
3391
  const vfsFileHandle = await rootDir.getFileHandle(".vfs.bin");
3305
- const handle = await vfsFileHandle.createSyncAccessHandle();
3392
+ const { handle } = await openVFSHandle(vfsFileHandle);
3306
3393
  let entries;
3307
3394
  try {
3308
3395
  const engine = new VFSEngine();
@@ -3340,7 +3427,7 @@ async function loadFromOPFS(root = "/") {
3340
3427
  } catch (_) {
3341
3428
  }
3342
3429
  const vfsFileHandle = await rootDir.getFileHandle(".vfs.bin", { create: true });
3343
- const handle = await vfsFileHandle.createSyncAccessHandle();
3430
+ const { handle, isMemory } = await openFreshVFSHandle(vfsFileHandle);
3344
3431
  try {
3345
3432
  const engine = new VFSEngine();
3346
3433
  engine.init(handle);
@@ -3357,6 +3444,9 @@ async function loadFromOPFS(root = "/") {
3357
3444
  files++;
3358
3445
  }
3359
3446
  engine.flush();
3447
+ if (isMemory) {
3448
+ await saveMemoryHandle(vfsFileHandle, handle);
3449
+ }
3360
3450
  return { files, directories };
3361
3451
  } finally {
3362
3452
  handle.close();
@@ -3458,7 +3548,7 @@ async function repairVFS(root = "/") {
3458
3548
  }
3459
3549
  await rootDir.removeEntry(".vfs.bin");
3460
3550
  const newFileHandle = await rootDir.getFileHandle(".vfs.bin", { create: true });
3461
- const handle = await newFileHandle.createSyncAccessHandle();
3551
+ const { handle, isMemory } = await openFreshVFSHandle(newFileHandle);
3462
3552
  try {
3463
3553
  const engine = new VFSEngine();
3464
3554
  engine.init(handle);
@@ -3479,6 +3569,9 @@ async function repairVFS(root = "/") {
3479
3569
  if (result.status !== 0) lost++;
3480
3570
  }
3481
3571
  engine.flush();
3572
+ if (isMemory) {
3573
+ await saveMemoryHandle(newFileHandle, handle);
3574
+ }
3482
3575
  } finally {
3483
3576
  handle.close();
3484
3577
  }