@effect-vfs/core 0.5.0 → 0.6.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.
Files changed (130) hide show
  1. package/README.md +34 -31
  2. package/dist/BytePath.d.ts +114 -4
  3. package/dist/BytePath.d.ts.map +1 -1
  4. package/dist/BytePath.js +155 -0
  5. package/dist/Caller.d.ts +391 -0
  6. package/dist/Caller.d.ts.map +1 -0
  7. package/dist/Caller.js +199 -0
  8. package/dist/FileHandle.d.ts +50 -0
  9. package/dist/FileHandle.d.ts.map +1 -0
  10. package/dist/FileHandle.js +28 -0
  11. package/dist/Fixture.d.ts +155 -0
  12. package/dist/Fixture.d.ts.map +1 -0
  13. package/dist/Fixture.js +65 -0
  14. package/dist/LiveVolume.d.ts +11 -21
  15. package/dist/LiveVolume.d.ts.map +1 -1
  16. package/dist/LiveVolume.js +17 -14
  17. package/dist/Metadata.d.ts +186 -0
  18. package/dist/Metadata.d.ts.map +1 -0
  19. package/dist/Metadata.js +125 -0
  20. package/dist/Snapshot.d.ts +31 -53
  21. package/dist/Snapshot.d.ts.map +1 -1
  22. package/dist/Snapshot.js +26 -49
  23. package/dist/SnapshotDelta.d.ts +24 -39
  24. package/dist/SnapshotDelta.d.ts.map +1 -1
  25. package/dist/SnapshotDelta.js +14 -35
  26. package/dist/Target.d.ts +225 -0
  27. package/dist/Target.d.ts.map +1 -0
  28. package/dist/Target.js +92 -0
  29. package/dist/Testing.d.ts +128 -0
  30. package/dist/Testing.d.ts.map +1 -0
  31. package/dist/Testing.js +111 -0
  32. package/dist/VfsError.d.ts +199 -0
  33. package/dist/VfsError.d.ts.map +1 -0
  34. package/dist/VfsError.js +164 -0
  35. package/dist/VirtualFileSystem.d.ts +628 -347
  36. package/dist/VirtualFileSystem.d.ts.map +1 -1
  37. package/dist/VirtualFileSystem.js +448 -115
  38. package/dist/Volume.d.ts +211 -0
  39. package/dist/Volume.d.ts.map +1 -0
  40. package/dist/Volume.js +172 -0
  41. package/dist/Watch.d.ts +68 -0
  42. package/dist/Watch.d.ts.map +1 -0
  43. package/dist/Watch.js +46 -0
  44. package/dist/index.d.ts +56 -1
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +56 -1
  47. package/dist/internal/budget.d.ts +90 -0
  48. package/dist/internal/budget.d.ts.map +1 -0
  49. package/dist/internal/budget.js +70 -0
  50. package/dist/internal/bytePath.d.ts +2 -0
  51. package/dist/internal/bytePath.d.ts.map +1 -1
  52. package/dist/internal/bytePath.js +3 -2
  53. package/dist/internal/bytes.d.ts +3 -0
  54. package/dist/internal/bytes.d.ts.map +1 -1
  55. package/dist/internal/bytes.js +4 -2
  56. package/dist/internal/canonicalBase64.d.ts +1 -4
  57. package/dist/internal/canonicalBase64.d.ts.map +1 -1
  58. package/dist/internal/canonicalBase64.js +9 -13
  59. package/dist/internal/errors.d.ts +47 -5
  60. package/dist/internal/errors.d.ts.map +1 -1
  61. package/dist/internal/errors.js +80 -36
  62. package/dist/internal/fixture.d.ts +2 -2
  63. package/dist/internal/fixture.d.ts.map +1 -1
  64. package/dist/internal/fixture.js +57 -74
  65. package/dist/internal/hex128.d.ts +12 -0
  66. package/dist/internal/hex128.d.ts.map +1 -0
  67. package/dist/internal/hex128.js +7 -0
  68. package/dist/internal/hmac.d.ts +5 -0
  69. package/dist/internal/hmac.d.ts.map +1 -0
  70. package/dist/internal/hmac.js +150 -0
  71. package/dist/internal/image.d.ts +14 -117
  72. package/dist/internal/image.d.ts.map +1 -1
  73. package/dist/internal/image.js +137 -167
  74. package/dist/internal/inodeTable.d.ts +20 -0
  75. package/dist/internal/inodeTable.d.ts.map +1 -0
  76. package/dist/internal/inodeTable.js +48 -0
  77. package/dist/internal/lines.d.ts +36 -0
  78. package/dist/internal/lines.d.ts.map +1 -0
  79. package/dist/internal/lines.js +115 -0
  80. package/dist/internal/liveImage.d.ts +19 -278
  81. package/dist/internal/liveImage.d.ts.map +1 -1
  82. package/dist/internal/liveImage.js +75 -166
  83. package/dist/internal/merkle.d.ts +45 -0
  84. package/dist/internal/merkle.d.ts.map +1 -0
  85. package/dist/internal/merkle.js +179 -0
  86. package/dist/internal/metadata.d.ts +2 -13
  87. package/dist/internal/metadata.d.ts.map +1 -1
  88. package/dist/internal/metadata.js +5 -15
  89. package/dist/internal/overlayDiff.d.ts +2 -2
  90. package/dist/internal/overlayDiff.d.ts.map +1 -1
  91. package/dist/internal/overlayDiff.js +0 -3
  92. package/dist/internal/path.d.ts +17 -5
  93. package/dist/internal/path.d.ts.map +1 -1
  94. package/dist/internal/path.js +38 -24
  95. package/dist/internal/snapshotDelta.d.ts +61 -60
  96. package/dist/internal/snapshotDelta.d.ts.map +1 -1
  97. package/dist/internal/snapshotDelta.js +612 -504
  98. package/dist/internal/snapshotDeltaModel.d.ts.map +1 -1
  99. package/dist/internal/snapshotDeltaModel.js +0 -1
  100. package/dist/internal/stagedState.d.ts +11 -15
  101. package/dist/internal/stagedState.d.ts.map +1 -1
  102. package/dist/internal/stagedState.js +15 -59
  103. package/dist/internal/testSeams.d.ts +17 -0
  104. package/dist/internal/testSeams.d.ts.map +1 -0
  105. package/dist/internal/testSeams.js +20 -0
  106. package/dist/internal/tree.d.ts +225 -0
  107. package/dist/internal/tree.d.ts.map +1 -0
  108. package/dist/internal/tree.js +385 -0
  109. package/dist/internal/turnstile.d.ts +8 -0
  110. package/dist/internal/turnstile.d.ts.map +1 -0
  111. package/dist/internal/turnstile.js +43 -0
  112. package/dist/internal/virtualFileSystem.d.ts +39 -489
  113. package/dist/internal/virtualFileSystem.d.ts.map +1 -1
  114. package/dist/internal/virtualFileSystem.js +2112 -2465
  115. package/dist/internal/volumeState.d.ts +110 -0
  116. package/dist/internal/volumeState.d.ts.map +1 -0
  117. package/dist/internal/volumeState.js +204 -0
  118. package/dist/internal/watchHub.d.ts +15 -6
  119. package/dist/internal/watchHub.d.ts.map +1 -1
  120. package/dist/internal/watchHub.js +56 -42
  121. package/package.json +48 -4
  122. package/dist/VirtualFileSystemError.d.ts +0 -54
  123. package/dist/VirtualFileSystemError.d.ts.map +0 -1
  124. package/dist/VirtualFileSystemError.js +0 -49
  125. package/dist/internal/overlayContent.d.ts +0 -15
  126. package/dist/internal/overlayContent.d.ts.map +0 -1
  127. package/dist/internal/overlayContent.js +0 -29
  128. package/dist/internal/testHooks.d.ts +0 -20
  129. package/dist/internal/testHooks.d.ts.map +0 -1
  130. package/dist/internal/testHooks.js +0 -23
@@ -12,24 +12,33 @@ import type * as ByteSize from "effect/ByteSize";
12
12
  import * as Context from "effect/Context";
13
13
  import type * as Crypto from "effect/Crypto";
14
14
  import * as Effect from "effect/Effect";
15
+ import * as Layer from "effect/Layer";
15
16
  import type * as Order from "effect/Order";
16
17
  import type * as PlatformError from "effect/PlatformError";
17
18
  import * as Schema from "effect/Schema";
18
19
  import type * as Scope from "effect/Scope";
19
- import type * as Stream from "effect/Stream";
20
+ import * as Sink from "effect/Sink";
21
+ import * as Stream from "effect/Stream";
20
22
  import type { BytePath } from "./BytePath.js";
21
- import type { ConfigurationError, FsError } from "./VirtualFileSystemError.js";
23
+ import type { CallerId, ObjectReferenceId } from "./Caller.js";
24
+ import * as CallerModule from "./Caller.js";
25
+ import type { DirectoryHandleId, FileHandleId } from "./FileHandle.js";
26
+ import * as FileHandleModule from "./FileHandle.js";
27
+ import * as FixtureModule from "./Fixture.js";
28
+ import * as MetadataModule from "./Metadata.js";
29
+ import type { Entry, EntryInput, Target, TargetInput } from "./Target.js";
30
+ import type { ArgumentFailure, FsFailure, ImageFailure, VfsError } from "./VfsError.js";
31
+ import * as VfsErrorModule from "./VfsError.js";
32
+ import type { VolumeId } from "./Volume.js";
33
+ import * as VolumeModule from "./Volume.js";
34
+ import * as WatchModule from "./Watch.js";
35
+ export { Entry, type EntryInput, isEntry, isTarget, Name, type NameInput, type PathTarget, Target, type TargetInput } from "./Target.js";
22
36
  export { BytePath } from "./BytePath.js";
23
- import type { DecodeLimits, ImageError, Snapshot } from "./Snapshot.js";
24
- export { DecodeLimits, ImageError, type Snapshot, SnapshotTypeId } from "./Snapshot.js";
25
- import * as VfsModel from "./internal/virtualFileSystem.js";
37
+ import type { DecodeLimits, Snapshot } from "./Snapshot.js";
38
+ export { DecodeLimits, type Snapshot, SnapshotTypeId } from "./Snapshot.js";
39
+ import { type LiveImageStore, type Options as LiveVolumeOptions } from "./LiveVolume.js";
26
40
  import * as SnapshotDeltaModel from "./SnapshotDelta.js";
27
- export { SnapshotChange, SnapshotChangesOptions, type SnapshotDelta, SnapshotDeltaError, SnapshotDeltaLimits, SnapshotDeltaTypeId, SnapshotDifference, SnapshotNodeKind } from "./SnapshotDelta.js";
28
- declare const VolumeId: typeof VfsModel.VolumeId;
29
- declare const CallerId: typeof VfsModel.CallerId;
30
- declare const FileHandleId: typeof VfsModel.FileHandleId;
31
- declare const DirectoryHandleId: typeof VfsModel.DirectoryHandleId;
32
- declare const ObjectReferenceId: typeof VfsModel.ObjectReferenceId;
41
+ export { SnapshotChange, SnapshotChangesOptions, type SnapshotDelta, SnapshotDeltaLimits, SnapshotDeltaTypeId, SnapshotDifference, SnapshotNodeKind } from "./SnapshotDelta.js";
33
42
  /**
34
43
  * A UTF-8 string path or an opaque byte-preserving path.
35
44
  *
@@ -73,7 +82,7 @@ export type PathInput = string | BytePath;
73
82
  * @category schemas
74
83
  * @since 0.1.0
75
84
  */
76
- export declare const FsCode: typeof VfsModel.FsCode;
85
+ export declare const FsCode: typeof VfsErrorModule.FsCode;
77
86
  /**
78
87
  * A portable virtual filesystem error code.
79
88
  *
@@ -91,7 +100,6 @@ export type FsCode = typeof FsCode.Type;
91
100
  * @example
92
101
  * ```ts
93
102
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
94
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
95
103
  * import { Effect } from "effect"
96
104
  *
97
105
  * const program = Effect.gen(function*() {
@@ -100,28 +108,26 @@ export type FsCode = typeof FsCode.Type;
100
108
  * // Every filesystem operation fails with this one tagged error; `code`
101
109
  * // distinguishes the cases, so match on it rather than on the tag alone.
102
110
  * return yield* caller.readFile("/missing").pipe(
103
- * Effect.catchTag("FsError", (error) =>
111
+ * Effect.catchTag("VfsError", (error) =>
104
112
  * error.code === "NotFound"
105
113
  * ? Effect.succeed(new Uint8Array())
106
114
  * : Effect.fail(error))
107
115
  * )
108
116
  * })
109
117
  *
110
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
118
+ * Effect.runPromise(program).then(console.log)
111
119
  * // Uint8Array(0) []
112
120
  * ```
113
121
  *
114
122
  * @category errors
115
123
  * @since 0.1.0
116
124
  */
