@effect-vfs/core 0.0.1 → 0.1.0

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
@@ -10,7 +10,7 @@ Effect's standard `FileSystem` service, use [`@effect-vfs/memory`](https://www.n
10
10
  which adapts this package to that interface.
11
11
 
12
12
  The package implements a documented subset of POSIX behavior. It does not claim full POSIX conformance. See the
13
- [implemented profile](https://github.com/lloydrichards/effect-virtual-fs/blob/main/.docs/context/implemented-profile.md)
13
+ [implemented profile](https://github.com/lloydrichards/effect-virtual-fs/blob/main/.okf/profiles/implemented-filesystem.md)
14
14
  for the exact permission, path, timestamp, quota, and atomicity rules.
15
15
 
16
16
  ## Install
@@ -174,6 +174,53 @@ Snapshot decoding requires explicit work limits because encoded bytes may come f
174
174
  contains the reachable namespace and metadata. It excludes callers, open handles, cursor positions, watch
175
175
  subscriptions, and unlinked content. Each restored volume is independent.
176
176
 
177
+ ## Branch from one immutable snapshot
178
+
179
+ Use `makeOverlay` when several writable workspaces start from the same snapshot. Untouched regular-file contents are
180
+ shared internally. A content change copies that whole file for the editing workspace; namespace and metadata state are
181
+ always private. Logical quotas still count the complete visible volume, including shared base files and unlinked-open
182
+ contents.
183
+
184
+ ```ts
185
+ import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
186
+ import { Effect } from "effect"
187
+
188
+ const branch = Effect.gen(function*() {
189
+ const template = yield* Vfs.fromFixture({
190
+ entries: [
191
+ { kind: "directory", path: "/project" },
192
+ { kind: "file", path: "/project/settings.json", bytes: new Uint8Array() }
193
+ ]
194
+ })
195
+ const base = yield* template.snapshot
196
+ const workspace = yield* Vfs.makeOverlay(base, { maxBytes: 1_000_000 })
197
+ const fs = yield* workspace.caller()
198
+
199
+ yield* fs.rename("/project/settings.json", "/project/preferences.json")
200
+ return yield* workspace.capture()
201
+ })
202
+ ```
203
+
204
+ `makeOverlay(base, options)` returns a reusable Effect. Each execution creates a fresh workspace from `base`; it does
205
+ not memoize or reset an earlier workspace. Invalid volume options fail with `ConfigurationError`, while failure to
206
+ inspect the base snapshot fails with `ImageError`.
207
+
208
+ `changes()` reports final differences from the base. It hides timestamp-only differences unless called with
209
+ `{ includeTimestamps: true }`. Renames are paired only from retained object identity; equal contents are never treated
210
+ as rename evidence. Paths in summaries are `BytePath` values, so inspect them with `pathToBytes` when names may not be
211
+ UTF-8. Although `OverlayChange` and its options have schemas, a summary is an in-process value containing opaque
212
+ `BytePath` objects. The schema is not a portable summary encoding.
213
+
214
+ `capture()` returns a complete version 1 snapshot and matching summary from one committed state. Later writes change
215
+ neither result. The snapshot works with `encodeSnapshot`, `fromSnapshot`, and `@effect-vfs/persistence` checkpoints.
216
+ Restoring it recovers the complete filesystem state, not the earlier overlay base relationship or summary. To reset,
217
+ create a fresh overlay from the base and replace your application reference; existing callers, handles, and watches
218
+ remain attached to the old workspace until their own lifetimes end.
219
+
220
+ The Effects returned by `changes(options)` and `capture(options)` are also reusable. Each execution observes the
221
+ workspace again. Invalid summary options fail with `ConfigurationError`; failure to construct the complete observed
222
+ snapshot fails with `ImageError`. Reusing a previously completed `capture()` result does not observe later writes.
223
+
177
224
  ## Use scoped handles for incremental I/O
178
225
 
179
226
  Whole-file operations are convenient, but handles give each open file an independent `bigint` cursor and support
@@ -280,4 +327,4 @@ candidate.
280
327
  - `sync` checks handle liveness. An in-memory volume provides no host or crash durability.
281
328
 
282
329
  For the complete contract, read the
283
- [implemented profile](https://github.com/lloydrichards/effect-virtual-fs/blob/main/.docs/context/implemented-profile.md).
330
+ [implemented profile](https://github.com/lloydrichards/effect-virtual-fs/blob/main/.okf/profiles/implemented-filesystem.md).
@@ -19,6 +19,13 @@ declare const DirectoryHandleId: unique symbol;
19
19
  export interface BytePath {
20
20
  readonly [BytePathId]: true;
21
21
  }
22
+ /**
23
+ * Schema for an opaque byte-preserving filesystem path.
24
+ *
25
+ * @category schemas
26
+ * @since 0.1.0
27
+ */
28
+ export declare const BytePath: Schema.declare<BytePath, BytePath>;
22
29
  /**
23
30
  * A UTF-8 string path or an opaque byte-preserving path.
24
31
  *
@@ -450,6 +457,102 @@ export interface Change {
450
457
  /** Absolute path of the changed entry. */
451
458
  readonly path: BytePath;
452
459
  }
460
+ /**
461
+ * Schema for the filesystem entry kinds reported by overlay summaries.
462
+ *
463
+ * @category schemas
464
+ * @since 0.1.0
465
+ */
466
+ export declare const OverlayNodeKind: Schema.Literals<readonly ["directory", "file", "symlink"]>;
467
+ /**
468
+ * A filesystem entry kind reported by an overlay summary.
469
+ *
470
+ * @category models
471
+ * @since 0.1.0
472
+ */
473
+ export type OverlayNodeKind = typeof OverlayNodeKind.Type;
474
+ /**
475
+ * Schema for observable fields that can differ from an overlay's immutable base.
476
+ *
477
+ * @category schemas
478
+ * @since 0.1.0
479
+ */
480
+ export declare const OverlayDifference: Schema.Literals<readonly ["content", "mode", "uid", "gid", "atimeNs", "mtimeNs", "ctimeNs", "birthtimeNs"]>;
481
+ /**
482
+ * A content, ownership, permission, or timestamp field that differs from the overlay base.
483
+ *
484
+ * @category models
485
+ * @since 0.1.0
486
+ */
487
+ export type OverlayDifference = typeof OverlayDifference.Type;
488
+ /**
489
+ * Schema for a final-state overlay difference.
490
+ *
491
+ * **Details**
492
+ *
493
+ * Paths retain arbitrary non-NUL bytes. Renames are reported only when retained
494
+ * base identity makes the removed and added names unambiguous.
495
+ *
496
+ * @category schemas
497
+ * @since 0.1.0
498
+ */
499
+ export declare const OverlayChange: Schema.Union<readonly [Schema.TaggedStruct<"Added", {
500
+ readonly path: Schema.declare<BytePath, BytePath>;
501
+ readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]>;
502
+ }>, Schema.TaggedStruct<"Removed", {
503
+ readonly path: Schema.declare<BytePath, BytePath>;
504
+ readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]>;
505
+ }>, Schema.TaggedStruct<"Replaced", {
506
+ readonly path: Schema.declare<BytePath, BytePath>;
507
+ readonly beforeKind: Schema.Literals<readonly ["directory", "file", "symlink"]>;
508
+ readonly afterKind: Schema.Literals<readonly ["directory", "file", "symlink"]>;
509
+ readonly differences: Schema.$Array<Schema.Literals<readonly ["content", "mode", "uid", "gid", "atimeNs", "mtimeNs", "ctimeNs", "birthtimeNs"]>>;
510
+ }>, Schema.TaggedStruct<"Renamed", {
511
+ readonly from: Schema.declare<BytePath, BytePath>;
512
+ readonly to: Schema.declare<BytePath, BytePath>;
513
+ readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]>;
514
+ readonly differences: Schema.$Array<Schema.Literals<readonly ["content", "mode", "uid", "gid", "atimeNs", "mtimeNs", "ctimeNs", "birthtimeNs"]>>;
515
+ }>, Schema.TaggedStruct<"Updated", {
516
+ readonly path: Schema.declare<BytePath, BytePath>;
517
+ readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]>;
518
+ readonly differences: Schema.$Array<Schema.Literals<readonly ["content", "mode", "uid", "gid", "atimeNs", "mtimeNs", "ctimeNs", "birthtimeNs"]>>;
519
+ }>]>;
520
+ /**
521
+ * A path-oriented final-state difference from an overlay's immutable base.
522
+ *
523
+ * @category models
524
+ * @since 0.1.0
525
+ */
526
+ export type OverlayChange = typeof OverlayChange.Type;
527
+ /**
528
+ * Schema for overlay summary filtering.
529
+ *
530
+ * @category schemas
531
+ * @since 0.1.0
532
+ */
533
+ export declare const OverlayChangesOptions: Schema.Struct<{
534
+ /** Include access, modification, change, and birth-time differences. Defaults to `false`. */
535
+ readonly includeTimestamps: Schema.optionalKey<Schema.Boolean>;
536
+ }>;
537
+ /**
538
+ * Filtering options for an overlay final-difference summary.
539
+ *
540
+ * @category models
541
+ * @since 0.1.0
542
+ */
543
+ export type OverlayChangesOptions = typeof OverlayChangesOptions.Type;
544
+ /**
545
+ * A complete snapshot and final-difference summary captured from one committed state.
546
+ *
547
+ * @category models
548
+ * @since 0.1.0
549
+ */
550
+ export interface OverlayCapture {
551
+ /** Complete version 1 snapshot owned by this capture. */
552
+ readonly snapshot: Snapshot;
553
+ /** Owned summary that describes the same state as `snapshot`. */
554
+ readonly changes: ReadonlyArray<OverlayChange>;
555
+ }
453
556
  /**
454
557
  * An isolated virtual filesystem namespace that creates callers, snapshots, and watch streams.
455
558
  *
@@ -465,6 +568,18 @@ export interface Volume {
465
568
  /** Creates a caller rooted at `/` with independent credentials, umask, and current directory. */
