@poe-platform/safe-fs 0.1.815 → 0.1.817

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
@@ -15,6 +15,9 @@ To bound aggregate pages across multiple live stores, pass the same
15
15
  This shares a 256 KiB resident-page budget; `cache.residentBytes` reports current
16
16
  page retention. Eviction uses each store's filesystem, and each store still owns
17
17
  its `close()` cleanup. The budget excludes caller buffers and backend storage.
18
+ Call `await storage.prepare()` before opening a directory iterator when its
19
+ snapshot includes the scratch directory. It acquires and unlinks the backing
20
+ handle up front, so later spills do not mutate that directory's namespace.
18
21
  `IntegerTable.entries()` replays records in unsigned key order with a fixed
19
22
  16-level node stack and batches of at most 128 records. It accepts zero-filled
20
23
  caller storage with `allocate`, `read` and `write`; mutation during traversal
@@ -317,6 +320,8 @@ Every raw filesystem operation accepts an optional `signal`. Additional fields a
317
320
 
318
321
  `access` takes a separate mode bitmask from `ACCESS_MODES`. `chmod` takes a mode, `utimes` takes millisecond timestamps, and `truncate` takes a byte length (default 0). For conditional chmod, check `capabilitiesFor(path, { conditionalChmod: true })` (or `capabilities`) and require `conditionalChmod: true`; supply `parent`, `expected`, and complete root-to-parent `ancestors` together. An optional mutation-free `commitGuard` must return literal `true` synchronously. Memory validates at its metadata commit; mount and supported Memory overlays preserve wrapper ancestry. Real uses its existing externally isolated host-tree boundary and does not prevent races with other processes. Backend limits still apply. Node-shaped bridge methods translate their own options rather than accepting these raw option objects; see the [bridge signatures](src/bridge/filesystem.ts).
319
322
 
323
+ Cross-mount streamed copies start reading the source before opening the destination for writing. An initial read failure leaves the destination unchanged; later read or write failures can leave partial destination bytes.
324
+
320
325
  Overlay copy-up, copy, stream replacement/append and resize transfer retained ranges in 64 KiB chunks through the injected upper's stream writer. Resize streams the retained prefix and bounded zero chunks. Stream writes stage input before publication; cancellation or transfer failure leaves the destination unchanged and cleans staging. Stock Memory overlays transfer staged storage into existing upper inodes, preserving hardlinks without an additional whole-file buffer. `readFile` and `writeFile` remain buffering convenience APIs; use `readStream`/`writeStream` for large payloads. `maxBufferBytes` retains its existing optional total-byte admission semantics, not a working-memory guarantee.
321
326
 
322
327
  A streaming overlay needs an upper with `atomicRename`, `streamingWrite`, retained reads (`retainedRead` plus `openReadFile`), exclusive create, directory mutation, permissions and timestamps. Both layers must provide retained, bounded range reads with stable file and directory identities. For example, inject these host-authorized adapters with `createOverlayFileSystem({ upper: externalSpool, lower: retainedSource })`, then use `overlay.writeStream(path, source)`. The upper must await each consumed chunk, keep transport queues bounded, and preserve ownership until publication. Supply bounded chunks at the input boundary; the overlay cannot undo a payload-wide allocation already made by its caller. Whole-file-only uppers explicitly reject transfer operations with `ENOTSUP`; they do not silently collect streams.
@@ -1308,7 +1308,16 @@ export class MountFileSystem {
1308
1308
  const source = readBytes(reader.readStream(origin.local, options), options.signal);
1309
1309
  let failed = false;
1310
1310
  try {
1311
- await writer.writeStream(target.local, source, writeOptions);
1311
+ // Admit lazy reader startup before the writer can truncate its target.
1312
+ // Later transfer failures retain the backend's partial-write semantics.
1313
+ const first = await source.next();
1314
+ await writer.writeStream(target.local, (async function* () {
1315
+ let item = first;
1316
+ while (!item.done) {
1317
+ yield item.value;
1318
+ item = await source.next();
1319
+ }
1320
+ })(), writeOptions);
1312
1321
  }
1313
1322
  catch (error) {
1314
1323
  failed = true;
@@ -44,6 +44,8 @@ export declare class PagedStorage {
44
44
  private closing;
45
45
  constructor(context: PagedStorageContext, maxPages?: number, cache?: PagedStorageCache | undefined);
46
46
  allocate(length: number): number;
47
+ /** Acquire and unlink backing storage before observing a directory snapshot. */
48
+ prepare(): Promise<void>;
47
49
  private operation;
48
50
  private spill;
49
51
  private flush;
@@ -73,6 +73,11 @@ export class PagedStorage {
73
73
  this.end += length;
74
74
  return offset;
75
75
  }
76
+ /** Acquire and unlink backing storage before observing a directory snapshot. */
77
+ async prepare() {
78
+ await this.operation(async () => { if (!this.descriptor)
79
+ await this.spill(); });
80
+ }
76
81
  operation(action) {
77
82
  this.signal.throwIfAborted();
78
83
  const execute = () => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poe-platform/safe-fs",
3
- "version": "0.1.815",
3
+ "version": "0.1.817",
4
4
  "description": "Composable filesystem with a portable core and explicit Node adapters",
5
5
  "type": "module",
6
6
  "license": "MIT",