117
- export { FsError } from "./VirtualFileSystemError.js";
118
125
  /**
119
126
  * Describes an invalid volume or caller option and names the rejected field.
120
127
  *
121
128
  * @example
122
129
  * ```ts
123
130
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
124
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
125
131
  * import { Effect } from "effect"
126
132
  *
127
133
  * // `field` names the rejected option, so the caller learns which key was wrong.
@@ -131,25 +137,25 @@ export { FsError } from "./VirtualFileSystemError.js";
131
137
  *
132
138
  * return yield* Vfs.make(options).pipe(
133
139
  * Effect.as("ok"),
134
- * Effect.catchTag("ConfigurationError", (error) => Effect.succeed(`rejected ${error.field}`))
140
+ * Effect.catchTag("VfsError", (error) => Effect.succeed(`rejected ${error.field}`))
135
141
  * )
136
142
  * })
137
143
  *
138
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
144
+ * Effect.runPromise(program).then(console.log)
139
145
  * // rejected maxEntries
140
146
  * ```
141
147
  *
142
148
  * @category errors
143
149
  * @since 0.1.0
144
150
  */
145
- export { ConfigurationError } from "./VirtualFileSystemError.js";
151
+ export { type ArgumentFailure, DeltaCode, type FsFailure, ImageCode, type ImageFailure, StoreCode, type StoreFailure, VfsCode, VfsError } from "./VfsError.js";
146
152
  /**
147
153
  * Schema for a caller's numeric identity, supplementary groups, and explicit privilege.
148
154
  *
149
155
  * @category schemas
150
156
  * @since 0.1.0
151
157
  */
152
- export declare const Identity: typeof VfsModel.Identity;
158
+ export declare const Identity: typeof CallerModule.Identity;
153
159
  /**
154
160
  * A caller identity used for permission checks.
155
161
  *
@@ -163,7 +169,6 @@ export type Identity = typeof Identity.Type;
163
169
  * @example
164
170
  * ```ts
165
171
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
166
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
167
172
  * import { Effect } from "effect"
168
173
  *
169
174
  * const program = Effect.gen(function*() {
@@ -184,14 +189,14 @@ export type Identity = typeof Identity.Type;
184
189
  * return ((yield* user.stat("/home/user")).mode & 0o777).toString(8)
185
190
  * })
186
191
  *
187
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
192
+ * Effect.runPromise(program).then(console.log)
188
193
  * // 755
189
194
  * ```
190
195
  *
191
196
  * @category schemas
192
197
  * @since 0.1.0
193
198
  */
194
- export declare const RootCallerOptions: typeof VfsModel.RootCallerOptions;
199
+ export declare const RootCallerOptions: typeof CallerModule.RootCallerOptions;
195
200
  /**
196
201
  * Options for creating a root caller on a volume.
197
202
  *
@@ -205,7 +210,7 @@ export type RootCallerOptions = typeof RootCallerOptions.Type;
205
210
  * @category schemas
206
211
  * @since 0.1.0
207
212
  */
208
- export declare const VolumeDurability: typeof VfsModel.VolumeDurability;
213
+ export declare const VolumeDurability: typeof VolumeModule.VolumeDurability;
209
214
  /**
210
215
  * The failure boundary survived by an acknowledged volume write.
211
216
  *
@@ -233,7 +238,7 @@ export declare const isVolumeDurabilityAtLeast: (actual: VolumeDurability, requi
233
238
  * @category schemas
234
239
  * @since 0.1.0
235
240
  */
236
- export declare const VolumeIdentity: typeof VfsModel.VolumeIdentity;
241
+ export declare const VolumeIdentity: typeof VolumeModule.VolumeIdentity;
237
242
  /**
238
243
  * Stable logical identity of a volume as a branded 128-bit lowercase hexadecimal string.
239
244
  *
@@ -247,7 +252,7 @@ export type VolumeIdentity = typeof VolumeIdentity.Type;
247
252
  * @category schemas
248
253
  * @since 0.1.0
249
254
  */
250
- export declare const VolumeIncarnation: typeof VfsModel.VolumeIncarnation;
255
+ export declare const VolumeIncarnation: typeof VolumeModule.VolumeIncarnation;
251
256
  /**
252
257
  * One uninterrupted storage lifetime as a branded 128-bit lowercase hexadecimal string.
253
258
  *
@@ -255,13 +260,52 @@ export declare const VolumeIncarnation: typeof VfsModel.VolumeIncarnation;
255
260
  * @since 0.1.0
256
261
  */
257
262
  export type VolumeIncarnation = typeof VolumeIncarnation.Type;
263
+ /**
264
+ * Schema for the serialisable name of one object in one volume: the volume's
265
+ * identity, the epoch of its inode numbers, the object's inode number, and a
266
+ * tag only the volume can compute, so a key cannot be guessed from another.
267
+ *
268
+ * @example
269
+ * ```ts
270
+ * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
271
+ * import { Effect, Schema } from "effect"
272
+ *
273
+ * const Json = Schema.fromJsonString(Vfs.ReferenceKey)
274
+ *
275
+ * const program = Effect.gen(function*() {
276
+ * const volume = yield* Vfs.make()
277
+ * const caller = yield* volume.caller()
278
+ * yield* caller.writeFile("/f", new Uint8Array([1]), { access: "write", create: "exclusive" })
279
+ *
280
+ * // The key survives a wire; resolving it returns the same reference.
281
+ * const reference = yield* caller.lookup("/f")
282
+ * const text = yield* Schema.encodeEffect(Json)(yield* volume.referenceKey(reference))
283
+ * const key = yield* Schema.decodeEffect(Json)(text)
284
+ *
285
+ * return (yield* volume.resolveReferenceKey(key)) === reference
286
+ * })
287
+ *
288
+ * Effect.runPromise(program).then(console.log)
289
+ * // true
290
+ * ```
291
+ *
292
+ * @category schemas
293
+ * @since 0.6.0
294
+ */
295
+ export declare const ReferenceKey: typeof VolumeModule.ReferenceKey;
296
+ /**
297
+ * The serialisable name of one object in one volume.
298
+ *
299
+ * @category models
300
+ * @since 0.6.0
301
+ */
302
+ export type ReferenceKey = typeof ReferenceKey.Type;
258
303
  /**
259
304
  * Schema for optional volume capacity and path limits.
260
305
  *
261
306
  * @example
262
307
  * ```ts
263
308
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
264
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
265
309
  * import { ByteSize, Effect } from "effect"
266
310
  *
267
311
  * // Every limit is optional, and each one is enforced once set.
@@ -274,18 +318,18 @@ export type VolumeIncarnation = typeof VolumeIncarnation.Type;
274
318
  * create: "exclusive"
275
319
  * }).pipe(
276
320
  * Effect.as("written"),
277
- * Effect.catchTag("FsError", (error) => Effect.succeed(error.code))
321
+ * Effect.catchTag("VfsError", (error) => Effect.succeed(error.code))
278
322
  * )
279
323
  * })
280
324
  *
281
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
325
+ * Effect.runPromise(program).then(console.log)
282
326
  * // FileTooLarge
283
327
  * ```
284
328
  *
285
329
  * @category schemas
286
330
  * @since 0.1.0
287
331
  */
288
- export declare const VolumeOptions: typeof VfsModel.VolumeOptions;
332
+ export declare const VolumeOptions: typeof VolumeModule.VolumeOptions;
289
333
  /**
290
334
  * Capacity, admission, watch retention, and path limits for a volume.
291
335
  * `maxPendingOperations` defaults to 64 and `maxWatchEvents` defaults to 256.
@@ -328,7 +372,6 @@ export interface VolumeUsage {
328
372
  * @example
329
373
  * ```ts
330
374
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
331
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
332
375
  * import { Effect } from "effect"
333
376
  *
334
377
  * // Sizes and inode numbers are bigint; times are nanosecond timestamps.
@@ -345,14 +388,14 @@ export interface VolumeUsage {
345
388
  * return [metadata.kind, metadata.size, metadata.nlink]
346
389
  * })
347
390
  *
348
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
391
+ * Effect.runPromise(program).then(console.log)
349
392
  * // [ 'file', 3n, 1 ]
350
393
  * ```
351
394
  *
352
395
  * @category schemas
353
396
  * @since 0.1.0
354
397
  */
355
- export declare const Metadata: typeof VfsModel.Metadata;
398
+ export declare const Metadata: typeof MetadataModule.Metadata;
356
399
  /**
357
400
  * Metadata for a directory, regular file, or symbolic link.
358
401
  *
@@ -375,7 +418,6 @@ export interface ObjectReference {
375
418
  * @example
376
419
  * ```ts
377
420
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
378
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
379
421
  * import { Effect } from "effect"
380
422
  *
381
423
  * // References survive renames, and each observation carries the revision it
@@ -385,20 +427,20 @@ export interface ObjectReference {
385
427
  *
386
428
  * yield* caller.mkdir("/work")
387
429
  *
388
- * const root = yield* caller.rootReference
389
- * const work = yield* caller.lookupReference(root, new TextEncoder().encode("work"))
430
+ * const root = yield* caller.root
431
+ * const work = yield* caller.lookup(Vfs.Entry(root, "work"))
390
432
  *
391
- * const before = yield* caller.observeMetadata(work)
433
+ * const before = yield* caller.stat(work)
392
434
  *
393
435
  * yield* caller.rename("/work", "/moved")
394
436
  *
395
- * const after = yield* caller.observeMetadata(work)
437
+ * const after = yield* caller.stat(work)
396
438
  *
397
- * return [before.value.kind, after.value.kind]
439
+ * return [before.kind, after.kind, after.revision > before.revision]
398
440
  * })
399
441
  *
400
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
401
- * // [ 'directory', 'directory' ]
442
+ * Effect.runPromise(program).then(console.log)
443
+ * // [ 'directory', 'directory', true ]
402
444
  * ```
403
445
  *
404
446
  * @category models
@@ -418,13 +460,60 @@ export interface DirectoryEntry {
418
460
  readonly name: Uint8Array;
419
461
  readonly reference: ObjectReference;
420
462
  }
463
+ /**
464
+ * One entry a walk reached: its path below the walk's root, its name, the
465
+ * object it names and the directory it was listed in, its kind, and its depth.
466
+ *
467
+ * **Details**
468
+ *
469
+ * `path` is relative to the walk's root, with no leading slash (`a/b` for
470
+ * `b` in the root's child `a`), so it reads the same whichever way the root
471
+ * was addressed; the root itself is not an entry. `depth` is 1 for a child of
472
+ * the root. A symbolic link is reported as a link and never followed.
473
+ *
474
+ * @category models
475
+ * @since 0.6.0
476
+ */
477
+ export interface WalkEntry {
478
+ readonly path: BytePath;
479
+ readonly name: Uint8Array;
480
+ readonly reference: ObjectReference;
481
+ readonly directory: ObjectReference;
482
+ readonly kind: Metadata["kind"];
483
+ readonly depth: number;
484
+ }
485
+ /**
486
+ * What a walk fails with: a failure of the directory it lists, or
487
+ * `LimitExceeded` when it would pass one of its bounds, which `field` names.
488
+ *
489
+ * @category models
490
+ * @since 0.6.0
491
+ */
492
+ export type WalkFailure = FsFailure | (VfsError & {
493
+ readonly code: "LimitExceeded";
494
+ });
495
+ /**
496
+ * Settings for walking a directory tree: the order and the optional depth,
497
+ * entry, and byte bounds.
498
+ *
499
+ * @category schemas
500
+ * @since 0.6.0
501
+ */
502
+ export declare const WalkOptions: typeof CallerModule.WalkOptions;
503
+ /**
504
+ * Settings for walking a directory tree.
505
+ *
506
+ * @category models
507
+ * @since 0.6.0
508
+ */
509
+ export type WalkOptions = typeof WalkOptions.Type;
421
510
  /**
422
511
  * A directory revision transition captured inside one coordinated operation.
423
512
  *
424
513
  * @category schemas
425
514
  * @since 0.4.0
426
515
  */
427
- export declare const DirectoryChange: typeof VfsModel.DirectoryChange;
516
+ export declare const DirectoryChange: typeof CallerModule.DirectoryChange;
428
517
  /**
429
518
  * A directory revision transition captured inside one coordinated operation.
430
519
  *
@@ -448,7 +537,7 @@ export interface ReferenceEntryResult {
448
537
  * @category schemas
449
538
  * @since 0.4.0
450
539
  */
451
- export declare const RenameReferenceResult: typeof VfsModel.RenameReferenceResult;
540
+ export declare const RenameReferenceResult: typeof CallerModule.RenameReferenceResult;
452
541
  /**
453
542
  * The directory transitions produced by a same-directory or cross-directory rename.
454
543
  *
@@ -456,64 +545,13 @@ export declare const RenameReferenceResult: typeof VfsModel.RenameReferenceResul
456
545
  * @since 0.4.0
457
546
  */
458
547
  export type RenameReferenceResult = typeof RenameReferenceResult.Type;