466
569
  readonly caller: (options?: RootCallerOptions) => Effect.Effect<Caller, ConfigurationError>;
467
570
  }
571
+ /**
572
+ * An ordinary volume with final-state inspection relative to one immutable snapshot base.
573
+ *
574
+ * @category models
575
+ * @since 0.1.0
576
+ */
577
+ export interface OverlayVolume extends Volume {
578
+ /** Computes final differences from the immutable base when this reusable effect executes. */
579
+ readonly changes: (options?: OverlayChangesOptions) => Effect.Effect<ReadonlyArray<OverlayChange>, ConfigurationError | ImageError>;
580
+ /** Captures one committed state when this reusable effect executes. */
581
+ readonly capture: (options?: OverlayChangesOptions) => Effect.Effect<OverlayCapture, ConfigurationError | ImageError>;
582
+ }
468
583
  declare const CurrentFileSystem_base: Context.ServiceClass<CurrentFileSystem, "@effect-vfs/core/CurrentFileSystem", Caller>;
469
584
  /**
470
585
  * Optional Effect service for providing an existing filesystem caller.
@@ -535,6 +650,28 @@ export declare const fromSnapshot: (snapshot: Snapshot, options?: {
535
650
  readonly maxFileBytes?: number;
536
651
  readonly maxPathBytes?: number;
537
652
  } | undefined) => Effect.Effect<Volume, ConfigurationError | ImageError, never>;
653
+ /**
654
+ * Creates an isolated writable volume relative to one immutable snapshot base.
655
+ *
656
+ * **Details**
657
+ *
658
+ * Workspaces made from the same snapshot share unchanged regular-file payloads.
659
+ * The first content mutation copies the whole file into workspace-private
660
+ * storage. Metadata, namespace state, coordination, handles, and watches are
661
+ * always private to the new workspace.
662
+ *
663
+ * Invalid base snapshots fail with `ImageError`; invalid volume limits fail
664
+ * with `ConfigurationError`. Each execution creates a fresh workspace.
665
+ *
666
+ * @category constructors
667
+ * @since 0.1.0
668
+ */
669
+ export declare const makeOverlay: (base: Snapshot, options?: {
670
+ readonly maxEntries?: number;
671
+ readonly maxBytes?: number;
672
+ readonly maxFileBytes?: number;
673
+ readonly maxPathBytes?: number;
674
+ } | undefined) => Effect.Effect<OverlayVolume, ConfigurationError | ImageError, never>;
538
675
  /**
539
676
  * Schema for a complete fixture namespace with optional metadata and forward hard links.
540
677
  *
@@ -1 +1 @@
1
- {"version":3,"file":"VirtualFileSystem.d.ts","sourceRoot":"","sources":["../src/VirtualFileSystem.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAA;AACvE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEvF,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAIvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAErC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC,QAAA,MAAM,UAAU,eAAsC,CAAA;AACtD,QAAA,MAAM,QAAQ,eAAoC,CAAA;AAClD,QAAA,MAAM,QAAQ,eAAoC,CAAA;AAClD,QAAA,MAAM,YAAY,eAAwC,CAAA;AAC1D,QAAA,MAAM,iBAAiB,eAA6C,CAAA;AAEpE;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,CAAC,UAAU,CAAC,EAAE,IAAI,CAAA;CAC5B;AACD;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAA;AAEzC;;;;;GAKG;AACH,eAAO,MAAM,MAAM,0SAkBjB,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAA;;;;AACvC;;;;;GAKG;AACH,qBAAa,OAAQ,SAAQ,aAA4B;IACvD,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,2CAA2C;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,iEAAiE;IACjE,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAC1B,CAAC;CAAG;;;;AACL;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,wBAAuC;IAC7E,mCAAmC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB,CAAC;CAAG;AAQL;;;;;GAKG;AACH,eAAO,MAAM,QAAQ;IACnB,uEAAuE;;IAEvE,wCAAwC;;IAExC,uEAAuE;;IAEvE,oEAAoE;;EAEpE,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;AAC3C;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB;IAC5B,+DAA+D;;QAvB/D,uEAAuE;;QAEvE,wCAAwC;;QAExC,uEAAuE;;QAEvE,oEAAoE;;;IAmBpE,qEAAqE;;EAErE,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,iBAAiB,CAAC,IAAI,CAAA;AAC7D;;;;;GAKG;AACH,eAAO,MAAM,aAAa;IACxB,+EAA+E;;IAE/E,sDAAsD;;IAEtD,yDAAyD;;IAEzD,mEAAmE;;EAEnE,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAA;AAOrD;;;;;GAKG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;EAYnB,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;AAE3C;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,2GAA2G;IAC3G,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAA;CACtC;AACD;;;;;GAKG;AACH,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD,0DAA0D;IAC1D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CACtC;AACD;;;;;GAKG;AACH,eAAO,MAAM,WAAW;;;EAAwF,CAAA;AAChH;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,IAAI,CAAA;AACjD;;;;;GAKG;AACH,eAAO,MAAM,UAAU;;;;;;;IAIrB,CAAA;AACF;;;;;GAKG;AACH,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;EAAkE,CAAA;AACpF;;;;;GAKG;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AACrC;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,CAAC,iBAAiB,CAAC,EAAE,IAAI,CAAA;IAClC,sEAAsE;IACtE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC/C,sFAAsF;IACtF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;CAC7C;AACD;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,uEAA+D,CAAA;AACpF;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;AAC3C;;;;;GAKG;AACH,eAAO,MAAM,YAAY;IACvB,mDAAmD;;IAEnD,8CAA8C;;IAE9C,yEAAyE;;IAEzE,wEAAwE;;IAExE,mEAAmE;;IAEnE,0DAA0D;;EAE1D,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,YAAY,CAAC,IAAI,GAAG,eAAe,CAAA;AACpE,QAAA,MAAM,iBAAiB;IApBrB,mDAAmD;;IAEnD,8CAA8C;;IAE9C,yEAAyE;;IAEzE,wEAAwE;;IAExE,mEAAmE;;IAEnE,0DAA0D;;IAY1D,oEAAoE;;IAEpE,sDAAsD;;EAEtD,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,OAAO,iBAAiB,CAAC,IAAI,GAAG,eAAe,CAAA;AAC9E;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,CAAC,YAAY,CAAC,EAAE,IAAI,CAAA;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,IAAI,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IAC3E,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IAC5F,6FAA6F;IAC7F,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACrE,qGAAqG;IACrG,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACtF,sGAAsG;IACtG,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjF,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACnE,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC/C,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC3C,sFAAsF;IACtF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;CAC7C;AACD;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAA;IACzB,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC/F,4FAA4F;IAC5F,QAAQ,CAAC,MAAM,EAAE,CACf,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,SAAS,EACtB,OAAO,CAAC,EAAE;QACR,iDAAiD;QACjD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,eAAe,CAAA;QAC3C,sDAAsD;QACtD,QAAQ,CAAC,qBAAqB,CAAC,EAAE,eAAe,CAAA;KACjD,KACE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IACrG,sFAAsF;IACtF,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACnH,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC5G,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC/G,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC1G,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAChH,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC3G,kEAAkE;IAClE,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC1G,oEAAoE;IACpE,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,EAAE,KAAK,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAChH,gFAAgF;IAChF,QAAQ,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,EAAE,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC3G,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAChG,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,CACb,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,SAAS,EACtB,OAAO,CAAC,EAAE;QACR,iDAAiD;QACjD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,eAAe,CAAA;QAC3C,sDAAsD;QACtD,QAAQ,CAAC,qBAAqB,CAAC,EAAE,eAAe,CAAA;QAChD,2EAA2E;QAC3E,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAA;KACvC,KACE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACjC,mFAAmF;IACnF,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACjH,iGAAiG;IACjG,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjG,mDAAmD;IACnD,QAAQ,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IAC1G,gFAAgF;IAChF,QAAQ,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAA;IACrH,kDAAkD;IAClD,QAAQ,CAAC,kBAAkB,EAAE,CAC3B,IAAI,EAAE,SAAS,EACf,OAAO,CAAC,EAAE,eAAe,KACtB,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAA;IACtD,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjG,0EAA0E;IAC1E,QAAQ,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IACxG,yFAAyF;IACzF,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACzG,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC7F,kCAAkC;IAClC,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC5F,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,CACd,IAAI,EAAE,SAAS,EACf,OAAO,CAAC,EAAE,eAAe,GAAG;QAC1B,8EAA8E;QAC9E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KACvB,KACE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACjC,0FAA0F;IAC1F,QAAQ,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACnH,iFAAiF;IACjF,QAAQ,CAAC,aAAa,EAAE,CACtB,IAAI,EAAE,SAAS,EACf,OAAO,CAAC,EAAE,eAAe,KACtB,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;CAC1D;AACD;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACrB,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;IAC7C,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CACxB;AACD;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACrB,kFAAkF;IAClF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACxE,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;IACtD,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAA;IACzB,iGAAiG;IACjG,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,iBAAiB,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;CAC5F;;AACD;;;;;GAKG;AACH,qBAAa,iBACX,SAAQ,sBAAkF;CAC1F;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAwB,CAAA;AAEjH;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,CAC3B,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,YAAY,KACjB,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAwB,CAAA;AAgC/D;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,iFAUxB,CAAA;AAEF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,4EAItB,CAAA;AA80CF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,IAAI;;;;;kEAEf,CAAA;AACF;;;;;GAKG;AACH,eAAO,MAAM,YAAY;;;;;+EAKxB,CAAA;AAiBD;;;;;;;;;GASG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBlB,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AACzC;;;;;GAKG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+EAoIvB,CAAA"}
1
+ {"version":3,"file":"VirtualFileSystem.d.ts","sourceRoot":"","sources":["../src/VirtualFileSystem.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,MAAM,eAAe,CAAA;AACvE,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,KAAK,QAAQ,EAAE,cAAc,EAAE,MAAM,eAAe,CAAA;AAEvF,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAIvC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AAErC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAMvC,QAAA,MAAM,UAAU,eAAsC,CAAA;AACtD,QAAA,MAAM,QAAQ,eAAoC,CAAA;AAClD,QAAA,MAAM,QAAQ,eAAoC,CAAA;AAClD,QAAA,MAAM,YAAY,eAAwC,CAAA;AAC1D,QAAA,MAAM,iBAAiB,eAA6C,CAAA;AAGpE;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,CAAC,UAAU,CAAC,EAAE,IAAI,CAAA;CAC5B;AACD;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,oCAEpB,CAAA;AACD;;;;;GAKG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,QAAQ,CAAA;AAEzC;;;;;GAKG;AACH,eAAO,MAAM,MAAM,0SAkBjB,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,MAAM,GAAG,OAAO,MAAM,CAAC,IAAI,CAAA;;;;AACvC;;;;;GAKG;AACH,qBAAa,OAAQ,SAAQ,aAA4B;IACvD,+CAA+C;IAC/C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,2CAA2C;IAC3C,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,iEAAiE;IACjE,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAA;CAC1B,CAAC;CAAG;;;;AACL;;;;;GAKG;AACH,qBAAa,kBAAmB,SAAQ,wBAAuC;IAC7E,mCAAmC;IACnC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CACvB,CAAC;CAAG;AAQL;;;;;GAKG;AACH,eAAO,MAAM,QAAQ;IACnB,uEAAuE;;IAEvE,wCAAwC;;IAExC,uEAAuE;;IAEvE,oEAAoE;;EAEpE,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;AAC3C;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB;IAC5B,+DAA+D;;QAvB/D,uEAAuE;;QAEvE,wCAAwC;;QAExC,uEAAuE;;QAEvE,oEAAoE;;;IAmBpE,qEAAqE;;EAErE,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,iBAAiB,CAAC,IAAI,CAAA;AAC7D;;;;;GAKG;AACH,eAAO,MAAM,aAAa;IACxB,+EAA+E;;IAE/E,sDAAsD;;IAEtD,yDAAyD;;IAEzD,mEAAmE;;EAEnE,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAA;AAOrD;;;;;GAKG;AACH,eAAO,MAAM,QAAQ;;;;;;;;;;;;EAYnB,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;AAE3C;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,2GAA2G;IAC3G,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAA;CACtC;AACD;;;;;GAKG;AACH,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD,0DAA0D;IAC1D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;CACtC;AACD;;;;;GAKG;AACH,eAAO,MAAM,WAAW;;;EAAwF,CAAA;AAChH;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,WAAW,CAAC,IAAI,CAAA;AACjD;;;;;GAKG;AACH,eAAO,MAAM,UAAU;;;;;;;IAIrB,CAAA;AACF;;;;;GAKG;AACH,eAAO,MAAM,KAAK;;;;;;;;;;;;;;;;;EAAkE,CAAA;AACpF;;;;;GAKG;AACH,MAAM,MAAM,KAAK,GAAG,OAAO,KAAK,CAAC,IAAI,CAAA;AACrC;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,CAAC,iBAAiB,CAAC,EAAE,IAAI,CAAA;IAClC,sEAAsE;IACtE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC/C,sFAAsF;IACtF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;CAC7C;AACD;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,uEAA+D,CAAA;AACpF;;;;;GAKG;AACH,MAAM,MAAM,QAAQ,GAAG,OAAO,QAAQ,CAAC,IAAI,CAAA;AAC3C;;;;;GAKG;AACH,eAAO,MAAM,YAAY;IACvB,mDAAmD;;IAEnD,8CAA8C;;IAE9C,yEAAyE;;IAEzE,wEAAwE;;IAExE,mEAAmE;;IAEnE,0DAA0D;;EAE1D,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,OAAO,YAAY,CAAC,IAAI,GAAG,eAAe,CAAA;AACpE,QAAA,MAAM,iBAAiB;IApBrB,mDAAmD;;IAEnD,8CAA8C;;IAE9C,yEAAyE;;IAEzE,wEAAwE;;IAExE,mEAAmE;;IAEnE,0DAA0D;;IAY1D,oEAAoE;;IAEpE,sDAAsD;;EAEtD,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,OAAO,iBAAiB,CAAC,IAAI,GAAG,eAAe,CAAA;AAC9E;;;;;GAKG;AACH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,CAAC,YAAY,CAAC,EAAE,IAAI,CAAA;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,IAAI,EAAE,CAAC,YAAY,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IAC3E,qDAAqD;IACrD,QAAQ,CAAC,KAAK,EAAE,CAAC,YAAY,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IAC5F,6FAA6F;IAC7F,QAAQ,CAAC,KAAK,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACrE,qGAAqG;IACrG,QAAQ,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACtF,sGAAsG;IACtG,QAAQ,CAAC,IAAI,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjF,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACnE,wCAAwC;IACxC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC/C,0FAA0F;IAC1F,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC3C,sFAAsF;IACtF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;CAC7C;AACD;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAA;IACzB,oEAAoE;IACpE,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAC/F,4FAA4F;IAC5F,QAAQ,CAAC,MAAM,EAAE,CACf,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,SAAS,EACtB,OAAO,CAAC,EAAE;QACR,iDAAiD;QACjD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,eAAe,CAAA;QAC3C,sDAAsD;QACtD,QAAQ,CAAC,qBAAqB,CAAC,EAAE,eAAe,CAAA;KACjD,KACE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACjC,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IACrG,sFAAsF;IACtF,QAAQ,CAAC,SAAS,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,OAAO,EAAE,gBAAgB,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACnH,sEAAsE;IACtE,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC5G,4EAA4E;IAC5E,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC/G,6EAA6E;IAC7E,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC1G,+EAA+E;IAC/E,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAChH,2FAA2F;IAC3F,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC3G,kEAAkE;IAClE,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC1G,oEAAoE;IACpE,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,EAAE,KAAK,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAChH,gFAAgF;IAChF,QAAQ,CAAC,YAAY,EAAE,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,EAAE,KAAK,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC3G,gEAAgE;IAChE,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IAChG,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,EAAE,CACb,MAAM,EAAE,SAAS,EACjB,WAAW,EAAE,SAAS,EACtB,OAAO,CAAC,EAAE;QACR,iDAAiD;QACjD,QAAQ,CAAC,gBAAgB,CAAC,EAAE,eAAe,CAAA;QAC3C,sDAAsD;QACtD,QAAQ,CAAC,qBAAqB,CAAC,EAAE,eAAe,CAAA;QAChD,2EAA2E;QAC3E,QAAQ,CAAC,mBAAmB,CAAC,EAAE,OAAO,CAAA;KACvC,KACE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACjC,mFAAmF;IACnF,QAAQ,CAAC,OAAO,EAAE,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACjH,iGAAiG;IACjG,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjG,mDAAmD;IACnD,QAAQ,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,CAAA;IAC1G,gFAAgF;IAChF,QAAQ,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC,CAAA;IACrH,kDAAkD;IAClD,QAAQ,CAAC,kBAAkB,EAAE,CAC3B,IAAI,EAAE,SAAS,EACf,OAAO,CAAC,EAAE,eAAe,KACtB,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAA;IACtD,qDAAqD;IACrD,QAAQ,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjG,0EAA0E;IAC1E,QAAQ,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAA;IACxG,yFAAyF;IACzF,QAAQ,CAAC,IAAI,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACzG,8EAA8E;IAC9E,QAAQ,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC7F,kCAAkC;IAClC,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAC5F,oEAAoE;IACpE,QAAQ,CAAC,KAAK,EAAE,CACd,IAAI,EAAE,SAAS,EACf,OAAO,CAAC,EAAE,eAAe,GAAG;QAC1B,8EAA8E;QAC9E,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KACvB,KACE,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IACjC,0FAA0F;IAC1F,QAAQ,CAAC,aAAa,EAAE,CAAC,IAAI,EAAE,SAAS,EAAE,OAAO,CAAC,EAAE,eAAe,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACnH,iFAAiF;IACjF,QAAQ,CAAC,aAAa,EAAE,CACtB,IAAI,EAAE,SAAS,EACf,OAAO,CAAC,EAAE,eAAe,KACtB,MAAM,CAAC,MAAM,CAAC,eAAe,EAAE,OAAO,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;CAC1D;AACD;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACrB,qDAAqD;IACrD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAA;IAC7C,0CAA0C;IAC1C,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CACxB;AACD;;;;;GAKG;AACH,eAAO,MAAM,eAAe,4DAAoD,CAAA;AAChF;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GAAG,OAAO,eAAe,CAAC,IAAI,CAAA;AACzD;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,6GAS5B,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,iBAAiB,GAAG,OAAO,iBAAiB,CAAC,IAAI,CAAA;AAG7D;;;;;;;;;;GAUG;AACH,eAAO,MAAM,aAAa;;;;;;;;;;;;;;;;;;;;IAgBxB,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,OAAO,aAAa,CAAC,IAAI,CAAA;AACrD;;;;;GAKG;AACH,eAAO,MAAM,qBAAqB;IAChC,6FAA6F;;EAE7F,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,qBAAqB,GAAG,OAAO,qBAAqB,CAAC,IAAI,CAAA;AACrE;;;;;GAKG;AACH,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAA;IAC3B,iEAAiE;IACjE,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,aAAa,CAAC,CAAA;CAC/C;AACD;;;;;GAKG;AACH,MAAM,WAAW,MAAM;IACrB,kFAAkF;IAClF,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,CAAC,CAAA;IACxE,6EAA6E;IAC7E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAC,CAAA;IACtD,QAAQ,CAAC,CAAC,QAAQ,CAAC,EAAE,IAAI,CAAA;IACzB,iGAAiG;IACjG,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,iBAAiB,KAAK,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAA;CAC5F;AACD;;;;;GAKG;AACH,MAAM,WAAW,aAAc,SAAQ,MAAM;IAC3C,6FAA6F;IAC7F,QAAQ,CAAC,OAAO,EAAE,CAChB,OAAO,CAAC,EAAE,qBAAqB,KAC5B,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,aAAa,CAAC,EAAE,kBAAkB,GAAG,UAAU,CAAC,CAAA;IACjF,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,CAChB,OAAO,CAAC,EAAE,qBAAqB,KAC5B,MAAM,CAAC,MAAM,CAAC,cAAc,EAAE,kBAAkB,GAAG,UAAU,CAAC,CAAA;CACpE;;AACD;;;;;GAKG;AACH,qBAAa,iBACX,SAAQ,sBAAkF;CAC1F;AAEF;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,CAAC,QAAQ,EAAE,QAAQ,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,UAAU,CAAwB,CAAA;AAEjH;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAE,CAC3B,KAAK,EAAE,UAAU,EACjB,MAAM,EAAE,YAAY,KACjB,MAAM,CAAC,MAAM,CAAC,QAAQ,EAAE,UAAU,CAAwB,CAAA;AA+B/D;;;;;;;;;GASG;AACH,eAAO,MAAM,aAAa,iFAUxB,CAAA;AAEF;;;;;GAKG;AACH,eAAO,MAAM,WAAW,4EAItB,CAAA;AA27CF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,IAAI;;;;;kEAIf,CAAA;AACF;;;;;GAKG;AACH,eAAO,MAAM,YAAY;;;;;+EAOxB,CAAA;AAED;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,WAAW;;;;;sFAUvB,CAAA;AAiBD;;;;;;;;;GASG;AACH,eAAO,MAAM,OAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBlB,CAAA;AACF;;;;;GAKG;AACH,MAAM,MAAM,OAAO,GAAG,OAAO,OAAO,CAAC,IAAI,CAAA;AACzC;;;;;GAKG;AACH,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;+EAuIvB,CAAA"}
@@ -21,12 +21,23 @@ import * as Schema from "effect/Schema";
21
21
  import * as Scope from "effect/Scope";
22
22
  import * as Semaphore from "effect/Semaphore";
23
23
  import * as Stream from "effect/Stream";
24
+ import * as Content from "./internal/content.js";
24
25
  import * as Image from "./internal/image.js";
26
+ import { compareOverlay } from "./internal/overlayChanges.js";
27
+ import * as OverlayTesting from "./internal/overlayTesting.js";
25
28
  const BytePathId = Symbol("@effect-vfs/core/BytePath");
26
29
  const VolumeId = Symbol("@effect-vfs/core/Volume");
27
30
  const CallerId = Symbol("@effect-vfs/core/Caller");
28
31
  const FileHandleId = Symbol("@effect-vfs/core/FileHandle");
29
32
  const DirectoryHandleId = Symbol("@effect-vfs/core/DirectoryHandle");
33
+ const bytePaths = new WeakMap();
34
+ /**
35
+ * Schema for an opaque byte-preserving filesystem path.
36
+ *
37
+ * @category schemas
38
+ * @since 0.1.0
39
+ */
40
+ export const BytePath = Schema.declare((value) => typeof value === "object" && value !== null && bytePaths.has(value));
30
41
  /**
31
42
  * Schema for portable virtual filesystem error codes.
32
43
  *
@@ -195,6 +206,69 @@ const WriteFileSettings = Schema.Struct({
195
206
  /** Mode to apply after replacing an existing file. */