459
- /**
460
- * Resolves a relative path from a live directory handle instead of the caller's directory.
461
- *
462
- * @category models
463
- * @since 0.1.0
464
- */
465
- export interface RelativeOptions {
466
- /** Resolve relative paths from this live, same-volume handle instead of the caller's current directory. */
467
- readonly relativeTo?: DirectoryHandle;
468
- }
469
- /**
470
- * Controls the base directory and whether metadata operations follow the final symbolic link.
471
- *
472
- * @example
473
- * ```ts
474
- * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
475
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
476
- * import { Effect } from "effect"
477
- *
478
- * const program = Effect.gen(function*() {
479
- * const caller = yield* (yield* Vfs.make()).caller()
480
- *
481
- * yield* caller.writeFile("/target", new Uint8Array([1]), {
482
- * access: "write",
483
- * create: "exclusive",
484
- * mode: 0o644
485
- * })
486
- * yield* caller.symlink("/target", "/link")
487
- *
488
- * // Metadata operations follow the final link by default, so this reaches the
489
- * // target; opting out changes the link itself and leaves the target alone.
490
- * yield* caller.chmod("/link", 0o600)
491
- * const followed = (yield* caller.stat("/target")).mode & 0o777
492
- *
493
- * yield* caller.chmod("/link", 0o777, { followFinalSymlink: false })
494
- * const unchanged = (yield* caller.stat("/target")).mode & 0o777
495
- *
496
- * return [followed.toString(8), unchanged.toString(8)]
497
- * })
498
- *
499
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
500
- * // [ '600', '600' ]
501
- * ```
502
- *
503
- * @category models
504
- * @since 0.1.0
505
- */
506
- export interface MetadataOptions extends RelativeOptions {
507
- /** Follow the final symbolic link. Defaults to `true`. */
508
- readonly followFinalSymlink?: boolean;
509
- }
510
548
  /**
511
549
  * Schema for an owner update. Omitted fields retain their existing values.
512
550
  *
513
551
  * @category schemas
514
552
  * @since 0.1.0
515
553
  */
516
- export declare const OwnerUpdate: typeof VfsModel.OwnerUpdate;
554
+ export declare const OwnerUpdate: typeof MetadataModule.OwnerUpdate;
517
555
  /**
518
556
  * An owner update for `chown` operations.
519
557
  *
@@ -527,14 +565,13 @@ export type OwnerUpdate = typeof OwnerUpdate.Type;
527
565
  * @category schemas
528
566
  * @since 0.1.0
529
567
  */
530
- export declare const TimeUpdate: typeof VfsModel.TimeUpdate;
568
+ export declare const TimeUpdate: typeof MetadataModule.TimeUpdate;
531
569
  /**
532
570
  * Schema for independent access and modification time updates.
533
571
  *
534
572
  * @example
535
573
  * ```ts
536
574
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
537
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
538
575
  * import { Effect } from "effect"
539
576
  *
540
577
  * const program = Effect.gen(function*() {
@@ -556,14 +593,14 @@ export declare const TimeUpdate: typeof VfsModel.TimeUpdate;
556
593
  * return [metadata.atimeNs, metadata.mtimeNs === 1_000n]
557
594
  * })
558
595
  *
559
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
596
+ * Effect.runPromise(program).then(console.log)
560
597
  * // [ 1000n, false ]
561
598
  * ```
562
599
  *
563
600
  * @category schemas
564
601
  * @since 0.1.0
565
602
  */
566
- export declare const Times: typeof VfsModel.Times;
603
+ export declare const Times: typeof MetadataModule.Times;
567
604
  /**
568
605
  * Access and modification time updates for `utimes` operations.
569
606
  *
@@ -577,7 +614,6 @@ export type Times = typeof Times.Type;
577
614
  * @example
578
615
  * ```ts
579
616
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
580
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
581
617
  * import { Effect } from "effect"
582
618
  *
583
619
  * // The handle anchors relative paths to the directory itself, so a rename in
@@ -590,16 +626,15 @@ export type Times = typeof Times.Type;
590
626
  * const directory = yield* caller.openDirectory("/project")
591
627
  *
592
628
  * yield* caller.rename("/project", "/renamed")
593
- * yield* caller.writeFile("notes.txt", new TextEncoder().encode("kept"), {
629
+ * yield* caller.writeFile(Vfs.Target.Path({ path: "notes.txt", relativeTo: directory }), new TextEncoder().encode("kept"), {
594
630
  * access: "write",
595
- * create: "exclusive",
596
- * relativeTo: directory
631
+ * create: "exclusive"
597
632
  * })
598
633
  *
599
- * return yield* caller.readDirectory("/renamed")
634
+ * return (yield* caller.readDirectory("/renamed")).value.map((entry) => new TextDecoder().decode(entry.name))
600
635
  * }).pipe(Effect.scoped)
601
636
  *
602
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
637
+ * Effect.runPromise(program).then(console.log)
603
638
  * // [ 'notes.txt' ]
604
639
  * ```
605
640
  *
@@ -608,8 +643,8 @@ export type Times = typeof Times.Type;
608
643
  */
609
644
  export interface DirectoryHandle {
610
645
  readonly [DirectoryHandleId]: true;
611
- readonly stat: Effect.Effect<Metadata, FsError>;
612
- readonly close: Effect.Effect<void, FsError>;
646
+ readonly stat: Effect.Effect<Metadata, FsFailure>;
647
+ readonly close: Effect.Effect<void, FsFailure>;
613
648
  }
614
649
  /**
615
650
  * Schema for file seek origins, including dense-file data and hole queries.
@@ -617,7 +652,7 @@ export interface DirectoryHandle {
617
652
  * @category schemas
618
653
  * @since 0.1.0
619
654
  */
620
- export declare const SeekMode: typeof VfsModel.SeekMode;
655
+ export declare const SeekMode: typeof FileHandleModule.SeekMode;
621
656
  /**
622
657
  * The origin used by a file handle seek operation.
623
658
  *
@@ -631,7 +666,6 @@ export type SeekMode = typeof SeekMode.Type;
631
666
  * @example
632
667
  * ```ts
633
668
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
634
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
635
669
  * import { Effect } from "effect"
636
670
  *
637
671
  * // `access` is required; `create` decides whether a missing file is an error.
@@ -649,65 +683,86 @@ export type SeekMode = typeof SeekMode.Type;
649
683
  * return (yield* handle.stat).size
650
684
  * }).pipe(Effect.scoped)
651
685
  *
652
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
686
+ * Effect.runPromise(program).then(console.log)
653
687
  * // 2n
654
688
  * ```
655
689
  *
656
690
  * @category schemas
657
691
  * @since 0.1.0
658
692
  */
659
- export declare const OpenSettings: typeof VfsModel.OpenSettings;
693
+ export declare const OpenOptions: typeof CallerModule.OpenOptions;
660
694
  /**
661
- * Options for acquiring a scoped file handle.
695
+ * Options of `open` on a target.
662
696
  *
663
697
  * @category models
664
698
  * @since 0.1.0
665
699
  */
666
- export type OpenOptions = typeof OpenSettings.Type & RelativeOptions;
700
+ export type OpenOptions = typeof OpenOptions.Type;
667
701
  /**
668
702
  * Settings for creating a directory through a parent object reference.
669
703
  * `exactMode` skips the caller's umask for an explicit creation mode; permission policy still applies.
704
+ * `recursive` creates every missing directory on a path in one change, as `mkdir -p` does.
670
705
  *
671
706
  * @category schemas
672
707
  * @since 0.4.0
673
708
  */
674
- export declare const MkdirReferenceSettings: typeof VfsModel.MkdirReferenceSettings;
709
+ export declare const MkdirOptions: typeof CallerModule.MkdirOptions;
675
710
  /**
676
711
  * Settings for creating a directory through a parent object reference.
677
712
  * `exactMode` skips the caller's umask for an explicit creation mode; permission policy still applies.
713
+ * `recursive` creates every missing directory on a path in one change, as `mkdir -p` does.
678
714
  *
679
715
  * @category models
680
716
  * @since 0.4.0
681
717
  */
682
- export type MkdirReferenceSettings = typeof MkdirReferenceSettings.Type;
718
+ export type MkdirOptions = typeof MkdirOptions.Type;
719
+ /**
720
+ * Settings for removing an entry: whether a directory goes with everything
721
+ * under it, and whether a missing target is success.
722
+ *
723
+ * @category schemas
724
+ * @since 0.6.0
725
+ */
726
+ export declare const RemoveOptions: typeof CallerModule.RemoveOptions;
727
+ /**
728
+ * Settings for removing an entry.
729
+ *
730
+ * @category models
731
+ * @since 0.6.0
732
+ */
733
+ export type RemoveOptions = typeof RemoveOptions.Type;
683
734
  /**
684
735
  * Settings for creating a symbolic link through a parent object reference.
685
736
  *
686
737
  * @category schemas
687
738
  * @since 0.4.0
688
739
  */
689
- export declare const SymlinkReferenceSettings: typeof VfsModel.SymlinkReferenceSettings;
740
+ export declare const SymlinkOptions: typeof CallerModule.SymlinkOptions;
690
741
  /**
691
742
  * Settings for creating a symbolic link through a parent object reference.
692
743
  *
693
744
  * @category models
694
745
  * @since 0.4.0
695
746
  */
696
- export type SymlinkReferenceSettings = typeof SymlinkReferenceSettings.Type;
747
+ export type SymlinkOptions = typeof SymlinkOptions.Type;
697
748
  /**
698
- * Settings for opening an existing regular-file reference.
749
+ * The attributes `setattr` changes together: a regular file's size, the
750
+ * permission mode, the owner, and the access and modification times. An
751
+ * omitted attribute keeps its current value. `expected` pins the revision the
752
+ * caller last observed and fails as `StaleReference` naming `expected` when
753
+ * the target has moved on.
699
754
  *
700
755
  * @category schemas
701
- * @since 0.4.0
756
+ * @since 0.6.0
702
757
  */
703
- export declare const OpenReferenceSettings: typeof VfsModel.OpenReferenceSettings;
758
+ export declare const SetattrOptions: typeof CallerModule.SetattrOptions;
704
759
  /**
705
- * Settings for opening an existing regular-file reference.
760
+ * The attributes `setattr` changes together.
706
761
  *
707
762
  * @category models
708
- * @since 0.4.0
763
+ * @since 0.6.0
709
764
  */
710
- export type OpenReferenceSettings = typeof OpenReferenceSettings.Type;
765
+ export type SetattrOptions = typeof SetattrOptions.Type;
711
766
  /**
712
767
  * Settings for atomically looking up or creating and opening one referenced child.
713
768
  * `exactMode` skips the caller's umask for an explicit creation mode; permission policy still applies.
@@ -715,7 +770,7 @@ export type OpenReferenceSettings = typeof OpenReferenceSettings.Type;
715
770
  * @category schemas
716
771
  * @since 0.4.0
717
772
  */
718
- export declare const OpenChildReferenceSettings: typeof VfsModel.OpenChildReferenceSettings;
773
+ export declare const OpenEntryOptions: typeof CallerModule.OpenEntryOptions;
719
774
  /**
720
775
  * Settings for atomically looking up or creating and opening one referenced child.
721
776
  * `exactMode` skips the caller's umask for an explicit creation mode; permission policy still applies.
@@ -728,14 +783,14 @@ export declare const OpenChildReferenceSettings: typeof VfsModel.OpenChildRefere
728
783
  * @category models
729
784
  * @since 0.4.0
730
785
  */
731
- export type OpenChildReferenceSettings = typeof OpenChildReferenceSettings.Type;
786
+ export type OpenEntryOptions = typeof OpenEntryOptions.Type;
732
787
  /**
733
788
  * A scoped child-open result captured inside one coordinated operation.
734
789
  *
735
790
  * @category models
736
791
  * @since 0.4.0
737
792
  */