196
207
  finalMode: Schema.optionalKey(Mode)
197
208
  });
209
+ /**
210
+ * Schema for the filesystem entry kinds reported by overlay summaries.
211
+ *
212
+ * @category schemas
213
+ * @since 0.1.0
214
+ */
215
+ export const OverlayNodeKind = Schema.Literals(["directory", "file", "symlink"]);
216
+ /**
217
+ * Schema for observable fields that can differ from an overlay's immutable base.
218
+ *
219
+ * @category schemas
220
+ * @since 0.1.0
221
+ */
222
+ export const OverlayDifference = Schema.Literals([
223
+ "content",
224
+ "mode",
225
+ "uid",
226
+ "gid",
227
+ "atimeNs",
228
+ "mtimeNs",
229
+ "ctimeNs",
230
+ "birthtimeNs"
231
+ ]);
232
+ const OverlayDifferences = Schema.Array(OverlayDifference);
233
+ const NonEmptyOverlayDifferences = OverlayDifferences.check(Schema.isMinLength(1));
234
+ /**
235
+ * Schema for a final-state overlay difference.
236
+ *
237
+ * **Details**
238
+ *
239
+ * Paths retain arbitrary non-NUL bytes. Renames are reported only when retained
240
+ * base identity makes the removed and added names unambiguous.
241
+ *
242
+ * @category schemas
243
+ * @since 0.1.0
244
+ */
245
+ export const OverlayChange = Schema.Union([
246
+ Schema.TaggedStruct("Added", { path: BytePath, kind: OverlayNodeKind }),
247
+ Schema.TaggedStruct("Removed", { path: BytePath, kind: OverlayNodeKind }),
248
+ Schema.TaggedStruct("Replaced", {
249
+ path: BytePath,
250
+ beforeKind: OverlayNodeKind,
251
+ afterKind: OverlayNodeKind,
252
+ differences: OverlayDifferences
253
+ }),
254
+ Schema.TaggedStruct("Renamed", {
255
+ from: BytePath,
256
+ to: BytePath,
257
+ kind: OverlayNodeKind,
258
+ differences: OverlayDifferences
259
+ }),
260
+ Schema.TaggedStruct("Updated", { path: BytePath, kind: OverlayNodeKind, differences: NonEmptyOverlayDifferences })
261
+ ]);
262
+ /**
263
+ * Schema for overlay summary filtering.
264
+ *
265
+ * @category schemas
266
+ * @since 0.1.0
267
+ */
268
+ export const OverlayChangesOptions = Schema.Struct({
269
+ /** Include access, modification, change, and birth-time differences. Defaults to `false`. */
270
+ includeTimestamps: Schema.optionalKey(Schema.Boolean)
271
+ });
198
272
  /**
199
273
  * Optional Effect service for providing an existing filesystem caller.
200
274
  *
@@ -217,7 +291,6 @@ export const encodeSnapshot = Image.encodeSnapshot;
217
291
  * @since 0.1.0
218
292
  */