738
- export interface OpenChildReferenceResult {
793
+ export interface OpenEntryResult {
739
794
  readonly handle: FileHandle;
740
795
  readonly reference: ObjectReference;
741
796
  readonly created: boolean;
@@ -747,7 +802,6 @@ export interface OpenChildReferenceResult {
747
802
  * @example
748
803
  * ```ts
749
804
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
750
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
751
805
  * import { Effect } from "effect"
752
806
  *
753
807
  * const program = Effect.gen(function*() {
@@ -767,7 +821,7 @@ export interface OpenChildReferenceResult {
767
821
  * create: "exclusive"
768
822
  * }).pipe(
769
823
  * Effect.as("replaced"),
770
- * Effect.catchTag("FsError", (error) => Effect.succeed(error.code))
824
+ * Effect.catchTag("VfsError", (error) => Effect.succeed(error.code))
771
825
  * )
772
826
  *
773
827
  * yield* caller.writeFile("/app.conf", bytes, { access: "write", truncate: true })
@@ -775,21 +829,37 @@ export interface OpenChildReferenceResult {
775
829
  * return [refused, new TextDecoder().decode(yield* caller.readFile("/app.conf"))]
776
830
  * })
777
831
  *
778
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
832
+ * Effect.runPromise(program).then(console.log)
779
833
  * // [ 'AlreadyExists', 'v2' ]
780
834
  * ```
781
835
  *
782
836
  * @category models
783
837
  * @since 0.1.0
784
838
  */
785
- export type WriteFileOptions = VfsModel.WriteFileOptions;
839
+ export declare const WriteFileOptions: typeof CallerModule.WriteFileOptions;
840
+ /**
841
+ * Options of `writeFile`.
842
+ *
843
+ * @category models
844
+ * @since 0.1.0
845
+ */
846
+ export type WriteFileOptions = typeof WriteFileOptions.Type;
847
+ /**
848
+ * The bytes a positional read returned and whether they reach the end of the file.
849
+ *
850
+ * @category models
851
+ * @since 0.6.0
852
+ */
853
+ export interface ReadResult {
854
+ readonly bytes: Uint8Array;
855
+ readonly eof: boolean;
856
+ }
786
857
  /**
787
858
  * A scoped regular-file capability with an independent bigint cursor.
788
859
  *
789
860
  * @example
790
861
  * ```ts
791
862
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
792
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
793
863
  * import { Effect } from "effect"
794
864
  *
795
865
  * const program = Effect.gen(function*() {
@@ -805,11 +875,11 @@ export type WriteFileOptions = VfsModel.WriteFileOptions;
805
875
  * // `pread` reads at an offset and leaves the cursor where writing left it.
806
876
  * const slice = yield* handle.pread(2, 1n)
807
877
  *
808
- * return [slice, yield* handle.seek(0n, "current")]
878
+ * return [slice.bytes, slice.eof, yield* handle.seek(0n, "current")]
809
879
  * }).pipe(Effect.scoped)
810
880
  *
811
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
812
- * // [ Uint8Array(2) [ 2, 3 ], 4n ]
881
+ * Effect.runPromise(program).then(console.log)
882
+ * // [ Uint8Array(2) [ 2, 3 ], false, 4n ]
813
883
  * ```
814
884
  *
815
885
  * @category models
@@ -818,23 +888,23 @@ export type WriteFileOptions = VfsModel.WriteFileOptions;
818
888
  export interface FileHandle {
819
889
  readonly [FileHandleId]: true;
820
890
  /** Reads up to `maximumBytes` from the cursor and advances it by the returned length. */
821
- readonly read: (maximumBytes: number) => Effect.Effect<Uint8Array, FsError>;
891
+ readonly read: (maximumBytes: number) => Effect.Effect<Uint8Array, FsFailure>;
822
892
  /** Reads at `offset` without changing the cursor. */
823
- readonly pread: (maximumBytes: number, offset: bigint) => Effect.Effect<Uint8Array, FsError>;
893
+ readonly pread: (maximumBytes: number, offset: bigint) => Effect.Effect<ReadResult, FsFailure>;
824
894
  /** Writes at the cursor and advances it, or writes at end of file when opened for append. */
825
- readonly write: (bytes: Uint8Array) => Effect.Effect<number, FsError>;
895
+ readonly write: (bytes: Uint8Array) => Effect.Effect<number, FsFailure>;
826
896
  /** Writes at `offset` without changing the cursor. Append mode does not affect positional writes. */
827
- readonly pwrite: (bytes: Uint8Array, offset: bigint) => Effect.Effect<number, FsError>;
897
+ readonly pwrite: (bytes: Uint8Array, offset: bigint) => Effect.Effect<number, FsFailure>;
828
898
  /** Moves the cursor and returns its new offset. `data` finds content and `hole` finds end of file. */
829
- readonly seek: (offset: bigint, mode: SeekMode) => Effect.Effect<bigint, FsError>;
899
+ readonly seek: (offset: bigint, mode: SeekMode) => Effect.Effect<bigint, FsFailure>;
830
900
  /** Sets the file length without changing the cursor. */
831
- readonly truncate: (length: bigint) => Effect.Effect<void, FsError>;
901
+ readonly truncate: (length: bigint) => Effect.Effect<void, FsFailure>;
832
902
  /** Reads metadata for the open file. */
833
- readonly stat: Effect.Effect<Metadata, FsError>;
903
+ readonly stat: Effect.Effect<Metadata, FsFailure>;
834
904
  /** Checks handle and provider health. Durable mutations have already committed; memory volumes have nothing to flush. */
835
- readonly sync: Effect.Effect<void, FsError>;
905
+ readonly sync: Effect.Effect<void, FsFailure>;
836
906
  /** Closes the handle. A repeated explicit close fails; scope cleanup remains safe. */
837
- readonly close: Effect.Effect<void, FsError>;
907
+ readonly close: Effect.Effect<void, FsFailure>;
838
908
  }
839
909
  /**
840
910
  * A filesystem caller with its own identity, creation mask, and current directory.
@@ -842,7 +912,6 @@ export interface FileHandle {
842
912
  * @example
843
913
  * ```ts
844
914
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
845
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
846
915
  * import { Effect } from "effect"
847
916
  *
848
917
  * // Whole-file reads and writes need no scope. `writeFile` always takes an
@@ -857,17 +926,16 @@ export interface FileHandle {
857
926
  * create: "exclusive"
858
927
  * })
859
928
  *
860
- * return yield* caller.readDirectory("/work")
929
+ * return (yield* caller.readDirectory("/work")).value.map((entry) => new TextDecoder().decode(entry.name))
861
930
  * })
862
931
  *
863
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
932
+ * Effect.runPromise(program).then(console.log)
864
933
  * // [ 'notes.txt' ]
865
934
  * ```
866
935
  *
867
936
  * @example
868
937
  * ```ts
869
938
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
870
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
871
939
  * import { Effect } from "effect"
872
940
  *
873
941
  * // Reach for a handle when a whole-file write will not do. A handle carries a
@@ -887,14 +955,13 @@ export interface FileHandle {
887
955
  * return new TextDecoder().decode(yield* handle.read(64))
888
956
  * }).pipe(Effect.scoped)
889
957
  *
890
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
958
+ * Effect.runPromise(program).then(console.log)
891
959
  * // first second
892
960
  * ```
893
961
  *
894
962
  * @example
895
963
  * ```ts
896
964
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
897
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
898
965
  * import { Effect } from "effect"
899
966
  *
900
967
  * // A derived caller holds the directory's identity, not its path, so renaming
@@ -912,10 +979,10 @@ export interface FileHandle {
912
979
  * create: "exclusive"
913
980
  * })
914
981
  *
915
- * return yield* caller.readDirectory("/dist")
982
+ * return (yield* caller.readDirectory("/dist")).value.map((entry) => new TextDecoder().decode(entry.name))
916
983
  * }).pipe(Effect.scoped)
917
984
  *
918
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
985
+ * Effect.runPromise(program).then(console.log)
919
986
  * // [ 'out.txt' ]
920
987
  * ```
921
988
  *
@@ -925,109 +992,99 @@ export interface FileHandle {
925
992
  */
926
993
  export interface Caller {
927
994
  readonly [CallerId]: true;
928
- /** Returns the stable reference for this volume's root directory. */
929
- readonly rootReference: Effect.Effect<ObjectReference, FsError>;
930
- /** Looks up one byte-preserving child name from a referenced directory. */
931
- readonly lookupReference: (directory: ObjectReference, name: Uint8Array) => Effect.Effect<ObjectReference, FsError>;
932
- /** Returns a referenced directory's current parent. The root is its own parent. */
933
- readonly parentReference: (directory: ObjectReference) => Effect.Effect<ObjectReference, FsError>;
934
- /** Reads metadata and its matching live revision. Requires no permission on the object, as `stat` does not. */
935
- readonly observeMetadata: (reference: ObjectReference) => Effect.Effect<ObjectObservation<Metadata>, FsError>;
936
- /** Checks permission bits on the exact referenced object without resolving a path. */
937
- readonly accessReference: (reference: ObjectReference, bits?: number) => Effect.Effect<void, FsError>;
938
- /** Reads owned directory entries and their matching directory revision. */
939
- readonly observeDirectory: (reference: ObjectReference) => Effect.Effect<ObjectObservation<ReadonlyArray<DirectoryEntry>>, FsError>;
940
- /** Reads an owned symbolic-link target through a stable reference. Requires no permission on the link. */
941
- readonly readLinkReference: (reference: ObjectReference) => Effect.Effect<Uint8Array, FsError>;
942
- /** Creates one directory from a referenced parent and returns its exact identity and directory transition. */
943
- readonly mkdirReference: (directory: ObjectReference, name: Uint8Array, settings?: MkdirReferenceSettings) => Effect.Effect<ReferenceEntryResult, FsError>;
944
- /** Creates one symbolic link from a referenced parent and returns its exact identity and directory transition. */
945
- readonly symlinkReference: (target: PathInput, directory: ObjectReference, name: Uint8Array, settings?: SymlinkReferenceSettings) => Effect.Effect<ReferenceEntryResult, FsError>;
946
- /** Creates another name for the exact source object; symbolic links are not followed. */
947
- readonly linkReference: (source: ObjectReference, destinationDirectory: ObjectReference, destinationName: Uint8Array) => Effect.Effect<ReferenceEntryResult, FsError>;
948
- /** Removes a non-directory child and returns the parent directory transition. */
949
- readonly unlinkReference: (directory: ObjectReference, name: Uint8Array) => Effect.Effect<DirectoryChange, FsError>;
950
- /** Removes an empty directory child and returns the parent directory transition. */
951
- readonly rmdirReference: (directory: ObjectReference, name: Uint8Array) => Effect.Effect<DirectoryChange, FsError>;
952
- /** Removes a child of either type in one coordinated mutation and returns the parent directory transition. */
953
- readonly removeReference: (directory: ObjectReference, name: Uint8Array) => Effect.Effect<DirectoryChange, FsError>;
954
- /** Renames one referenced child and returns one transition per distinct parent directory. */
955
- readonly renameReference: (sourceDirectory: ObjectReference, sourceName: Uint8Array, destinationDirectory: ObjectReference, destinationName: Uint8Array) => Effect.Effect<RenameReferenceResult, FsError>;
956
- /** Changes permission bits on the exact referenced object. */
957
- readonly chmodReference: (reference: ObjectReference, mode: number) => Effect.Effect<void, FsError>;
958
- /** Changes ownership on the exact referenced object. */
959
- readonly chownReference: (reference: ObjectReference, owner: OwnerUpdate) => Effect.Effect<void, FsError>;
960
- /** Changes timestamps on the exact referenced object. */
961
- readonly utimesReference: (reference: ObjectReference, times: Times) => Effect.Effect<void, FsError>;
962
- /** Changes the length of the exact referenced regular file. */
963
- readonly truncateReference: (reference: ObjectReference, length: bigint) => Effect.Effect<void, FsError>;
964
- /** Opens an existing referenced regular file. Omitted settings preserve read-only behavior. */
965
- readonly openReference: (reference: ObjectReference, settings?: OpenReferenceSettings) => Effect.Effect<FileHandle, FsError, Scope.Scope>;
966
- /** Atomically looks up or creates and opens one child; `expected` guards its current identity, with `null` meaning absent. */
967
- readonly openChildReference: (directory: ObjectReference, name: Uint8Array, settings: OpenChildReferenceSettings, expected?: ObjectReference | null) => Effect.Effect<OpenChildReferenceResult, FsError, Scope.Scope>;
968
- /** Reads metadata, following the final symbolic link by default. */
969
- readonly stat: (path: PathInput, options?: RelativeOptions) => Effect.Effect<Metadata, FsError>;
970
- /** Atomically moves an entry within this volume without replacing a non-empty directory. */
971
- readonly rename: (source: PathInput, destination: PathInput, options?: {
972
- readonly sourceRelativeTo?: DirectoryHandle;
973
- readonly destinationRelativeTo?: DirectoryHandle;
974
- }) => Effect.Effect<void, FsError>;
975
- /** Reads and returns an owned copy of a regular file's complete contents. */
976
- readonly readFile: (path: PathInput, options?: RelativeOptions) => Effect.Effect<Uint8Array, FsError>;
977
- /** Atomically writes a complete regular file according to the replacement options. */
978
- readonly writeFile: (path: PathInput, bytes: Uint8Array, options: WriteFileOptions) => Effect.Effect<void, FsError>;
979
- /** Checks the requested permission bits without opening the entry. */
980
- readonly access: (path: PathInput, bits?: number, options?: RelativeOptions) => Effect.Effect<void, FsError>;
981
- /** Sets a regular file's length. Extending creates a zero-filled region. */
982
- readonly truncate: (path: PathInput, length: bigint, options?: RelativeOptions) => Effect.Effect<void, FsError>;
983
- /** Changes permission bits, following the final symbolic link by default. */
984
- readonly chmod: (path: PathInput, mode: number, options?: MetadataOptions) => Effect.Effect<void, FsError>;
985
- /** Changes uid, gid, or both, following the final symbolic link by default. */
986
- readonly chown: (path: PathInput, owner: OwnerUpdate, options?: MetadataOptions) => Effect.Effect<void, FsError>;
987
- /** Updates access and modification times, following the final symbolic link by default. */
988
- readonly utimes: (path: PathInput, times: Times, options?: MetadataOptions) => Effect.Effect<void, FsError>;
989
- /** Changes permission bits through a live, same-volume handle. */
990
- readonly chmodHandle: (handle: FileHandle | DirectoryHandle, mode: number) => Effect.Effect<void, FsError>;
991
- /** Changes uid, gid, or both through a live, same-volume handle. */
992
- readonly chownHandle: (handle: FileHandle | DirectoryHandle, owner: OwnerUpdate) => Effect.Effect<void, FsError>;
993
- /** Updates access and modification times through a live, same-volume handle. */
994
- readonly utimesHandle: (handle: FileHandle | DirectoryHandle, times: Times) => Effect.Effect<void, FsError>;
995
- /** Reads metadata without following the final symbolic link. */
996
- readonly lstat: (path: PathInput, options?: RelativeOptions) => Effect.Effect<Metadata, FsError>;
997
- /** Creates a hard link to an existing non-directory entry. */
998
- readonly link: (source: PathInput, destination: PathInput, options?: {
999
- readonly sourceRelativeTo?: DirectoryHandle;
1000
- readonly destinationRelativeTo?: DirectoryHandle;
1001
- readonly followSourceSymlink?: boolean;
1002
- }) => Effect.Effect<void, FsError>;
1003
- /** Creates a symbolic link. The target bytes are stored without resolving them. */
1004
- readonly symlink: (target: PathInput, path: PathInput, options?: RelativeOptions) => Effect.Effect<void, FsError>;
1005
- /** Reads a symbolic-link target as UTF-8, failing `UnrepresentableName` for other bytes. See `readLinkBytes`. */
1006
- readonly readLink: (path: PathInput, options?: RelativeOptions) => Effect.Effect<string, FsError>;
1007
- /** Reads a symbolic-link target as owned bytes. */
1008
- readonly readLinkBytes: (path: PathInput, options?: RelativeOptions) => Effect.Effect<Uint8Array, FsError>;
1009
- /** Reads directory names as UTF-8, failing `UnrepresentableName` for other bytes. See `readDirectoryBytes`. */
1010
- readonly readDirectory: (path: PathInput, options?: RelativeOptions) => Effect.Effect<ReadonlyArray<string>, FsError>;
1011
- /** Reads directory names as owned byte arrays. */
1012
- readonly readDirectoryBytes: (path: PathInput, options?: RelativeOptions) => Effect.Effect<ReadonlyArray<Uint8Array>, FsError>;
1013
- /** Resolves links and normalizes a path as UTF-8, failing `UnrepresentableName` for other bytes. See `realPathBytes`. */
1014
- readonly realPath: (path: PathInput, options?: RelativeOptions) => Effect.Effect<string, FsError>;
1015
- /** Resolves links and normalizes a path without requiring UTF-8 names. */
1016
- readonly realPathBytes: (path: PathInput, options?: RelativeOptions) => Effect.Effect<BytePath, FsError>;
1017
- /** Opens a scoped regular-file handle. The surrounding scope closes it automatically. */
1018
- readonly open: (path: PathInput, options: OpenOptions) => Effect.Effect<FileHandle, FsError, Scope.Scope>;
1019
- /** Removes a non-directory entry. Open handles remain usable until closed. */
1020
- readonly unlink: (path: PathInput, options?: RelativeOptions) => Effect.Effect<void, FsError>;
995
+ /** The reference of the volume's root directory. */
996
+ readonly root: Effect.Effect<ObjectReference, FsFailure>;
997
+ /** The reference of a child of a directory, without following it. */
998
+ readonly lookup: (entry: EntryInput) => Effect.Effect<ObjectReference, FsFailure>;
999
+ /** The reference of a directory's parent; the root is its own parent. */
1000
+ readonly parent: (directory: TargetInput) => Effect.Effect<ObjectReference, FsFailure>;
1001
+ /** Metadata and revision of a target. A path follows a final symbolic link unless its target says otherwise. */
1002
+ readonly stat: (target: TargetInput) => Effect.Effect<Metadata, FsFailure>;
1003
+ /** The entries of a directory with their references, and the directory's revision. */
1004
+ readonly readDirectory: (directory: TargetInput) => Effect.Effect<ObjectObservation<ReadonlyArray<DirectoryEntry>>, FsFailure>;
1005
+ /**
1006
+ * Every entry below a directory, as a stream that reads one directory at a time and holds no handle. Entries
1007
+ * arrive depth first, each directory's entries in the byte order of their names; symbolic links are reported,
1008
+ * never followed. Each directory is read in one observation, so the walk sees each listing whole but no
1009
+ * snapshot of the tree, and it refreshes no access time. Each directory below the root is reached by name, as a
1010
+ * path lookup reaches it, so it needs search permission on every directory above it, and one that goes away or
1011
+ * is renamed out of the tree before the walk reads it is skipped. A failure below the root names the entry's
1012
+ * path under the path the root was given by, or under the root when it was given by reference or handle.
1013
+ */
1014
+ readonly walk: (directory: TargetInput, options?: WalkOptions) => Stream.Stream<WalkEntry, WalkFailure>;
1015
+ /** The bytes a symbolic link points at. A path never follows the final link. */
1016
+ readonly readLink: (target: TargetInput) => Effect.Effect<Uint8Array, FsFailure>;
1017
+ /** The canonical absolute path of a target. */
1018
+ readonly realPath: (target: TargetInput) => Effect.Effect<BytePath, FsFailure>;
1019
+ /** The bits of `bits` the caller may exercise on the target: READ, WRITE, EXECUTE as 4, 2, 1. */
1020
+ readonly access: (target: TargetInput, bits?: number) => Effect.Effect<number, FsFailure>;
1021
+ /** The whole content of a regular file. */
1022
+ readonly readFile: (target: TargetInput) => Effect.Effect<Uint8Array, FsFailure>;
1023
+ /** Writes a whole file in one operation, creating or replacing it as the options say. */
1024
+ readonly writeFile: (entry: EntryInput, bytes: Uint8Array, options: WriteFileOptions) => Effect.Effect<void, FsFailure>;
1025
+ /**
1026
+ * Opens a file. On a target, the file must exist and `create` is rejected. On an entry, the name is looked up
1027
+ * and created in one gate hold, and the result reports whether it was created.
1028
+ */
1029
+ readonly open: {
1030
+ (target: Target | PathInput | ObjectReference | FileHandle, options: OpenOptions): Effect.Effect<FileHandle, FsFailure, Scope.Scope>;
1031
+ (entry: Entry, options: OpenEntryOptions): Effect.Effect<OpenEntryResult, FsFailure, Scope.Scope>;
1032
+ };
1033
+ /**
1034
+ * Creates a directory. With `recursive`, every missing directory on a path is created in one change, so a
1035
+ * failure creates none and a watcher sees them all at once; symbolic links on the way are followed, dot names
1036
+ * are walked, and a final name that is already a directory (or a link to one) succeeds without a change. The
1037
+ * result names the directory the path ends on, with its parent's revision before and after the call, so a path
1038
+ * that leaves a directory it created, such as `new/..`, still reports the change. A name on the way that is not
1039
+ * a directory fails with `NotDirectory`, a final one with `AlreadyExists`, and only names the caller wrote are
1040
+ * created, so a dangling link fails with `NotFound`.
1041
+ */
1042
+ readonly mkdir: (entry: EntryInput, options?: MkdirOptions) => Effect.Effect<ReferenceEntryResult, FsFailure>;
1043
+ /** Creates a symbolic link to `target`, stored as given. */
1044
+ readonly symlink: (target: PathInput, entry: EntryInput, options?: SymlinkOptions) => Effect.Effect<ReferenceEntryResult, FsFailure>;
1045
+ /** Adds a name for an existing file or symbolic link. A path source follows a final symbolic link only when its target says so. */
1046
+ readonly link: (source: TargetInput, entry: EntryInput) => Effect.Effect<ReferenceEntryResult, FsFailure>;
1047
+ /** Removes a file or symbolic link. */
1048
+ readonly unlink: (entry: EntryInput) => Effect.Effect<DirectoryChange, FsFailure>;
1021
1049
  /** Removes an empty directory. */
1022
- readonly rmdir: (path: PathInput, options?: RelativeOptions) => Effect.Effect<void, FsError>;
1023
- /** Creates one directory. Parent directories must already exist. */
1024
- readonly mkdir: (path: PathInput, options?: RelativeOptions & {
1025
- readonly mode?: number;
1026
- }) => Effect.Effect<void, FsError>;
1027
- /** Creates a scoped caller whose current directory is the resolved directory identity. */
1028
- readonly withDirectory: (path: PathInput, options?: RelativeOptions) => Effect.Effect<Caller, FsError, Scope.Scope>;
1029
- /** Opens a scoped directory handle for metadata and relative path resolution. */
1030
- readonly openDirectory: (path: PathInput, options?: RelativeOptions) => Effect.Effect<DirectoryHandle, FsError, Scope.Scope>;
1050
+ readonly rmdir: (entry: EntryInput) => Effect.Effect<DirectoryChange, FsFailure>;
1051
+ /**
1052
+ * Removes a file, a symbolic link, or an empty directory. With `recursive`, a directory that has entries is
1053
+ * emptied first, entries before their directories, each removal its own change; it stops at the first failure,
1054
+ * whose path names the entry it stopped at, and leaves what it has not reached. Each entry is removed by its name
1055
+ * in a directory reached by name, so a subtree renamed out of the target is left alone. A directory with entries
1056
+ * must be readable, and no permission is changed to make it so. With `force`, a missing target is success and the
1057
+ * result is `undefined`; a failure below the target is still a failure.
1058
+ */
1059
+ readonly remove: {
1060
+ (entry: EntryInput, options?: RemoveOptions & {
1061
+ readonly force?: false;
1062
+ }): Effect.Effect<DirectoryChange, FsFailure>;
1063
+ (entry: EntryInput, options: RemoveOptions): Effect.Effect<DirectoryChange | undefined, FsFailure>;
1064
+ };
1065
+ /** Moves an entry, replacing a compatible destination. */
1066
+ readonly rename: (from: EntryInput, to: EntryInput) => Effect.Effect<RenameReferenceResult, FsFailure>;
1067
+ /** Sets the permission bits of a target. */
1068
+ readonly chmod: (target: TargetInput, mode: number) => Effect.Effect<void, FsFailure>;
1069
+ /** Changes the owner or group of a target. */
1070
+ readonly chown: (target: TargetInput, owner: OwnerUpdate) => Effect.Effect<void, FsFailure>;
1071
+ /** Sets the access and modification times of a target. */
1072
+ readonly utimes: (target: TargetInput, times: Times) => Effect.Effect<void, FsFailure>;
1073
+ /** Sets the size of a regular file. */
1074
+ readonly truncate: (target: TargetInput, length: bigint) => Effect.Effect<void, FsFailure>;
1075
+ /**
1076
+ * Changes a target's size, owner, mode and times as one change. Every check passes before any attribute
1077
+ * applies, so a failure changes nothing. The attributes apply as size, owner, mode, then times, the POSIX
1078
+ * composition of chown then chmod, so a requested mode wins over the set-ID clearing an owner change or a
1079
+ * resize triggers. A protocol adapter that sanitises the requested mode, as NFS does after Linux knfsd,
1080
+ * does so before it calls setattr, and passes the revision it read as `expected` so the change refuses a
1081
+ * target another change has moved.
1082
+ */
1083
+ readonly setattr: (target: TargetInput, attributes: SetattrOptions) => Effect.Effect<void, FsFailure>;
1084
+ /** A caller whose working directory is the target, released with the scope. */
1085
+ readonly withDirectory: (directory: TargetInput) => Effect.Effect<Caller, FsFailure, Scope.Scope>;
1086
+ /** A handle on a directory, released with the scope. */
1087
+ readonly openDirectory: (directory: TargetInput) => Effect.Effect<DirectoryHandle, FsFailure, Scope.Scope>;
1031
1088
  }
1032
1089
  /**
1033
1090
  * A committed namespace or content change emitted by a volume watch stream.
@@ -1035,14 +1092,13 @@ export interface Caller {
1035
1092
  * @example
1036
1093
  * ```ts
1037
1094
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1038
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
1039
1095
  * import { Effect, Fiber, Stream } from "effect"
1040
1096
  *
1041
1097
  * const program = Effect.gen(function*() {
1042
1098
  * const volume = yield* Vfs.make()
1043
1099
  * const caller = yield* volume.caller()
1044
1100
  *
1045
- * const watcher = yield* volume.watch.pipe(
1101
+ * const watcher = yield* volume.watch().pipe(
1046
1102
  * Effect.flatMap((stream) => Stream.runCollect(Stream.take(stream, 2))),
1047
1103
  * Effect.forkChild({ startImmediately: true })
1048
1104
  * )
@@ -1060,26 +1116,80 @@ export interface Caller {
1060
1116
  * ))
1061
1117
  * }).pipe(Effect.scoped)
1062
1118
  *
1063
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
1119
+ * Effect.runPromise(program).then(console.log)
1064
1120
  * // [ 'Create /logs', 'Remove /logs' ]
1065
1121
  * ```
1066
1122
  *
1123
+ * @category schemas
1124
+ * @since 0.1.0
1125
+ */
1126
+ export declare const Change: typeof WatchModule.Change;
1127
+ /**
1128
+ * One committed change observed through a watch subscription. `Rescan` means
1129
+ * the subscriber lost events; it names `/` for a volume-wide watch and the
1130
+ * scope's current path for a scoped one.
1131
+ *
1067
1132
  * @category models
1068
1133
  * @since 0.1.0
1069
1134
  */
1070
- export interface Change {
1071
- /** Kind of committed change. `Rescan` means this subscriber lost events. */
1072
- readonly _tag: "Create" | "Update" | "Remove" | "Rescan";
1073
- /** Absolute path of the changed entry, or `/` when retained events were lost. */
1074
- readonly path: BytePath;
1075
- }
1135
+ export type Change = typeof Change.Type;
1136
+ /**
1137
+ * Schema for the options of `Volume.watch`: an object reference to narrow the
1138
+ * watch to, and whether its whole subtree is reported.
1139
+ *
1140
+ * @example
1141
+ * ```ts
1142
+ * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1143
+ * import { Effect, Fiber, Stream } from "effect"
1144
+ *
1145
+ * const program = Effect.gen(function*() {
1146
+ * const volume = yield* Vfs.make()
1147
+ * const caller = yield* volume.caller()
1148
+ * yield* caller.mkdir("/work")
1149
+ * const work = yield* caller.lookup("/work")
1150
+ *
1151
+ * const watcher = yield* volume.watch({ scope: work }).pipe(
1152
+ * Effect.flatMap((stream) => Stream.runCollect(Stream.take(stream, 3))),
1153
+ * Effect.forkChild({ startImmediately: true })
1154
+ * )
1155
+ *
1156
+ * // Outside the scope, so not reported.
1157
+ * yield* caller.mkdir("/elsewhere")
1158
+ * // The scope is the directory, not its path: the rename is reported and the watch goes on.
1159
+ * yield* caller.rename("/work", "/renamed")
1160
+ * yield* caller.mkdir("/renamed/out")
1161
+ *
1162
+ * const events = yield* Fiber.join(watcher)
1163
+ *
1164
+ * return yield* Effect.forEach(events, (change) =>
1165
+ * Effect.map(
1166
+ * Vfs.pathToBytes(change.path),
1167
+ * (bytes) => `${change._tag} ${new TextDecoder().decode(bytes)}`
1168
+ * ))
1169
+ * }).pipe(Effect.scoped)
1170
+ *
1171
+ * Effect.runPromise(program).then(console.log)
1172
+ * // [ 'Remove /work', 'Create /renamed', 'Create /renamed/out' ]
1173
+ * ```
1174
+ *
1175
+ * @category schemas
1176
+ * @since 0.6.0
1177
+ */
1178
+ export declare const WatchOptions: typeof WatchModule.WatchOptions;
1179
+ /**
1180
+ * Options of `Volume.watch`.
1181
+ *
1182
+ * @category models
1183
+ * @since 0.6.0
1184
+ */
1185
+ export type WatchOptions = typeof WatchOptions.Type;
1076
1186
  /**
1077
1187
  * Schema for the filesystem entry kinds reported by overlay summaries.
1078
1188
  *
1079
1189
  * @category schemas
1080
1190
  * @since 0.1.0
1081
1191
  */
1082
- export declare const OverlayNodeKind: typeof VfsModel.OverlayNodeKind;
1192
+ export declare const OverlayNodeKind: typeof VolumeModule.OverlayNodeKind;
1083
1193
  /**
1084
1194
  * A filesystem entry kind reported by an overlay summary.
1085
1195
  *
@@ -1093,7 +1203,7 @@ export type OverlayNodeKind = typeof OverlayNodeKind.Type;
1093
1203
  * @category schemas
1094
1204
  * @since 0.1.0
1095
1205
  */
1096
- export declare const OverlayDifference: typeof VfsModel.OverlayDifference;
1206
+ export declare const OverlayDifference: typeof VolumeModule.OverlayDifference;
1097
1207
  /**
1098
1208
  * A content, ownership, permission, or timestamp field that differs from the overlay base.
1099
1209
  *
@@ -1112,7 +1222,7 @@ export type OverlayDifference = typeof OverlayDifference.Type;
1112
1222
  * @category schemas
1113
1223
  * @since 0.1.0
1114
1224
  */
1115
- export declare const OverlayChange: typeof VfsModel.OverlayChange;
1225
+ export declare const OverlayChange: typeof VolumeModule.OverlayChange;
1116
1226
  /**
1117
1227
  * A path-oriented final-state difference from an overlay's immutable base.
1118
1228
  *
@@ -1126,7 +1236,6 @@ export type OverlayChange = typeof OverlayChange.Type;
1126
1236
  * @example
1127
1237
  * ```ts
1128
1238
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1129
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
1130
1239
  * import { Effect } from "effect"
1131
1240
  *
1132
1241
  * const program = Effect.gen(function*() {
@@ -1142,14 +1251,14 @@ export type OverlayChange = typeof OverlayChange.Type;
1142
1251
  * return [plain.length, timed.length]
1143
1252
  * })
1144
1253
  *
1145
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
1254
+ * Effect.runPromise(program).then(console.log)
1146
1255
  * // [ 1, 2 ]
1147
1256
  * ```
1148
1257
  *
1149
1258
  * @category schemas
1150
1259
  * @since 0.1.0
1151
1260
  */
1152
- export declare const OverlayChangesOptions: typeof VfsModel.OverlayChangesOptions;
1261
+ export declare const OverlayChangesOptions: typeof VolumeModule.OverlayChangesOptions;
1153
1262
  /**
1154
1263
  * Filtering options for an overlay final-difference summary.
1155
1264
  *
@@ -1163,7 +1272,6 @@ export type OverlayChangesOptions = typeof OverlayChangesOptions.Type;
1163
1272
  * @example
1164
1273
  * ```ts
1165
1274
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1166
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
1167
1275
  * import { Effect } from "effect"
1168
1276
  *
1169
1277
  * const program = Effect.gen(function*() {
@@ -1187,7 +1295,7 @@ export type OverlayChangesOptions = typeof OverlayChangesOptions.Type;
1187
1295
  * ]
1188
1296
  * })
1189
1297
  *
1190
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
1298
+ * Effect.runPromise(program).then(console.log)
1191
1299
  * // [ [ 'Added' ], 'built' ]
1192
1300
  * ```
1193
1301
  *
@@ -1206,7 +1314,6 @@ export interface OverlayCapture {
1206
1314
  * @example
1207
1315
  * ```ts
1208
1316
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1209
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
1210
1317
  * import { Effect, Fiber, Option, Stream } from "effect"
1211
1318
  *
1212
1319
  * const program = Effect.gen(function*() {
@@ -1214,7 +1321,7 @@ export interface OverlayCapture {
1214
1321
  * const caller = yield* volume.caller()
1215
1322
  *
1216
1323
  * // `watch` reports future changes only; nothing is replayed.
1217
- * const watcher = yield* volume.watch.pipe(
1324
+ * const watcher = yield* volume.watch().pipe(
1218
1325
  * Effect.flatMap(Stream.runHead),
1219
1326
  * Effect.forkChild({ startImmediately: true })
1220
1327
  * )
@@ -1226,7 +1333,7 @@ export interface OverlayCapture {
1226
1333
  * return Option.getOrElse(Option.map(change, (event) => event._tag), () => "none")
1227
1334
  * }).pipe(Effect.scoped)
1228
1335
  *
1229
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
1336
+ * Effect.runPromise(program).then(console.log)
1230
1337
  * // Create
1231
1338
  * ```
1232
1339
  *
@@ -1243,14 +1350,31 @@ export interface Volume {
1243
1350
  /** Effective static limits, where `undefined` means unlimited. */
1244
1351
  readonly limits: VolumeLimits;
1245
1352
  /** Samples content bytes and directory entries together from the current committed state. */
1246
- readonly usage: Effect.Effect<VolumeUsage, FsError>;
1247
- /** Opens a scoped stream of future changes. `Rescan` at `/` requires a full rescan; events are not replayed. */
1248
- readonly watch: Effect.Effect<Stream.Stream<Change>, FsError, Scope.Scope>;
1353
+ readonly usage: Effect.Effect<VolumeUsage, FsFailure>;
1354
+ /**
1355
+ * Opens a scoped stream of future changes, registered before it returns; events are not replayed. Without a
1356
+ * scope it covers the volume and `Rescan` at `/` requires a full rescan; with one it covers that object (and its
1357
+ * subtree unless `recursive` is `false`), `Rescan` names the object's current path, and the stream ends after
1358
+ * `Remove` for the object once its last name is gone.
1359
+ */
1360
+ readonly watch: (options?: WatchOptions) => Effect.Effect<Stream.Stream<Change>, FsFailure, Scope.Scope>;
1249
1361
  /** Captures an isolated snapshot of the reachable namespace and metadata. */
1250
- readonly snapshot: Effect.Effect<Snapshot, ImageError | FsError>;
1362
+ readonly snapshot: Effect.Effect<Snapshot, VfsError>;
1363
+ /**
1364
+ * The serialisable key of an object this volume issued. Fails with `InvalidReference` for a value that is not a
1365
+ * reference, `ForeignReference` for another volume's, and `StaleReference` once the object is gone.
1366
+ */
1367
+ readonly referenceKey: (reference: ObjectReference) => Effect.Effect<ReferenceKey, FsFailure>;
1368
+ /**
1369
+ * The object a key names, as the same reference traversal returns. Fails with `InvalidReference` for a malformed
1370
+ * key or one whose tag this volume did not compute, `ForeignReference` for a key another volume or another epoch
1371
+ * of this one minted, and `StaleReference` once the object is gone. It checks no permission: holding the volume
1372
+ * already grants every caller, and the tag keeps a key holder from reaching an object no key was issued for.
1373
+ */
1374
+ readonly resolveReferenceKey: (key: ReferenceKey) => Effect.Effect<ObjectReference, FsFailure>;
1251
1375
  readonly [VolumeId]: true;
1252
1376
  /** Creates a caller rooted at `/` with independent credentials, umask, and current directory. */
1253
- readonly caller: (options?: RootCallerOptions) => Effect.Effect<Caller, ConfigurationError | FsError>;
1377
+ readonly caller: (options?: RootCallerOptions) => Effect.Effect<Caller, VfsError>;
1254
1378
  }
1255
1379
  /**
1256
1380
  * An ordinary volume with final-state inspection relative to one immutable snapshot base.
@@ -1258,7 +1382,6 @@ export interface Volume {
1258
1382
  * @example
1259
1383
  * ```ts
1260
1384
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1261
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
1262
1385
  * import { Effect } from "effect"
1263
1386
  *
1264
1387
  * const program = Effect.gen(function*() {
@@ -1276,7 +1399,7 @@ export interface Volume {
1276
1399
  * return captured.changes.map((change) => change._tag)
1277
1400
  * })
1278
1401
  *
1279
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
1402
+ * Effect.runPromise(program).then(console.log)
1280
1403
  * // [ 'Added' ]
1281
1404
  * ```
1282
1405
  *
@@ -1285,62 +1408,118 @@ export interface Volume {
1285
1408
  */
1286
1409
  export interface OverlayVolume extends Volume {
1287
1410
  /** Computes final differences from the immutable base when this reusable effect executes. */
1288
- readonly changes: (options?: OverlayChangesOptions) => Effect.Effect<ReadonlyArray<OverlayChange>, ConfigurationError | ImageError | FsError>;
1411
+ readonly changes: (options?: OverlayChangesOptions) => Effect.Effect<ReadonlyArray<OverlayChange>, VfsError>;
1289
1412
  /** Captures one committed state when this reusable effect executes. */
1290
- readonly capture: (options?: OverlayChangesOptions) => Effect.Effect<OverlayCapture, ConfigurationError | ImageError | FsError>;
1413
+ readonly capture: (options?: OverlayChangesOptions) => Effect.Effect<OverlayCapture, VfsError>;
1291
1414
  }
1292
1415
  /**
1293
- * Optional Effect service for providing an existing filesystem caller.
1416
+ * The volume an application runs against, as an Effect service. The static
1417
+ * layers wrap the explicit constructors; each call builds a fresh layer, so
1418
+ * share one layer value to share one volume.
1294
1419
  *
1295
1420
  * @example
1296
1421
  * ```ts
1297
1422
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1298
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
1299
1423
  * import { Effect } from "effect"
1300
1424
  *
1301
- * // Code depending on the service does not choose the volume it runs against.
1302
- * const readVersion = Effect.gen(function*() {
1303
- * const caller = yield* Vfs.CurrentFileSystem
1425
+ * const program = Effect.gen(function*() {
1426
+ * const fs = yield* Vfs.Caller
1427
+ * yield* fs.mkdir("/work")
1304
1428
  *
1305
- * return yield* caller.readFile("/version")
1429
+ * return (yield* fs.readDirectory("/")).value.map((entry) => new TextDecoder().decode(entry.name))
1306
1430
  * })
1307
1431
  *
1308
- * const program = Effect.gen(function*() {
1309
- * const volume = yield* Vfs.make()
1310
- * const caller = yield* volume.caller()
1432
+ * Effect.runPromise(program.pipe(Effect.provide(Vfs.Caller.layer()), Effect.provide(Vfs.Volume.layer())))
1433
+ * .then(console.log)
1434
+ * // [ 'work' ]
1435
+ * ```
1311
1436
  *
1312
- * yield* caller.writeFile("/version", new Uint8Array([49]), {
1313
- * access: "write",
1314
- * create: "exclusive"
1437
+ * @category services
1438
+ * @since 0.6.0
1439
+ */
1440
+ export declare const Volume: Context.Service<Volume, Volume> & {
1441
+ /** A fresh empty volume. */
1442
+ readonly layer: (options?: VolumeOptions) => Layer.Layer<Volume, VfsError>;
1443
+ /** A fresh volume restored from a snapshot. */
1444
+ readonly layerFromSnapshot: (snapshot: Snapshot, options?: VolumeOptions) => Layer.Layer<Volume, VfsError>;
1445
+ /** A fresh volume built from a fixture. */
1446
+ readonly layerFromFixture: (fixture: Fixture, options?: VolumeOptions) => Layer.Layer<Volume, VfsError>;
1447
+ /** A writable overlay over a snapshot base. */
1448
+ readonly layerOverlay: (base: Snapshot, options?: VolumeOptions) => Layer.Layer<Volume, VfsError>;
1449
+ /** A durable volume opened through the `LiveImageStore` in context; it shuts down with the layer's scope. */
1450
+ readonly layerLive: (options: LiveVolumeOptions) => Layer.Layer<Volume, VfsError, LiveImageStore>;
1451
+ };
1452
+ /**
1453
+ * The caller an application operates through, as an Effect service. A caller
1454
+ * supplied through the service keeps its own volume, credentials, umask and
1455
+ * working directory; `layer` makes a root caller on the `Volume` in context.
1456
+ *
1457
+ * @category services
1458
+ * @since 0.6.0
1459
+ */
1460
+ export declare const Caller: Context.Service<Caller, Caller> & {
1461
+ /** A root caller on the volume in context; root callers need no scope. */
1462
+ readonly layer: (options?: RootCallerOptions) => Layer.Layer<Caller, VfsError, Volume>;
1463
+ };
1464
+ /**
1465
+ * Streams a snapshot's version 1 encoding as owned UTF-8 chunks: a header line,
1466
+ * then one line per node, each ending in a newline.
1467
+ *
1468
+ * **Details**
1469
+ *
1470
+ * The encoding is newline-delimited JSON. The first line names the format and
1471
+ * version; each later line is one node in inode order, with names and payloads
1472
+ * as canonical base64. Nodes are encoded as the stream is pulled, so writing a
1473
+ * large snapshot to a file or a socket never holds its whole encoding. A chunk
1474
+ * holds at most 128 lines and 64 KiB, so a pull holds at most that and the next
1475
+ * line; a line longer than 64 KiB is a chunk of its own.
1476
+ *
1477
+ * Given `limits`, the stream fails with `LimitExceeded` at the first line that
1478
+ * `decodeSnapshot` would refuse under the same limits, naming the same field,
1479
+ * so bytes it completes are known to decode without decoding them.
1480
+ *
1481
+ * @example
1482
+ * ```ts
1483
+ * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1484
+ * import { Effect, Stream } from "effect"
1485
+ *
1486
+ * const program = Effect.gen(function*() {
1487
+ * const volume = yield* Vfs.fromFixture({
1488
+ * entries: [{ kind: "file", path: "/f", bytes: new Uint8Array([1]) }]
1315
1489
  * })
1316
1490
  *
1317
- * return yield* readVersion.pipe(Effect.provideService(Vfs.CurrentFileSystem, caller))
1491
+ * const text = yield* Vfs.encodeSnapshotStream(yield* volume.snapshot).pipe(
1492
+ * Stream.decodeText,
1493
+ * Stream.mkString
1494
+ * )
1495
+ *
1496
+ * // A header line, then the root and the file, each line ending in a newline.
1497
+ * return text.split("\n").map((line) => (line === "" ? "end" : JSON.parse(line)._tag ?? "header"))
1318
1498
  * })
1319
1499
  *
1320
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
1321
- * // Uint8Array(1) [ 49 ]
1500
+ * Effect.runPromise(program).then(console.log)
1501
+ * // [ 'header', 'directory', 'file', 'end' ]
1322
1502
  * ```
1323
1503
  *
1324
- * @see The testing guide at `/guides/testing-with-an-isolated-filesystem`.
1325
- * @category services
1326
- * @since 0.1.0
1504
+ * @see {@link encodeSnapshot} for the same bytes collected into one array.
1505
+ * @see {@link decodeSnapshotSink} for reading a stream back under limits.
1506
+ * @category serialization
1507
+ * @since 0.6.0
1327
1508
  */
1328
- export declare const CurrentFileSystem: Context.Service<CurrentFileSystem, Caller>;
1509
+ export declare const encodeSnapshotStream: (snapshot: Snapshot, limits?: DecodeLimits) => Stream.Stream<Uint8Array, ImageFailure | ArgumentFailure>;
1329
1510
  /**
1330
- * The caller supplied by the optional filesystem service.
1511
+ * Encodes a snapshot as owned UTF-8 bytes in the version 1 snapshot format.
1331
1512
  *
1332
- * @category services
1333
- * @since 0.1.0
1334
- */
1335
- export interface CurrentFileSystem extends Caller {
1336
- }
1337
- /**
1338
- * Encodes a snapshot as owned UTF-8 JSON bytes using the version 1 snapshot format.
1513
+ * **Details**
1514
+ *
1515
+ * The bytes are the chunks of {@link encodeSnapshotStream} collected into one
1516
+ * array: newline-delimited JSON, a header line and then one line per node.
1517
+ * Given `limits`, encoding fails with `LimitExceeded` wherever `decodeSnapshot`
1518
+ * would refuse the bytes under the same limits.
1339
1519
  *
1340
1520
  * @example
1341
1521
  * ```ts
1342
1522
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1343
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
1344
1523
  * import { Effect } from "effect"
1345
1524
  *
1346
1525
  * // Bytes are portable: store or transmit them, then decode under explicit limits.
@@ -1355,11 +1534,11 @@ export interface CurrentFileSystem extends Caller {
1355
1534
  *
1356
1535
  * const bytes = yield* Vfs.encodeSnapshot(yield* volume.snapshot)
1357
1536
  *
1358
- * // The bytes are UTF-8 JSON carrying the version 1 snapshot format.
1359
- * return JSON.parse(new TextDecoder().decode(bytes)).version
1537
+ * // The first line is the header carrying the version 1 snapshot format.
1538
+ * return JSON.parse(new TextDecoder().decode(bytes).split("\n")[0]!).version
1360
1539
  * })
1361
1540
  *
1362
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
1541
+ * Effect.runPromise(program).then(console.log)
1363
1542
  * // 1
1364
1543
  * ```
1365
1544
  *
@@ -1367,17 +1546,75 @@ export interface CurrentFileSystem extends Caller {
1367
1546
  * @category serialization
1368
1547
  * @since 0.1.0
1369
1548
  */
1370
- export declare const encodeSnapshot: (snapshot: Snapshot) => Effect.Effect<Uint8Array, ImageError>;
1549
+ export declare const encodeSnapshot: (snapshot: Snapshot, limits?: DecodeLimits) => Effect.Effect<Uint8Array, ImageFailure | ArgumentFailure>;
1550
+ /**
1551
+ * A sink that decodes a stream of version 1 snapshot chunks while enforcing
1552
+ * explicit input and payload limits.
1553
+ *
1554
+ * **Details**
1555
+ *
1556
+ * Chunks may split lines, and UTF-8 sequences, anywhere. Each line is checked
1557
+ * as it ends, so input that breaks a limit or a rule is refused before the
1558
+ * sink holds more than one line of it: `maxLineBytes` bounds that line, and
1559
+ * the other limits bound what the accepted lines add up to. A line is
1560
+ * refused at the byte where it first crosses `maxLineBytes` or
1561
+ * `maxEncodedBytes`, so the field a refusal names does not depend on how the
1562
+ * input is chunked. Every line must end in a newline, the last included; a
1563
+ * carriage return, an empty line, a byte-order mark and malformed UTF-8 are
1564
+ * refused as `InvalidEncoding`.
1565
+ *
1566
+ * @example
1567
+ * ```ts
1568
+ * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1569
+ * import { ByteSize, Effect, Stream } from "effect"
1570
+ *
1571
+ * const limits: Vfs.DecodeLimits = {
1572
+ * maxEncodedBytes: ByteSize.megabytes(4),
1573
+ * maxRecords: 10_000,
1574
+ * maxEntries: 10_000,
1575
+ * maxDecodedBytes: ByteSize.megabytes(16),
1576
+ * maxLineBytes: ByteSize.megabytes(1)
1577
+ * }
1578
+ *
1579
+ * const program = Effect.gen(function*() {
1580
+ * const volume = yield* Vfs.fromFixture({
1581
+ * entries: [{ kind: "file", path: "/f", bytes: new Uint8Array([1, 2, 3]) }]
1582
+ * })
1583
+ *
1584
+ * // Rechunked to single bytes: the sink joins lines across chunks.
1585
+ * const snapshot = yield* Vfs.encodeSnapshotStream(yield* volume.snapshot).pipe(
1586
+ * Stream.flattenIterable,
1587
+ * Stream.rechunk(1),
1588
+ * Stream.map((byte) => new Uint8Array([byte])),
1589
+ * Stream.run(Vfs.decodeSnapshotSink(limits))
1590
+ * )
1591
+ *
1592
+ * return yield* (yield* (yield* Vfs.fromSnapshot(snapshot)).caller()).readFile("/f")
1593
+ * })
1594
+ *
1595
+ * Effect.runPromise(program).then(console.log)
1596
+ * // Uint8Array(3) [ 1, 2, 3 ]
1597
+ * ```
1598
+ *
1599
+ * @see {@link DecodeLimits} for what each bound protects.
1600
+ * @see {@link decodeSnapshot} for decoding bytes already in memory.
1601
+ * @category serialization
1602
+ * @since 0.6.0
1603
+ */
1604
+ export declare const decodeSnapshotSink: (limits: DecodeLimits) => Sink.Sink<Snapshot, Uint8Array, never, ImageFailure | ArgumentFailure>;
1371
1605
  /**
1372
1606
  * Decodes version 1 snapshot bytes while enforcing explicit input and payload limits.
1373
1607
  *
1608
+ * **Details**
1609
+ *
1610
+ * The same decoder as {@link decodeSnapshotSink}, run over one chunk.
1611
+ *
1374
1612
  * @example
1375
1613
  * ```ts
1376
1614
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1377
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
1378
1615
  * import { ByteSize, Effect } from "effect"
1379
1616
  *
1380
- * // Limits are mandatory: decoding is the boundary where untrusted input arrives.
1617
+ * // Limits are mandatory: decoding is the boundary where outside input arrives.
1381
1618
  * const program = Effect.gen(function*() {
1382
1619
  * const volume = yield* Vfs.make()
1383
1620
  * const bytes = yield* Vfs.encodeSnapshot(yield* volume.snapshot)
@@ -1389,11 +1626,11 @@ export declare const encodeSnapshot: (snapshot: Snapshot) => Effect.Effect<Uint8
1389
1626
  * maxDecodedBytes: ByteSize.megabytes(16)
1390
1627
  * }).pipe(
1391
1628
  * Effect.as("accepted"),
1392
- * Effect.catchTag("ImageError", (error) => Effect.succeed([error.code, error.field]))
1629
+ * Effect.catchTag("VfsError", (error) => Effect.succeed([error.code, error.field]))
1393
1630
  * )
1394
1631
  * })
1395
1632
  *
1396
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
1633
+ * Effect.runPromise(program).then(console.log)
1397
1634
  * // [ 'LimitExceeded', 'encodedBytes' ]
1398
1635
  * ```
1399
1636
  *
@@ -1401,7 +1638,7 @@ export declare const encodeSnapshot: (snapshot: Snapshot) => Effect.Effect<Uint8
1401
1638
  * @category serialization
1402
1639
  * @since 0.1.0
1403
1640
  */
1404
- export declare const decodeSnapshot: (input: Uint8Array, limits: DecodeLimits) => Effect.Effect<Snapshot, ImageError>;
1641
+ export declare const decodeSnapshot: (input: Uint8Array, limits: DecodeLimits) => Effect.Effect<Snapshot, ImageFailure | ArgumentFailure>;
1405
1642
  /**
1406
1643
  * Computes an exact portable delta between two immutable snapshots.
1407
1644
  * Requires the platform-neutral `Crypto.Crypto` service for base identity. Provide
@@ -1439,7 +1676,7 @@ export declare const decodeSnapshot: (input: Uint8Array, limits: DecodeLimits) =
1439
1676
  * @category snapshots
1440
1677
  * @since 0.1.0
1441
1678
  */
1442
- export declare const diffSnapshots: (base: Snapshot, target: Snapshot, limits?: SnapshotDeltaModel.SnapshotDeltaLimits) => Effect.Effect<SnapshotDeltaModel.SnapshotDelta, ConfigurationError | ImageError | PlatformError.PlatformError, Crypto.Crypto>;
1679
+ export declare const diffSnapshots: (base: Snapshot, target: Snapshot, limits?: SnapshotDeltaModel.SnapshotDeltaLimits) => Effect.Effect<SnapshotDeltaModel.SnapshotDelta, VfsError | PlatformError.PlatformError, Crypto.Crypto>;
1443
1680
  /**
1444
1681
  * Verifies an exact snapshot delta against its base and derives an owned path-oriented summary.
1445
1682
  * Requires the platform-neutral `Crypto.Crypto` service for base identity. Provide
@@ -1474,7 +1711,7 @@ export declare const diffSnapshots: (base: Snapshot, target: Snapshot, limits?:
1474
1711
  * @category snapshots
1475
1712
  * @since 0.1.0
1476
1713
  */
1477
- export declare const inspectSnapshotDelta: (base: Snapshot, delta: SnapshotDeltaModel.SnapshotDelta, options?: SnapshotDeltaModel.SnapshotChangesOptions, limits?: SnapshotDeltaModel.SnapshotDeltaLimits) => Effect.Effect<ReadonlyArray<SnapshotDeltaModel.SnapshotChange>, ConfigurationError | ImageError | PlatformError.PlatformError | SnapshotDeltaModel.SnapshotDeltaError, Crypto.Crypto>;
1714
+ export declare const inspectSnapshotDelta: (base: Snapshot, delta: SnapshotDeltaModel.SnapshotDelta, options?: SnapshotDeltaModel.SnapshotChangesOptions, limits?: SnapshotDeltaModel.SnapshotDeltaLimits) => Effect.Effect<ReadonlyArray<SnapshotDeltaModel.SnapshotChange>, VfsError | PlatformError.PlatformError, Crypto.Crypto>;
1478
1715
  /**
1479
1716
  * Applies an exact delta to its semantically matching base and returns a new snapshot.
1480
1717
  * Requires the platform-neutral `Crypto.Crypto` service for base identity. Provide
@@ -1514,7 +1751,7 @@ export declare const inspectSnapshotDelta: (base: Snapshot, delta: SnapshotDelta
1514
1751
  * @category snapshots
1515
1752
  * @since 0.1.0
1516
1753
  */
1517
- export declare const applySnapshotDelta: (base: Snapshot, delta: SnapshotDeltaModel.SnapshotDelta, limits?: SnapshotDeltaModel.SnapshotDeltaLimits) => Effect.Effect<Snapshot, ConfigurationError | ImageError | PlatformError.PlatformError | SnapshotDeltaModel.SnapshotDeltaError, Crypto.Crypto>;
1754
+ export declare const applySnapshotDelta: (base: Snapshot, delta: SnapshotDeltaModel.SnapshotDelta, limits?: SnapshotDeltaModel.SnapshotDeltaLimits) => Effect.Effect<Snapshot, VfsError | PlatformError.PlatformError, Crypto.Crypto>;
1518
1755
  /**
1519
1756
  * Creates an Effect Schema codec between owned bytes and opaque snapshot deltas.
1520
1757
  * Omission uses `SnapshotDeltaLimits.default`.
@@ -1570,7 +1807,7 @@ export declare const SnapshotDeltaFromBytes: (limits?: SnapshotDeltaModel.Snapsh
1570
1807
  *
1571
1808
  * const rejected = yield* Vfs.pathFromBytes(new Uint8Array([47, 0])).pipe(
1572
1809
  * Effect.as("accepted"),
1573
- * Effect.catchTag("FsError", (error) => Effect.succeed(error.code))
1810
+ * Effect.catchTag("VfsError", (error) => Effect.succeed(error.code))
1574
1811
  * )
1575
1812
  *
1576
1813
  * return [yield* Vfs.pathToBytes(path), rejected]
@@ -1583,14 +1820,14 @@ export declare const SnapshotDeltaFromBytes: (limits?: SnapshotDeltaModel.Snapsh
1583
1820
  * @category constructors
1584
1821
  * @since 0.1.0
1585
1822
  */
1586
- export declare const pathFromBytes: (bytes: Uint8Array) => Effect.Effect<BytePath, FsError>;
1823
+ export declare const pathFromBytes: (bytes: Uint8Array) => Effect.Effect<BytePath, FsFailure>;
1587
1824
  /**
1588
1825
  * Copies the bytes held by an opaque byte path.
1589
1826
  *
1590
1827
  * @category getters
1591
1828
  * @since 0.1.0
1592
1829
  */
1593
- export declare const pathToBytes: (path: BytePath) => Effect.Effect<Uint8Array, FsError>;
1830
+ export declare const pathToBytes: (path: BytePath) => Effect.Effect<Uint8Array, FsFailure>;
1594
1831
  /**
1595
1832
  * Schema for a complete fixture namespace with optional metadata and forward hard links.
1596
1833
  *
@@ -1601,7 +1838,6 @@ export declare const pathToBytes: (path: BytePath) => Effect.Effect<Uint8Array,
1601
1838
  * @example
1602
1839
  * ```ts
1603
1840
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1604
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
1605
1841
  * import { Effect, Schema } from "effect"
1606
1842
  *
1607
1843
  * // Fixtures must list every parent directory before its children.
@@ -1612,17 +1848,17 @@ export declare const pathToBytes: (path: BytePath) => Effect.Effect<Uint8Array,
1612
1848
  * }).pipe(
1613
1849
  * Effect.flatMap(Vfs.fromFixture),
1614
1850
  * Effect.as("built"),
1615
- * Effect.catchTag("ImageError", (error) => Effect.succeed(`rejected: ${error.code}`))
1851
+ * Effect.catchTag("VfsError", (error) => Effect.succeed(`rejected: ${error.code}`))
1616
1852
  * )
1617
1853
  *
1618
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
1854
+ * Effect.runPromise(program).then(console.log)
1619
1855
  * // rejected: InvalidStructure
1620
1856
  * ```
1621
1857
  *
1622
1858
  * @category schemas
1623
1859
  * @since 0.1.0
1624
1860
  */
1625
- export declare const Fixture: typeof VfsModel.Fixture;
1861
+ export declare const Fixture: typeof FixtureModule.Fixture;
1626
1862
  /**
1627
1863
  * A complete filesystem fixture accepted by `fromFixture`.
1628
1864
  *
@@ -1641,7 +1877,6 @@ export type Fixture = typeof Fixture.Type;
1641
1877
  * @example
1642
1878
  * ```ts
1643
1879
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1644
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
1645
1880
  * import { Effect } from "effect"
1646
1881
  *
1647
1882
  * const program = Effect.gen(function*() {
@@ -1654,7 +1889,7 @@ export type Fixture = typeof Fixture.Type;
1654
1889
  * return yield* caller.readFile("/work/notes.txt")
1655
1890
  * })
1656
1891
  *
1657
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
1892
+ * Effect.runPromise(program).then(console.log)
1658
1893
  * // Uint8Array(2) [ 104, 105 ]
1659
1894
  * ```
1660
1895
  *
@@ -1662,14 +1897,59 @@ export type Fixture = typeof Fixture.Type;
1662
1897
  * @category constructors
1663
1898
  * @since 0.1.0
1664
1899
  */
1665
- export declare const make: (options?: VolumeOptions) => Effect.Effect<Volume, ConfigurationError | PlatformError.PlatformError, Crypto.Crypto>;
1900
+ export declare const make: (options?: VolumeOptions) => Effect.Effect<Volume, VfsError>;
1901
+ /**
1902
+ * Streams the tree at `root` in a snapshot as fixture entries, read from the
1903
+ * snapshot itself without restoring a volume.
1904
+ *
1905
+ * **Details**
1906
+ *
1907
+ * `root` resolves as a privileged caller at `/` of a volume restored from the
1908
+ * snapshot with default options would resolve it, following intermediate
1909
+ * symbolic links but not a final one and applying no path byte limit. Entries come in sorted
1910
+ * pre-order, children in the byte order of their names, with paths rooted at
1911
+ * `root`, so the root entry's path is `/`. A path stays a string while every
1912
+ * name on it is UTF-8 and is a `BytePath` otherwise. A file or symbolic link
1913
+ * that more than one name reaches is an entry at its first path and a
1914
+ * `hardLink` to that path at the others. Each file's bytes are an owned copy,
1915
+ * made when the stream pulls its entry. Every entry after the root's is a
1916
+ * fixture entry for a volume holding the same tree.
1917
+ *
1918
+ * @example
1919
+ * ```ts
1920
+ * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1921
+ * import { Effect, Stream } from "effect"
1922
+ *
1923
+ * const program = Effect.gen(function*() {
1924
+ * const volume = yield* Vfs.fromFixture({
1925
+ * entries: [
1926
+ * { kind: "directory", path: "/src" },
1927
+ * { kind: "file", path: "/src/b.txt", bytes: new Uint8Array([2]) },
1928
+ * { kind: "file", path: "/src/a.txt", bytes: new Uint8Array([1]) },
1929
+ * { kind: "hardLink", path: "/src/c.txt", target: "/src/a.txt" }
1930
+ * ]
1931
+ * })
1932
+ *
1933
+ * const entries = yield* Stream.runCollect(Vfs.snapshotEntries(yield* volume.snapshot, "/src"))
1934
+ *
1935
+ * return entries.map((entry) => `${entry.kind} ${String(entry.path)}`)
1936
+ * })
1937
+ *
1938
+ * Effect.runPromise(program).then(console.log)
1939
+ * // [ 'directory /', 'file /a.txt', 'file /b.txt', 'hardLink /c.txt' ]
1940
+ * ```
1941
+ *
1942
+ * @see {@link fromFixture} for building a volume from the entries.
1943
+ * @category snapshots
1944
+ * @since 0.6.0
1945
+ */
1946
+ export declare const snapshotEntries: (snapshot: Snapshot, root: PathInput) => Stream.Stream<Fixture["entries"][number], FsFailure | ImageFailure>;
1666
1947
  /**
1667
1948
  * Restores a fresh volume from an opaque snapshot under the supplied destination limits.
1668
1949
  *
1669
1950
  * @example
1670
1951
  * ```ts
1671
1952
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1672
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
1673
1953
  * import { Effect } from "effect"
1674
1954
  *
1675
1955
  * const program = Effect.gen(function*() {
@@ -1696,7 +1976,7 @@ export declare const make: (options?: VolumeOptions) => Effect.Effect<Volume, Co
1696
1976
  * ]
1697
1977
  * })
1698
1978
  *
1699
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
1979
+ * Effect.runPromise(program).then(console.log)
1700
1980
  * // [ 'one', 'two' ]
1701
1981
  * ```
1702
1982
  *
@@ -1704,7 +1984,7 @@ export declare const make: (options?: VolumeOptions) => Effect.Effect<Volume, Co
1704
1984
  * @category constructors
1705
1985
  * @since 0.1.0
1706
1986
  */
1707
- export declare const fromSnapshot: (snapshot: Snapshot, options?: VolumeOptions) => Effect.Effect<Volume, ConfigurationError | ImageError | PlatformError.PlatformError, Crypto.Crypto>;
1987
+ export declare const fromSnapshot: (snapshot: Snapshot, options?: VolumeOptions) => Effect.Effect<Volume, VfsError>;
1708
1988
  /**
1709
1989
  * Creates an isolated writable volume relative to one immutable snapshot base.
1710
1990
  *
@@ -1715,13 +1995,12 @@ export declare const fromSnapshot: (snapshot: Snapshot, options?: VolumeOptions)
1715
1995
  * storage. Metadata, namespace state, coordination, handles, and watches are
1716
1996
  * always private to the new workspace.
1717
1997
  *
1718
- * Invalid base snapshots fail with `ImageError`; invalid volume limits fail
1719
- * with `ConfigurationError`. Each execution creates a fresh workspace.
1998
+ * Invalid base snapshots and invalid volume limits fail with `VfsError`. Each
1999
+ * execution creates a fresh workspace.
1720
2000
  *
1721
2001
  * @example
1722
2002
  * ```ts
1723
2003
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1724
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
1725
2004
  * import { Effect } from "effect"
1726
2005
  *
1727
2006
  * const program = Effect.gen(function*() {
@@ -1747,7 +2026,7 @@ export declare const fromSnapshot: (snapshot: Snapshot, options?: VolumeOptions)
1747
2026
  * ]
1748
2027
  * })
1749
2028
  *
1750
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
2029
+ * Effect.runPromise(program).then(console.log)
1751
2030
  * // [ '{}', [ 'Updated' ] ]
1752
2031
  * ```
1753
2032
  *
@@ -1755,20 +2034,19 @@ export declare const fromSnapshot: (snapshot: Snapshot, options?: VolumeOptions)
1755
2034
  * @category constructors
1756
2035
  * @since 0.1.0
1757
2036
  */
1758
- export declare const makeOverlay: (base: Snapshot, options?: VolumeOptions) => Effect.Effect<OverlayVolume, ConfigurationError | ImageError | PlatformError.PlatformError, Crypto.Crypto>;
2037
+ export declare const makeOverlay: (base: Snapshot, options?: VolumeOptions) => Effect.Effect<OverlayVolume, VfsError>;
1759
2038
  /**
1760
2039
  * Builds a fresh volume from a validated final-state fixture.
1761
2040
  *
1762
2041
  * @example
1763
2042
  * ```ts
1764
2043
  * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
1765
- * import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
1766
2044
  * import { Effect } from "effect"
1767
2045
  *
1768
2046
  * const encoder = new TextEncoder()
1769
2047
  *
1770
2048
  * const program = Effect.gen(function*() {
1771
- * // Parent directories must be listed explicitly, before their children.
2049
+ * // Parent directories must be listed explicitly, in any order.
1772
2050
  * const volume = yield* Vfs.fromFixture({
1773
2051
  * entries: [
1774
2052
  * { kind: "directory", path: "/project" },
@@ -1781,16 +2059,19 @@ export declare const makeOverlay: (base: Snapshot, options?: VolumeOptions) => E
1781
2059
  * ]
1782
2060
  * })
1783
2061
  *
1784
- * return yield* (yield* volume.caller()).readDirectory("/project")
2062
+ * // A fixture's directories list their entries in the byte order of their names.
2063
+ * const listing = yield* (yield* volume.caller()).readDirectory("/project")
2064
+ *
2065
+ * return listing.value.map((entry) => new TextDecoder().decode(entry.name))
1785
2066
  * })
1786
2067
  *
1787
- * Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
1788
- * // [ 'package.json', 'latest' ]
2068
+ * Effect.runPromise(program).then(console.log)
2069
+ * // [ 'latest', 'package.json' ]
1789
2070
  * ```
1790
2071
  *
1791
2072
  * @see The fixtures guide at `/guides/fixtures-and-snapshots`.
1792
2073
  * @category constructors
1793
2074
  * @since 0.1.0
1794
2075
  */
1795
- export declare const fromFixture: (fixture: Fixture, options?: VolumeOptions) => Effect.Effect<Volume, ConfigurationError | ImageError | PlatformError.PlatformError, Crypto.Crypto>;
2076
+ export declare const fromFixture: (fixture: Fixture, options?: VolumeOptions) => Effect.Effect<Volume, VfsError>;
1796
2077
  //# sourceMappingURL=VirtualFileSystem.d.ts.map