219
293
  export const decodeSnapshot = Image.decodeSnapshot;
220
- const bytePaths = new WeakMap();
221
294
  const failure = (code, operation, path) => new FsError({ code, operation, ...(path === undefined ? {} : { path }) });
222
295
  const ownedPath = (bytes) => {
223
296
  const path = Object.freeze({ [BytePathId]: true });
@@ -370,7 +443,8 @@ const storedMetadata = (metadata) => ({
370
443
  birthtimeNs: String(metadata.birthtimeNs)
371
444
  });
372
445
  /** Each execution constructs a fresh volume and captures its Clock. */
373
- const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options, image) {
446
+ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (source, options) {
447
+ const image = source._tag === "Empty" ? undefined : source.image;
374
448
  const decoded = decodeConfiguration(VolumeOptions, options === undefined ? {} : options);
375
449
  if (Result.isFailure(decoded))
376
450
  return yield* decoded.failure;
@@ -388,6 +462,7 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
388
462
  const gate = Semaphore.makeUnsafe(1);
389
463
  const root = {
390
464
  kind: "directory",
465
+ lineage: image?.root,
391
466
  parent: undefined,
392
467
  entries: new Map(),
393
468
  metadata: directoryMetadata(1n, 0, 0, 0o755, initialTime)
@@ -415,6 +490,7 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
415
490
  (settings.maxBytes !== undefined && content > settings.maxBytes)) {
416
491
  return yield* new ImageError({ code: "LimitExceeded", field: "volume" });
417
492
  }
493
+ const baseContents = source._tag === "Overlay" ? Content.forOverlay(source.base, image) : undefined;
418
494
  for (const record of image.records) {
419
495
  const metadata = {
420
496
  ...record.metadata,
@@ -430,22 +506,28 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
430
506
  if (record.kind === "directory") {
431
507
  const node = record.id === image.root
432
508
  ? root
433
- : { kind: "directory", parent: undefined, entries: new Map(), metadata };
509
+ : { kind: "directory", lineage: record.id, parent: undefined, entries: new Map(), metadata };
434
510
  node.metadata = metadata;
435
511
  incoming.set(record.id, node);
436
512
  }
437
513
  else if (record.kind === "file") {
438
- const data = Image.bytes(record.data);
514
+ const data = baseContents?.get(record.id) ?? Content.make(Image.bytes(record.data));
439
515
  incoming.set(record.id, {
440
516
  kind: "file",
517
+ lineage: record.id,
441
518
  data,
442
519
  openCount: 0,
443
- metadata: { ...metadata, size: BigInt(data.length) }
520
+ metadata: { ...metadata, size: BigInt(data.bytes.length) }
444
521
  });
445
522
  }
446
523
  else {
447
524
  const target = Image.bytes(record.target);
448
- incoming.set(record.id, { kind: "symlink", target, metadata: { ...metadata, size: BigInt(target.length) } });
525
+ incoming.set(record.id, {
526
+ kind: "symlink",
527
+ lineage: record.id,
528
+ target,
529
+ metadata: { ...metadata, size: BigInt(target.length) }
530
+ });
449
531
  }
450
532
  }
451
533
  for (const record of image.records) {
@@ -513,6 +595,75 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
513
595
  }
514
596
  }
515
597
  };
598
+ const captureSnapshot = Effect.fnUntraced(function* () {
599
+ const ids = new Map([[root, "0"]]);
600
+ const pending = [root];
601
+ const records = [];
602
+ for (let index = 0; index < pending.length; index++) {
603
+ const node = pending[index];
604
+ if (node === undefined)
605
+ continue;
606
+ const id = ids.get(node);
607
+ if (id === undefined)
608
+ return yield* new ImageError({ code: "InvalidStructure" });
609
+ const metadata = storedMetadata(node.metadata);
610
+ if (node.kind === "directory") {
611
+ const children = [];
612
+ for (const [name, child] of node.entries) {
613
+ let target = ids.get(child);
614
+ if (target === undefined) {
615
+ target = String(ids.size);
616
+ ids.set(child, target);
617
+ pending.push(child);
618
+ }
619
+ children.push({ name: Image.base64(nameBytes(name)), target });
620
+ }
621
+ records.push({ id, kind: "directory", metadata, entries: children });
622
+ }
623
+ else if (node.kind === "file") {
624
+ records.push({ id, kind: "file", metadata, data: Image.base64(node.data.bytes) });
625
+ }
626
+ else
627
+ records.push({ id, kind: "symlink", metadata, target: Image.base64(node.target) });
628
+ }
629
+ return yield* Image.capture({ format: "effect-vfs", version: 1, root: "0", records });
630
+ });
631
+ const observeChanges = () => {
632
+ const observation = [];
633
+ const paths = [[root, new Uint8Array([47])]];
634
+ for (let index = 0; index < paths.length; index++) {
635
+ const current = paths[index];
636
+ if (current === undefined)
637
+ continue;
638
+ const [node, path] = current;
639
+ observation.push({
640
+ path: new Uint8Array(path),
641
+ lineage: node.lineage,
642
+ kind: node.kind,
643
+ content: node.kind === "file" ? node.data.bytes : node.kind === "symlink" ? node.target : undefined,
644
+ metadata: storedMetadata(node.metadata)
645
+ });
646
+ if (node.kind !== "directory")
647
+ continue;
648
+ for (const [name, child] of node.entries) {
649
+ const bytes = nameBytes(name);
650
+ const childPath = new Uint8Array(path.length + (path.length === 1 ? 0 : 1) + bytes.length);
651
+ childPath.set(path);
652
+ let offset = path.length;
653
+ if (path.length !== 1)
654
+ childPath[offset++] = 47;
655
+ childPath.set(bytes, offset);
656
+ paths.push([child, childPath]);
657
+ }
658
+ }
659
+ return observation;
660
+ };
661
+ const captureState = Effect.fnUntraced(function* (hook) {
662
+ const snapshot = yield* captureSnapshot();
663
+ if (hook !== undefined)
664
+ yield* hook.betweenSnapshotAndSummary;
665
+ return { snapshot, observation: observeChanges() };
666
+ });
516
667
  // Permit waits stay interruptible. State transitions and resource registration do not.
517
668
  const coordinated = (effect) => gate.withPermit(Effect.uninterruptible(effect));
518
669
  const release = (reference) => coordinated(Effect.sync(() => {
@@ -534,8 +685,8 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
534
685
  };
535
686
  const reclaim = (file) => {
536
687
  if (file.metadata.nlink === 0 && file.openCount === 0) {
537
- usedBytes -= file.data.length;
538
- file.data = new Uint8Array(0);
688
+ usedBytes -= file.data.bytes.length;
689
+ file.data = Content.empty();
539
690
  }
540
691
  };
541
692
  const detach = (node, now) => {
@@ -565,14 +716,14 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
565
716
  if (length > BigInt(maxFileBytes))
566
717
  return yield* failure("FileTooLarge", operation);
567
718
  const size = Number(length);
568
- if (size - file.data.length > (settings.maxBytes ?? Number.MAX_SAFE_INTEGER) - usedBytes) {
719
+ if (size - file.data.bytes.length > (settings.maxBytes ?? Number.MAX_SAFE_INTEGER) - usedBytes) {
569
720
  return yield* failure("NoSpace", operation);
570
721
  }
571
722
  const data = new Uint8Array(size);
572
- data.set(file.data.subarray(0, size));
723
+ data.set(file.data.bytes.subarray(0, size));
573
724
  const now = yield* timestamp(operation);
574
- usedBytes += size - file.data.length;
575
- file.data = data;
725
+ usedBytes += size - file.data.bytes.length;
726
+ file.data = Content.make(data);
576
727
  file.metadata = {
577
728
  ...file.metadata,
578
729
  size: length,
@@ -596,7 +747,7 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
596
747
  return yield* failure("InvalidArgument", "read");
597
748
  }
598
749
  const start = Number(offset > file.metadata.size ? file.metadata.size : offset);
599
- const data = file.data.slice(start, start + Math.min(maximum, file.data.length - start));
750
+ const data = file.data.bytes.slice(start, start + Math.min(maximum, file.data.bytes.length - start));
600
751
  if (maximum > 0) {
601
752
  file.metadata = { ...file.metadata, atimeNs: (yield* timestamp("read")) };
602
753
  }
@@ -622,18 +773,19 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
622
773
  return yield* failure("FileTooLarge", "write");
623
774
  const start = Number(offset);
624
775
  const free = (settings.maxBytes ?? Number.MAX_SAFE_INTEGER) - usedBytes;
625
- const end = Math.min(maxFileBytes, file.data.length + free);
776
+ const end = Math.min(maxFileBytes, file.data.bytes.length + free);
626
777
  const count = Math.min(bytes.length, Math.max(0, end - start));
627
778
  if (count === 0)
628
779
  return yield* failure("NoSpace", "write");
629
- const size = Math.max(file.data.length, start + count);
630
- const data = size === file.data.length ? file.data : new Uint8Array(size);
631
- if (data !== file.data)
632
- data.set(file.data);
780
+ const size = Math.max(file.data.bytes.length, start + count);
781
+ // Always detach before mutation. A same-sized write is the critical
782
+ // case: the current payload may belong to the base or a prior capture.
783
+ const data = new Uint8Array(size);
784
+ data.set(file.data.bytes);
633
785
  const now = yield* timestamp("write");
634
786
  data.set(bytes.subarray(0, count), start);
635
- usedBytes += size - file.data.length;
636
- file.data = data;
787
+ usedBytes += size - file.data.bytes.length;
788
+ file.data = Content.make(data);
637
789
  file.metadata = {
638
790
  ...file.metadata,
639
791
  size: BigInt(size),
@@ -958,7 +1110,7 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
958
1110
  if (node.kind !== "file")
959
1111
  return yield* failure("IsDirectory", "readFile", input);
960
1112
  yield* authorize(node, identity, 4, "readFile", input);
961
- const data = new Uint8Array(node.data);
1113
+ const data = new Uint8Array(node.data.bytes);
962
1114
  node.metadata = { ...node.metadata, atimeNs: (yield* timestamp("readFile")) };
963
1115
  return data;
964
1116
  }));
@@ -1009,7 +1161,7 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
1009
1161
  else
1010
1162
  yield* authorize(file, identity, chosen.access === "readWrite" ? 6 : 2, "writeFile", input);
1011
1163
  const finalMode = chosen.finalMode === undefined ? undefined : yield* permittedMode(file?.metadata ?? { kind: "file", uid: identity.uid, gid: parent.metadata.gid }, chosen.finalMode, "writeFile", input);
1012
- const previous = file?.data.length ?? 0;
1164
+ const previous = file?.data.bytes.length ?? 0;
1013
1165
  const initial = chosen.truncate ? 0 : previous;
1014
1166
  const position = chosen.append ? initial : 0;
1015
1167
  const size = Math.max(initial, position + captured.length);
@@ -1026,14 +1178,15 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
1026
1178
  if (position !== 0 || size !== captured.length) {
1027
1179
  data = new Uint8Array(size);
1028
1180
  if (file !== undefined && !chosen.truncate)
1029
- data.set(file.data);
1181
+ data.set(file.data.bytes);
1030
1182
  data.set(captured, position);
1031
1183
  }
1032
1184
  const now = yield* timestamp("writeFile");
1033
1185
  const node = file ??
1034
1186
  {
1035
1187
  kind: "file",
1036
- data,
1188
+ lineage: undefined,
1189
+ data: Content.make(data),
1037
1190
  openCount: 0,
1038
1191
  metadata: {
1039
1192
  ...directoryMetadata(nextInode++, identity.uid, parent.metadata.gid, (chosen.mode ?? 0o666) & 0o777 & ~umask, now),
@@ -1041,7 +1194,7 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
1041
1194
  nlink: 1
1042
1195
  }
1043
1196
  };
1044
- node.data = data;
1197
+ node.data = Content.make(data);
1045
1198
  node.metadata = {
1046
1199
  ...node.metadata,
1047
1200
  mode: finalMode ?? node.metadata.mode & ~0o6000,
@@ -1175,6 +1328,7 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
1175
1328
  const now = yield* timestamp("symlink");
1176
1329
  const node = {
1177
1330
  kind: "symlink",
1331
+ lineage: undefined,
1178
1332
  target: new Uint8Array(bytes),
1179
1333
  metadata: {
1180
1334
  ...directoryMetadata(nextInode, identity.uid, parent.metadata.gid, 0o777, now),
@@ -1269,7 +1423,8 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
1269
1423
  const now = yield* timestamp("open");
1270
1424
  file = {
1271
1425
  kind: "file",
1272
- data: new Uint8Array(0),
1426
+ lineage: undefined,
1427
+ data: Content.empty(),
1273
1428
  openCount: 0,
1274
1429
  metadata: {
1275
1430
  ...directoryMetadata(nextInode, identity.uid, parent.metadata.gid, (chosen.mode ?? 0o666) & 0o777 & ~umask, now),
@@ -1464,6 +1619,7 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
1464
1619
  const now = yield* timestamp("mkdir");
1465
1620
  const child = {
1466
1621
  kind: "directory",
1622
+ lineage: undefined,
1467
1623
  parent,
1468
1624
  entries: new Map(),
1469
1625
  metadata: directoryMetadata(nextInode, identity.uid, parent.metadata.gid, (mode & 0o777 & ~umask) | (mode & 0o1000), now)
@@ -1506,6 +1662,28 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
1506
1662
  })
1507
1663
  });
1508
1664
  };
1665
+ const baseObservation = source._tag === "Overlay" ? observeChanges() : undefined;
1666
+ const publicChange = (change) => {
1667
+ switch (change._tag) {
1668
+ case "Added":
1669
+ case "Removed":
1670
+ case "Updated":
1671
+ case "Replaced":
1672
+ return Object.freeze({ ...change, path: ownedPath(new Uint8Array(change.path)) });
1673
+ case "Renamed":
1674
+ return Object.freeze({
1675
+ ...change,
1676
+ from: ownedPath(new Uint8Array(change.from)),
1677
+ to: ownedPath(new Uint8Array(change.to))
1678
+ });
1679
+ }
1680
+ throw new Error("Unknown internal overlay change");
1681
+ };
1682
+ const publicChanges = (changes) => Object.freeze(changes.map(publicChange));
1683
+ const changeOptions = (options) => {
1684
+ const decoded = decodeConfiguration(OverlayChangesOptions, options === undefined ? {} : options);
1685
+ return Result.isFailure(decoded) ? Effect.fail(decoded.failure) : Effect.succeed(decoded.success);
1686
+ };
1509
1687
  const volume = Object.freeze({
1510
1688
  [VolumeId]: true,
1511
1689
  watch: Effect.gen(function* () {
@@ -1516,39 +1694,7 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
1516
1694
  }));
1517
1695
  return Stream.fromEffectRepeat(PubSub.take(subscription));
1518
1696
  }).pipe(Effect.withSpan("Volume.watch")),
1519
- snapshot: coordinated(Effect.gen(function* () {
1520
- const ids = new Map([[root, "0"]]);
1521
- const pending = [root];
1522
- const records = [];
1523
- for (let index = 0; index < pending.length; index++) {
1524
- const node = pending[index];
1525
- if (node === undefined)
1526
- continue;
1527
- const id = ids.get(node);
1528
- if (id === undefined)
1529
- return yield* new ImageError({ code: "InvalidStructure" });
1530
- const metadata = storedMetadata(node.metadata);
1531
- if (node.kind === "directory") {
1532
- const children = [];
1533
- for (const [name, child] of node.entries) {
1534
- let target = ids.get(child);
1535
- if (target === undefined) {
1536
- target = String(ids.size);
1537
- ids.set(child, target);
1538
- pending.push(child);
1539
- }
1540
- children.push({ name: Image.base64(nameBytes(name)), target });
1541
- }
1542
- records.push({ id, kind: "directory", metadata, entries: children });
1543
- }
1544
- else if (node.kind === "file") {
1545
- records.push({ id, kind: "file", metadata, data: Image.base64(node.data) });
1546
- }
1547
- else
1548
- records.push({ id, kind: "symlink", metadata, target: Image.base64(node.target) });
1549
- }
1550
- return yield* Image.capture({ format: "effect-vfs", version: 1, root: "0", records });
1551
- })).pipe(Effect.withSpan("Volume.snapshot")),
1697
+ snapshot: coordinated(captureSnapshot()).pipe(Effect.withSpan("Volume.snapshot")),
1552
1698
  caller: Effect.fn("Volume.caller")(function* (options) {
1553
1699
  const decoded = decodeConfiguration(RootCallerOptions, options === undefined ? {} : options);
1554
1700
  if (Result.isFailure(decoded))
@@ -1558,7 +1704,27 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
1558
1704
  return createCaller({ volume: volumeIdentity, directory: root, closed: false }, identity, decoded.success.umask ?? 0o022);
1559
1705
  })
1560
1706
  });
1561
- return volume;
1707
+ if (source._tag !== "Overlay" || baseObservation === undefined) {
1708
+ return { _tag: "Volume", volume };
1709
+ }
1710
+ const hook = OverlayTesting.getObservationHook(source.base);
1711
+ const overlay = Object.freeze({
1712
+ ...volume,
1713
+ changes: Effect.fn("OverlayVolume.changes")(function* (options) {
1714
+ const selected = yield* changeOptions(options);
1715
+ const current = yield* coordinated(Effect.sync(observeChanges));
1716
+ return publicChanges(compareOverlay(baseObservation, current, selected.includeTimestamps ?? false));
1717
+ }),
1718
+ capture: Effect.fn("OverlayVolume.capture")(function* (options) {
1719
+ const selected = yield* changeOptions(options);
1720
+ const current = yield* coordinated(captureState(hook));
1721
+ return Object.freeze({
1722
+ snapshot: current.snapshot,
1723
+ changes: publicChanges(compareOverlay(baseObservation, current.observation, selected.includeTimestamps ?? false))
1724
+ });
1725
+ })
1726
+ });
1727
+ return { _tag: "Overlay", volume: overlay };
1562
1728
  });
1563
1729
  /**
1564
1730
  * Creates a fresh empty volume and captures the current Effect `Clock`.
@@ -1572,7 +1738,10 @@ const makeVolume = Effect.fn("VirtualFileSystem.makeVolume")(function* (options,
1572
1738
  * @since 0.1.0
1573
1739
  */
1574
1740
  export const make = Effect.fn("VirtualFileSystem.make")(function* (options) {
1575
- return yield* makeVolume(options).pipe(Effect.catchTag("ImageError", Effect.die));
1741
+ const result = yield* makeVolume({ _tag: "Empty" }, options).pipe(Effect.catchTag("ImageError", Effect.die));
1742
+ if (result._tag === "Overlay")
1743
+ return yield* Effect.die(new Error("empty volume constructed as overlay"));
1744
+ return result.volume;
1576
1745
  });
1577
1746
  /**
1578
1747
  * Restores a fresh volume from an opaque snapshot under the supplied destination limits.
@@ -1582,7 +1751,33 @@ export const make = Effect.fn("VirtualFileSystem.make")(function* (options) {
1582
1751
  */
1583
1752
  export const fromSnapshot = Effect.fn("VirtualFileSystem.fromSnapshot")(function* (snapshot, options) {
1584
1753
  const image = yield* Image.inspect(snapshot);
1585
- return yield* makeVolume(options, image);
1754
+ const result = yield* makeVolume({ _tag: "Snapshot", image }, options);
1755
+ if (result._tag === "Overlay")
1756
+ return yield* new ImageError({ code: "InvalidStructure" });
1757
+ return result.volume;
1758
+ });
1759
+ /**
1760
+ * Creates an isolated writable volume relative to one immutable snapshot base.
1761
+ *
1762
+ * **Details**
1763
+ *
1764
+ * Workspaces made from the same snapshot share unchanged regular-file payloads.
1765
+ * The first content mutation copies the whole file into workspace-private
1766
+ * storage. Metadata, namespace state, coordination, handles, and watches are
1767
+ * always private to the new workspace.
1768
+ *
1769
+ * Invalid base snapshots fail with `ImageError`; invalid volume limits fail
1770
+ * with `ConfigurationError`. Each execution creates a fresh workspace.
1771
+ *
1772
+ * @category constructors
1773
+ * @since 0.1.0
1774
+ */
1775
+ export const makeOverlay = Effect.fn("VirtualFileSystem.makeOverlay")(function* (base, options) {
1776
+ const image = yield* Image.inspect(base);
1777
+ const result = yield* makeVolume({ _tag: "Overlay", base, image }, options);
1778
+ if (result._tag === "Volume")
1779
+ return yield* new ImageError({ code: "InvalidStructure" });
1780
+ return result.volume;
1586
1781
  });
1587
1782
  const FixtureMetadata = Schema.Struct({
1588
1783
  uid: Schema.optionalKey(Natural),
@@ -1764,5 +1959,9 @@ export const fromFixture = Effect.fn("VirtualFileSystem.fromFixture")(function*
1764
1959
  }
1765
1960
  const records = [...new Set(declarations.values())].map((record) => record.kind === "directory" ? { ...record, entries: children.get(record.id) ?? [] } : record);
1766
1961
  const snapshot = yield* Image.capture({ format: "effect-vfs", version: 1, root: "root", records });
1767
- return yield* makeVolume(config.success, yield* Image.inspect(snapshot));
1962
+ const image = yield* Image.inspect(snapshot);
1963
+ const result = yield* makeVolume({ _tag: "Snapshot", image }, config.success);
1964
+ if (result._tag === "Overlay")
1965
+ return yield* new ImageError({ code: "InvalidStructure" });
1966
+ return result.volume;
1768
1967
  });
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Immutable regular-file payloads shared by overlay workspaces.
3
+ *
4
+ * @internal
5
+ */
6
+ import type { Snapshot } from "../Snapshot.js";
7
+ import * as Image from "./image.js";
8
+ /** @internal */
9
+ export interface Content {
10
+ readonly bytes: Uint8Array;
11
+ }
12
+ /** @internal */
13
+ export declare const hasOverlayContents: (snapshot: Snapshot) => boolean;
14
+ /** @internal */
15
+ export declare const make: (bytes: Uint8Array) => Content;
16
+ /** @internal */
17
+ export declare const empty: () => Content;
18
+ /**
19
+ * Returns the immutable file payloads associated with the identity of a base
20
+ * snapshot. The cache is weak so it cannot extend the snapshot's lifetime.
21
+ *
22
+ * @internal
23
+ */
24
+ export declare const forOverlay: (snapshot: Snapshot, image: Image.Document) => ReadonlyMap<string, Content>;
25
+ //# sourceMappingURL=content.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"content.d.ts","sourceRoot":"","sources":["../../src/internal/content.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAC9C,OAAO,KAAK,KAAK,MAAM,YAAY,CAAA;AAEnC,gBAAgB;AAChB,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAA;CAC3B;AAID,gBAAgB;AAChB,eAAO,MAAM,kBAAkB,aAAc,QAAQ,KAAG,OAAwC,CAAA;AAEhG,gBAAgB;AAChB,eAAO,MAAM,IAAI,UAAW,UAAU,KAAG,OAAsB,CAAA;AAE/D,gBAAgB;AAChB,eAAO,MAAM,KAAK,QAAO,OAAkC,CAAA;AAE3D;;;;;GAKG;AACH,eAAO,MAAM,UAAU,aAAc,QAAQ,SAAS,KAAK,CAAC,QAAQ,KAAG,WAAW,CAAC,MAAM,EAAE,OAAO,CASjG,CAAA"}
@@ -0,0 +1,26 @@
1
+ import * as Image from "./image.js";
2
+ const overlayContents = new WeakMap();
3
+ /** @internal */
4
+ export const hasOverlayContents = (snapshot) => overlayContents.has(snapshot);
5
+ /** @internal */
6
+ export const make = (bytes) => ({ bytes });
7
+ /** @internal */
8
+ export const empty = () => make(new Uint8Array(0));
9
+ /**
10
+ * Returns the immutable file payloads associated with the identity of a base
11
+ * snapshot. The cache is weak so it cannot extend the snapshot's lifetime.
12
+ *
13
+ * @internal
14
+ */
15
+ export const forOverlay = (snapshot, image) => {
16
+ const cached = overlayContents.get(snapshot);
17
+ if (cached !== undefined)
18
+ return cached;
19
+ const decoded = new Map();
20
+ for (const record of image.records) {
21
+ if (record.kind === "file")
22
+ decoded.set(record.id, make(Image.bytes(record.data)));
23
+ }
24
+ overlayContents.set(snapshot, decoded);
25
+ return decoded;
26
+ };
@@ -0,0 +1,50 @@
1
+ /** @internal */
2
+ export interface ObservationMetadata {
3
+ readonly uid: number;
4
+ readonly gid: number;
5
+ readonly mode: number;
6
+ readonly atimeNs: string;
7
+ readonly mtimeNs: string;
8
+ readonly ctimeNs: string;
9
+ readonly birthtimeNs: string;
10
+ }
11
+ /** @internal */
12
+ export interface ObservationEntry {
13
+ readonly path: Uint8Array;
14
+ readonly lineage: string | undefined;
15
+ readonly kind: "directory" | "file" | "symlink";
16
+ readonly content: Uint8Array | undefined;
17
+ readonly metadata: ObservationMetadata;
18
+ }
19
+ /** @internal */
20
+ export type OverlayDifference = "content" | "mode" | "uid" | "gid" | "atimeNs" | "mtimeNs" | "ctimeNs" | "birthtimeNs";
21
+ /** @internal */
22
+ export type RawOverlayChange = {
23
+ readonly _tag: "Added";
24
+ readonly path: Uint8Array;
25
+ readonly kind: ObservationEntry["kind"];
26
+ } | {
27
+ readonly _tag: "Removed";
28
+ readonly path: Uint8Array;
29
+ readonly kind: ObservationEntry["kind"];
30
+ } | {
31
+ readonly _tag: "Replaced";
32
+ readonly path: Uint8Array;
33
+ readonly beforeKind: ObservationEntry["kind"];
34
+ readonly afterKind: ObservationEntry["kind"];
35
+ readonly differences: ReadonlyArray<OverlayDifference>;
36
+ } | {
37
+ readonly _tag: "Renamed";
38
+ readonly from: Uint8Array;
39
+ readonly to: Uint8Array;
40
+ readonly kind: ObservationEntry["kind"];
41
+ readonly differences: ReadonlyArray<OverlayDifference>;
42
+ } | {
43
+ readonly _tag: "Updated";
44
+ readonly path: Uint8Array;
45
+ readonly kind: ObservationEntry["kind"];
46
+ readonly differences: ReadonlyArray<OverlayDifference>;
47
+ };
48
+ /** @internal */
49
+ export declare const compareOverlay: (base: ReadonlyArray<ObservationEntry>, current: ReadonlyArray<ObservationEntry>, includeTimestamps?: boolean) => ReadonlyArray<RawOverlayChange>;
50
+ //# sourceMappingURL=overlayChanges.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlayChanges.d.ts","sourceRoot":"","sources":["../../src/internal/overlayChanges.ts"],"names":[],"mappings":"AAAA,gBAAgB;AAChB,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAC7B;AAED,gBAAgB;AAChB,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,CAAA;IACpC,QAAQ,CAAC,IAAI,EAAE,WAAW,GAAG,MAAM,GAAG,SAAS,CAAA;IAC/C,QAAQ,CAAC,OAAO,EAAE,UAAU,GAAG,SAAS,CAAA;IACxC,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAA;CACvC;AAED,gBAAgB;AAChB,MAAM,MAAM,iBAAiB,GAAG,SAAS,GAAG,MAAM,GAAG,KAAK,GAAG,KAAK,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,GAAG,aAAa,CAAA;AAEtH,gBAAgB;AAChB,MAAM,MAAM,gBAAgB,GACxB;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAA;CAAE,GAC9F;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAA;CAAE,GAChG;IACA,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAC7C,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAA;IAC5C,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAA;CACvD,GACC;IACA,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,EAAE,EAAE,UAAU,CAAA;IACvB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAA;IACvC,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAA;CACvD,GACC;IACA,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,QAAQ,CAAC,IAAI,EAAE,gBAAgB,CAAC,MAAM,CAAC,CAAA;IACvC,QAAQ,CAAC,WAAW,EAAE,aAAa,CAAC,iBAAiB,CAAC,CAAA;CACvD,CAAA;AAqFH,gBAAgB;AAChB,eAAO,MAAM,cAAc,SACnB,aAAa,CAAC,gBAAgB,CAAC,WAC5B,aAAa,CAAC,gBAAgB,CAAC,kCAEvC,aAAa,CAAC,gBAAgB,CAoEhC,CAAA"}
@@ -0,0 +1,157 @@
1
+ const timestampFields = new Set(["atimeNs", "mtimeNs", "ctimeNs", "birthtimeNs"]);
2
+ const differenceOrder = [
3
+ "content",
4
+ "mode",
5
+ "uid",
6
+ "gid",
7
+ "atimeNs",
8
+ "mtimeNs",
9
+ "ctimeNs",
10
+ "birthtimeNs"
11
+ ];
12
+ const tagOrder = {
13
+ Added: 0,
14
+ Removed: 1,
15
+ Replaced: 2,
16
+ Renamed: 3,
17
+ Updated: 4
18
+ };
19
+ const key = (path) => {
20
+ let result = "";
21
+ for (const byte of path)
22
+ result += byte.toString(16).padStart(2, "0");
23
+ return result;
24
+ };
25
+ const comparePaths = (left, right) => {
26
+ const length = Math.min(left.length, right.length);
27
+ for (let index = 0; index < length; index++) {
28
+ const difference = left[index] - right[index];
29
+ if (difference !== 0)
30
+ return difference;
31
+ }
32
+ return left.length - right.length;
33
+ };
34
+ const sameBytes = (left, right) => {
35
+ if (left === right)
36
+ return true;
37
+ if (left === undefined || right === undefined || left.length !== right.length)
38
+ return false;
39
+ for (let index = 0; index < left.length; index++) {
40
+ if (left[index] !== right[index])
41
+ return false;
42
+ }
43
+ return true;
44
+ };
45
+ const differences = (before, after, includeTimestamps) => differenceOrder.filter((field) => {
46
+ if (!includeTimestamps && timestampFields.has(field))
47
+ return false;
48
+ if (field === "content")
49
+ return before.kind !== after.kind || !sameBytes(before.content, after.content);
50
+ return before.metadata[field] !== after.metadata[field];
51
+ });
52
+ const byLineage = (entries) => {
53
+ const result = new Map();
54
+ for (const entry of entries) {
55
+ if (entry.lineage === undefined)
56
+ continue;
57
+ const group = result.get(entry.lineage);
58
+ if (group === undefined)
59
+ result.set(entry.lineage, new Map([[key(entry.path), entry]]));
60
+ else
61
+ group.set(key(entry.path), entry);
62
+ }
63
+ return result;
64
+ };
65
+ const copyChange = (change) => {
66
+ switch (change._tag) {
67
+ case "Added":
68
+ case "Removed":
69
+ return Object.freeze({ ...change, path: change.path.slice() });
70
+ case "Replaced":
71
+ case "Updated":
72
+ return Object.freeze({
73
+ ...change,
74
+ path: change.path.slice(),
75
+ differences: Object.freeze([...change.differences])
76
+ });
77
+ case "Renamed":
78
+ return Object.freeze({
79
+ ...change,
80
+ from: change.from.slice(),
81
+ to: change.to.slice(),
82
+ differences: Object.freeze([...change.differences])
83
+ });
84
+ }
85
+ };
86
+ /** @internal */
87
+ export const compareOverlay = (base, current, includeTimestamps = false) => {
88
+ const basePaths = new Map(base.map((entry) => [key(entry.path), entry]));
89
+ const currentPaths = new Map(current.map((entry) => [key(entry.path), entry]));
90
+ const baseLineages = byLineage(base);
91
+ const currentLineages = byLineage(current);
92
+ const consumedBase = new Set();
93
+ const consumedCurrent = new Set();
94
+ const changes = [];
95
+ for (const [lineage, beforeEntries] of baseLineages) {
96
+ const afterEntries = currentLineages.get(lineage);
97
+ if (afterEntries === undefined)
98
+ continue;
99
+ const removed = [...beforeEntries].filter(([pathKey]) => !afterEntries.has(pathKey)).map(([, entry]) => entry);
100
+ const added = [...afterEntries].filter(([pathKey]) => !beforeEntries.has(pathKey)).map(([, entry]) => entry);
101
+ if (removed.length !== 1 || added.length !== 1)
102
+ continue;
103
+ const before = removed[0];
104
+ const after = added[0];
105
+ consumedBase.add(key(before.path));
106
+ consumedCurrent.add(key(after.path));
107
+ changes.push({
108
+ _tag: "Renamed",
109
+ from: before.path,
110
+ to: after.path,
111
+ kind: after.kind,
112
+ differences: differences(before, after, includeTimestamps)
113
+ });
114
+ }
115
+ for (const before of base) {
116
+ const pathKey = key(before.path);
117
+ if (consumedBase.has(pathKey))
118
+ continue;
119
+ const after = currentPaths.get(pathKey);
120
+ if (after === undefined || consumedCurrent.has(pathKey)) {
121
+ changes.push({ _tag: "Removed", path: before.path, kind: before.kind });
122
+ continue;
123
+ }
124
+ consumedCurrent.add(pathKey);
125
+ if (before.lineage === undefined || before.lineage !== after.lineage) {
126
+ changes.push({
127
+ _tag: "Replaced",
128
+ path: before.path,
129
+ beforeKind: before.kind,
130
+ afterKind: after.kind,
131
+ differences: differences(before, after, includeTimestamps)
132
+ });
133
+ continue;
134
+ }
135
+ const changed = differences(before, after, includeTimestamps);
136
+ if (changed.length > 0)
137
+ changes.push({ _tag: "Updated", path: before.path, kind: after.kind, differences: changed });
138
+ }
139
+ for (const after of current) {
140
+ const pathKey = key(after.path);
141
+ if (!consumedCurrent.has(pathKey) && (!basePaths.has(pathKey) || consumedBase.has(pathKey))) {
142
+ changes.push({ _tag: "Added", path: after.path, kind: after.kind });
143
+ }
144
+ }
145
+ changes.sort((left, right) => {
146
+ const leftPath = left._tag === "Renamed" ? left.from : left.path;
147
+ const rightPath = right._tag === "Renamed" ? right.from : right.path;
148
+ const pathOrder = comparePaths(leftPath, rightPath);
149
+ if (pathOrder !== 0)
150
+ return pathOrder;
151
+ const typeOrder = tagOrder[left._tag] - tagOrder[right._tag];
152
+ if (typeOrder !== 0)
153
+ return typeOrder;
154
+ return left._tag === "Renamed" && right._tag === "Renamed" ? comparePaths(left.to, right.to) : 0;
155
+ });
156
+ return Object.freeze(changes.map(copyChange));
157
+ };
@@ -0,0 +1,12 @@
1
+ /** Package-internal synchronization hooks for deterministic overlay tests. @internal */
2
+ import type * as Effect from "effect/Effect";
3
+ import type { Snapshot } from "../Snapshot.js";
4
+ /** @internal */
5
+ export interface ObservationHook {
6
+ readonly betweenSnapshotAndSummary: Effect.Effect<void>;
7
+ }
8
+ /** @internal */
9
+ export declare const getObservationHook: (snapshot: Snapshot) => ObservationHook | undefined;
10
+ /** @internal */
11
+ export declare const setObservationHook: (snapshot: Snapshot, hook: ObservationHook) => () => void;
12
+ //# sourceMappingURL=overlayTesting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"overlayTesting.d.ts","sourceRoot":"","sources":["../../src/internal/overlayTesting.ts"],"names":[],"mappings":"AAAA,wFAAwF;AAExF,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAA;AAC5C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAE9C,gBAAgB;AAChB,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,yBAAyB,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;CACxD;AAID,gBAAgB;AAChB,eAAO,MAAM,kBAAkB,aAAc,QAAQ,KAAG,eAAe,GAAG,SAA2C,CAAA;AAErH,gBAAgB;AAChB,eAAO,MAAM,kBAAkB,aAAc,QAAQ,QAAQ,eAAe,KAAG,MAAM,IAKpF,CAAA"}
@@ -0,0 +1,12 @@
1
+ /** Package-internal synchronization hooks for deterministic overlay tests. @internal */
2
+ const observationHooks = new WeakMap();
3
+ /** @internal */
4
+ export const getObservationHook = (snapshot) => observationHooks.get(snapshot);
5
+ /** @internal */
6
+ export const setObservationHook = (snapshot, hook) => {
7
+ observationHooks.set(snapshot, hook);
8
+ return () => {
9
+ if (observationHooks.get(snapshot) === hook)
10
+ observationHooks.delete(snapshot);
11
+ };
12
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-vfs/core",
3
- "version": "0.0.1",
3
+ "version": "0.1.0",
4
4
  "description": "A runtime-neutral virtual filesystem core for Effect.",
5
5
  "homepage": "https://github.com/lloydrichards/effect-virtual-fs#readme",
6
6
  "bugs": {
@@ -17,7 +17,7 @@
17
17
  "build": "tsc --project tsconfig.build.json && tsc --project compatibility/node-next/tsconfig.json",
18
18
  "clean": "git clean -xdf .cache .turbo dist node_modules tsconfig.tsbuildinfo",
19
19
  "test": "vitest run",
20
- "type-check": "tsc --noEmit"
20
+ "type-check": "tsc --noEmit && tsc --project contracts/tsconfig.json"
21
21
  },
22
22
  "peerDependencies": {
23
23
  "effect": "4.0.0-rc.112"