@effect-vfs/core 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BytePath.d.ts +49 -5
- package/dist/BytePath.d.ts.map +1 -1
- package/dist/BytePath.js +9 -9
- package/dist/Snapshot.d.ts +61 -5
- package/dist/Snapshot.d.ts.map +1 -1
- package/dist/Snapshot.js +60 -5
- package/dist/SnapshotDelta.d.ts +208 -31
- package/dist/SnapshotDelta.d.ts.map +1 -1
- package/dist/SnapshotDelta.js +162 -25
- package/dist/VirtualFileSystem.d.ts +1035 -348
- package/dist/VirtualFileSystem.d.ts.map +1 -1
- package/dist/VirtualFileSystem.js +620 -1772
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/internal/bytePath.d.ts +10 -0
- package/dist/internal/bytePath.d.ts.map +1 -0
- package/dist/internal/bytePath.js +41 -0
- package/dist/internal/bytes.d.ts +6 -0
- package/dist/internal/bytes.d.ts.map +1 -0
- package/dist/internal/bytes.js +17 -0
- package/dist/internal/canonicalBase64.d.ts +14 -6
- package/dist/internal/canonicalBase64.d.ts.map +1 -1
- package/dist/internal/canonicalBase64.js +29 -7
- package/dist/internal/image.d.ts +85 -104
- package/dist/internal/image.d.ts.map +1 -1
- package/dist/internal/image.js +99 -95
- package/dist/internal/metadata.d.ts +28 -0
- package/dist/internal/metadata.d.ts.map +1 -0
- package/dist/internal/metadata.js +29 -0
- package/dist/internal/snapshotDelta.d.ts +0 -1
- package/dist/internal/snapshotDelta.d.ts.map +1 -1
- package/dist/internal/snapshotDelta.js +358 -325
- package/dist/internal/snapshotDeltaModel.d.ts +10 -0
- package/dist/internal/snapshotDeltaModel.d.ts.map +1 -0
- package/dist/internal/snapshotDeltaModel.js +15 -0
- package/dist/internal/virtualFileSystem/errors.d.ts +31 -0
- package/dist/internal/virtualFileSystem/errors.d.ts.map +1 -0
- package/dist/internal/virtualFileSystem/errors.js +51 -0
- package/dist/internal/virtualFileSystem/fixture.d.ts +63 -0
- package/dist/internal/virtualFileSystem/fixture.d.ts.map +1 -0
- package/dist/internal/virtualFileSystem/fixture.js +144 -0
- package/dist/internal/{content.d.ts → virtualFileSystem/overlayContent.d.ts} +4 -14
- package/dist/internal/virtualFileSystem/overlayContent.d.ts.map +1 -0
- package/dist/internal/virtualFileSystem/overlayContent.js +29 -0
- package/dist/internal/{overlayChanges.d.ts → virtualFileSystem/overlayDiff.d.ts} +3 -12
- package/dist/internal/virtualFileSystem/overlayDiff.d.ts.map +1 -0
- package/dist/internal/virtualFileSystem/overlayDiff.js +120 -0
- package/dist/internal/virtualFileSystem/path.d.ts +44 -0
- package/dist/internal/virtualFileSystem/path.d.ts.map +1 -0
- package/dist/internal/virtualFileSystem/path.js +127 -0
- package/dist/internal/virtualFileSystem/testHooks.d.ts +20 -0
- package/dist/internal/virtualFileSystem/testHooks.d.ts.map +1 -0
- package/dist/internal/virtualFileSystem/testHooks.js +23 -0
- package/dist/internal/virtualFileSystem/watchHub.d.ts +16 -0
- package/dist/internal/virtualFileSystem/watchHub.d.ts.map +1 -0
- package/dist/internal/virtualFileSystem/watchHub.js +33 -0
- package/dist/internal/virtualFileSystem.d.ts +366 -0
- package/dist/internal/virtualFileSystem.d.ts.map +1 -0
- package/dist/internal/virtualFileSystem.js +1755 -0
- package/package.json +15 -5
- package/dist/internal/content.d.ts.map +0 -1
- package/dist/internal/content.js +0 -26
- package/dist/internal/overlayChanges.d.ts.map +0 -1
- package/dist/internal/overlayChanges.js +0 -146
- package/dist/internal/overlayTesting.d.ts +0 -12
- package/dist/internal/overlayTesting.d.ts.map +0 -1
- package/dist/internal/overlayTesting.js +0 -12
|
@@ -8,25 +8,31 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @since 0.1.0
|
|
10
10
|
*/
|
|
11
|
-
import * as ByteSize from "effect/ByteSize";
|
|
12
|
-
import * as Context from "effect/Context";
|
|
13
|
-
import { DecodeLimits, ImageError, type Snapshot } from "./Snapshot.js";
|
|
14
|
-
export { DecodeLimits, ImageError, type Snapshot, SnapshotTypeId } from "./Snapshot.js";
|
|
15
11
|
import * as Effect from "effect/Effect";
|
|
16
12
|
import * as Schema from "effect/Schema";
|
|
17
|
-
import * as Scope from "effect/Scope";
|
|
18
|
-
import * as Stream from "effect/Stream";
|
|
19
|
-
import { BytePath } from "./BytePath.js";
|
|
13
|
+
import type * as Scope from "effect/Scope";
|
|
14
|
+
import type * as Stream from "effect/Stream";
|
|
15
|
+
import type { BytePath } from "./BytePath.js";
|
|
20
16
|
export { BytePath } from "./BytePath.js";
|
|
17
|
+
import type { DecodeLimits, ImageError, Snapshot } from "./Snapshot.js";
|
|
18
|
+
export { DecodeLimits, ImageError, type Snapshot, SnapshotTypeId } from "./Snapshot.js";
|
|
19
|
+
import * as VfsModel from "./internal/virtualFileSystem.js";
|
|
21
20
|
import * as SnapshotDeltaModel from "./SnapshotDelta.js";
|
|
22
21
|
export { SnapshotChange, SnapshotChangesOptions, type SnapshotDelta, SnapshotDeltaError, SnapshotDeltaLimits, SnapshotDeltaTypeId, SnapshotDifference, SnapshotNodeKind } from "./SnapshotDelta.js";
|
|
23
|
-
declare const VolumeId:
|
|
24
|
-
declare const CallerId:
|
|
25
|
-
declare const FileHandleId:
|
|
26
|
-
declare const DirectoryHandleId:
|
|
22
|
+
declare const VolumeId: typeof VfsModel.VolumeId;
|
|
23
|
+
declare const CallerId: typeof VfsModel.CallerId;
|
|
24
|
+
declare const FileHandleId: typeof VfsModel.FileHandleId;
|
|
25
|
+
declare const DirectoryHandleId: typeof VfsModel.DirectoryHandleId;
|
|
26
|
+
declare const ObjectReferenceId: typeof VfsModel.ObjectReferenceId;
|
|
27
27
|
/**
|
|
28
28
|
* A UTF-8 string path or an opaque byte-preserving path.
|
|
29
29
|
*
|
|
30
|
+
* **Details**
|
|
31
|
+
*
|
|
32
|
+
* Every operation accepts both. Pass a string unless a name is not valid UTF-8;
|
|
33
|
+
* strings are the normal case and byte paths are the escape hatch.
|
|
34
|
+
*
|
|
35
|
+
* @see {@link BytePath} for the byte-preserving form.
|
|
30
36
|
* @category models
|
|
31
37
|
* @since 0.1.0
|
|
32
38
|
*/
|
|
@@ -34,10 +40,34 @@ export type PathInput = string | BytePath;
|
|
|
34
40
|
/**
|
|
35
41
|
* Schema for portable virtual filesystem error codes.
|
|
36
42
|
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
46
|
+
* import { Effect, Schema } from "effect"
|
|
47
|
+
*
|
|
48
|
+
* const asCode = Schema.decodeUnknownEffect(Vfs.FsCode)
|
|
49
|
+
*
|
|
50
|
+
* // Useful when a code has crossed a process boundary and arrives back as data.
|
|
51
|
+
* const program = Effect.gen(function*() {
|
|
52
|
+
* const payload = JSON.parse(`{"code":"NotFound","other":"Nonsense"}`)
|
|
53
|
+
*
|
|
54
|
+
* const known = yield* asCode(payload.code)
|
|
55
|
+
*
|
|
56
|
+
* const unknown = yield* asCode(payload.other).pipe(
|
|
57
|
+
* Effect.orElseSucceed(() => "not a code")
|
|
58
|
+
* )
|
|
59
|
+
*
|
|
60
|
+
* return [known, unknown]
|
|
61
|
+
* })
|
|
62
|
+
*
|
|
63
|
+
* Effect.runPromise(program).then(console.log)
|
|
64
|
+
* // [ 'NotFound', 'not a code' ]
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
37
67
|
* @category schemas
|
|
38
68
|
* @since 0.1.0
|
|
39
69
|
*/
|
|
40
|
-
export declare const FsCode:
|
|
70
|
+
export declare const FsCode: typeof VfsModel.FsCode;
|
|
41
71
|
/**
|
|
42
72
|
* A portable virtual filesystem error code.
|
|
43
73
|
*
|
|
@@ -45,37 +75,67 @@ export declare const FsCode: Schema.Literals<readonly ["NotFound", "AlreadyExist
|
|
|
45
75
|
* @since 0.1.0
|
|
46
76
|
*/
|
|
47
77
|
export type FsCode = typeof FsCode.Type;
|
|
48
|
-
declare const FsError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
49
|
-
readonly _tag: "FsError";
|
|
50
|
-
} & Readonly<A>;
|
|
51
78
|
/**
|
|
52
79
|
* Describes an expected filesystem operation failure.
|
|
53
80
|
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```ts
|
|
83
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
84
|
+
* import { Effect } from "effect"
|
|
85
|
+
*
|
|
86
|
+
* const program = Effect.gen(function*() {
|
|
87
|
+
* const caller = yield* (yield* Vfs.make()).caller()
|
|
88
|
+
*
|
|
89
|
+
* // Every filesystem operation fails with this one tagged error; `code`
|
|
90
|
+
* // distinguishes the cases, so match on it rather than on the tag alone.
|
|
91
|
+
* return yield* caller.readFile("/missing").pipe(
|
|
92
|
+
* Effect.catchTag("FsError", (error) =>
|
|
93
|
+
* error.code === "NotFound"
|
|
94
|
+
* ? Effect.succeed(new Uint8Array())
|
|
95
|
+
* : Effect.fail(error))
|
|
96
|
+
* )
|
|
97
|
+
* })
|
|
98
|
+
*
|
|
99
|
+
* Effect.runPromise(program).then(console.log)
|
|
100
|
+
* // Uint8Array(0) []
|
|
101
|
+
* ```
|
|
102
|
+
*
|
|
54
103
|
* @category errors
|
|
55
104
|
* @since 0.1.0
|
|
56
105
|
*/
|
|
57
|
-
export declare
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
/** Operation that detected the failure. */
|
|
61
|
-
readonly operation: string;
|
|
62
|
-
/** Path involved in the failure, when one path identifies it. */
|
|
63
|
-
readonly path?: PathInput;
|
|
64
|
-
}> {
|
|
106
|
+
export declare const FsError: typeof VfsModel.FsError;
|
|
107
|
+
/** @internal */
|
|
108
|
+
export interface FsError extends VfsModel.FsError {
|
|
65
109
|
}
|
|
66
|
-
declare const ConfigurationError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
67
|
-
readonly _tag: "ConfigurationError";
|
|
68
|
-
} & Readonly<A>;
|
|
69
110
|
/**
|
|
70
111
|
* Describes an invalid volume or caller option and names the rejected field.
|
|
71
112
|
*
|
|
113
|
+
* @example
|
|
114
|
+
* ```ts
|
|
115
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
116
|
+
* import { Effect } from "effect"
|
|
117
|
+
*
|
|
118
|
+
* // `field` names the rejected option, so the caller learns which key was wrong.
|
|
119
|
+
* const program = Effect.gen(function*() {
|
|
120
|
+
* // Options that arrived from a config file or CLI flag, not a literal.
|
|
121
|
+
* const options = JSON.parse(`{"maxEntries":-1}`) as Vfs.VolumeOptions
|
|
122
|
+
*
|
|
123
|
+
* return yield* Vfs.make(options).pipe(
|
|
124
|
+
* Effect.as("ok"),
|
|
125
|
+
* Effect.catchTag("ConfigurationError", (error) => Effect.succeed(`rejected ${error.field}`))
|
|
126
|
+
* )
|
|
127
|
+
* })
|
|
128
|
+
*
|
|
129
|
+
* Effect.runPromise(program).then(console.log)
|
|
130
|
+
* // rejected maxEntries
|
|
131
|
+
* ```
|
|
132
|
+
*
|
|
72
133
|
* @category errors
|
|
73
134
|
* @since 0.1.0
|
|
74
135
|
*/
|
|
75
|
-
export declare
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}> {
|
|
136
|
+
export declare const ConfigurationError: typeof VfsModel.ConfigurationError;
|
|
137
|
+
/** @internal */
|
|
138
|
+
export interface ConfigurationError extends VfsModel.ConfigurationError {
|
|
79
139
|
}
|
|
80
140
|
/**
|
|
81
141
|
* Schema for a caller's numeric identity, supplementary groups, and explicit privilege.
|
|
@@ -83,16 +143,7 @@ export declare class ConfigurationError extends ConfigurationError_base<{
|
|
|
83
143
|
* @category schemas
|
|
84
144
|
* @since 0.1.0
|
|
85
145
|
*/
|
|
86
|
-
export declare const Identity:
|
|
87
|
-
/** Numeric user identifier used by ownership and permission checks. */
|
|
88
|
-
readonly uid: Schema.Finite;
|
|
89
|
-
/** Primary numeric group identifier. */
|
|
90
|
-
readonly gid: Schema.Finite;
|
|
91
|
-
/** Supplementary group identifiers used by group permission checks. */
|
|
92
|
-
readonly groups: Schema.$Array<Schema.Finite>;
|
|
93
|
-
/** Grants root-style permission bypasses independently of `uid`. */
|
|
94
|
-
readonly privileged: Schema.Boolean;
|
|
95
|
-
}>;
|
|
146
|
+
export declare const Identity: typeof VfsModel.Identity;
|
|
96
147
|
/**
|
|
97
148
|
* A caller identity used for permission checks.
|
|
98
149
|
*
|
|
@@ -103,24 +154,37 @@ export type Identity = typeof Identity.Type;
|
|
|
103
154
|
/**
|
|
104
155
|
* Schema for root caller credentials and creation mask.
|
|
105
156
|
*
|
|
157
|
+
* @example
|
|
158
|
+
* ```ts
|
|
159
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
160
|
+
* import { Effect } from "effect"
|
|
161
|
+
*
|
|
162
|
+
* const program = Effect.gen(function*() {
|
|
163
|
+
* const volume = yield* Vfs.make()
|
|
164
|
+
*
|
|
165
|
+
* // Omitting `identity` yields a privileged root caller.
|
|
166
|
+
* const root = yield* volume.caller({ umask: 0 })
|
|
167
|
+
* const user = yield* volume.caller({
|
|
168
|
+
* identity: { uid: 1000, gid: 1000, groups: [], privileged: false },
|
|
169
|
+
* umask: 0o022
|
|
170
|
+
* })
|
|
171
|
+
*
|
|
172
|
+
* yield* root.mkdir("/home", { mode: 0o777 })
|
|
173
|
+
*
|
|
174
|
+
* // The umask clears its bits from the requested mode.
|
|
175
|
+
* yield* user.mkdir("/home/user", { mode: 0o777 })
|
|
176
|
+
*
|
|
177
|
+
* return ((yield* user.stat("/home/user")).mode & 0o777).toString(8)
|
|
178
|
+
* })
|
|
179
|
+
*
|
|
180
|
+
* Effect.runPromise(program).then(console.log)
|
|
181
|
+
* // 755
|
|
182
|
+
* ```
|
|
183
|
+
*
|
|
106
184
|
* @category schemas
|
|
107
185
|
* @since 0.1.0
|
|
108
186
|
*/
|
|
109
|
-
export declare const RootCallerOptions:
|
|
110
|
-
/** Caller identity. Defaults to privileged uid and gid `0`. */
|
|
111
|
-
readonly identity: Schema.optionalKey<Schema.Struct<{
|
|
112
|
-
/** Numeric user identifier used by ownership and permission checks. */
|
|
113
|
-
readonly uid: Schema.Finite;
|
|
114
|
-
/** Primary numeric group identifier. */
|
|
115
|
-
readonly gid: Schema.Finite;
|
|
116
|
-
/** Supplementary group identifiers used by group permission checks. */
|
|
117
|
-
readonly groups: Schema.$Array<Schema.Finite>;
|
|
118
|
-
/** Grants root-style permission bypasses independently of `uid`. */
|
|
119
|
-
readonly privileged: Schema.Boolean;
|
|
120
|
-
}>>;
|
|
121
|
-
/** Creation mask applied to requested modes. Defaults to `0o022`. */
|
|
122
|
-
readonly umask: Schema.optionalKey<Schema.Finite>;
|
|
123
|
-
}>;
|
|
187
|
+
export declare const RootCallerOptions: typeof VfsModel.RootCallerOptions;
|
|
124
188
|
/**
|
|
125
189
|
* Options for creating a root caller on a volume.
|
|
126
190
|
*
|
|
@@ -131,19 +195,33 @@ export type RootCallerOptions = typeof RootCallerOptions.Type;
|
|
|
131
195
|
/**
|
|
132
196
|
* Schema for optional volume capacity and path limits.
|
|
133
197
|
*
|
|
198
|
+
* @example
|
|
199
|
+
* ```ts
|
|
200
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
201
|
+
* import { ByteSize, Effect } from "effect"
|
|
202
|
+
*
|
|
203
|
+
* // Every limit is optional, and each one is enforced once set.
|
|
204
|
+
* const program = Effect.gen(function*() {
|
|
205
|
+
* const volume = yield* Vfs.make({ maxFileBytes: ByteSize.bytes(4) })
|
|
206
|
+
* const caller = yield* volume.caller()
|
|
207
|
+
*
|
|
208
|
+
* return yield* caller.writeFile("/big.bin", new Uint8Array(16), {
|
|
209
|
+
* access: "write",
|
|
210
|
+
* create: "exclusive"
|
|
211
|
+
* }).pipe(
|
|
212
|
+
* Effect.as("written"),
|
|
213
|
+
* Effect.catchTag("FsError", (error) => Effect.succeed(error.code))
|
|
214
|
+
* )
|
|
215
|
+
* })
|
|
216
|
+
*
|
|
217
|
+
* Effect.runPromise(program).then(console.log)
|
|
218
|
+
* // FileTooLarge
|
|
219
|
+
* ```
|
|
220
|
+
*
|
|
134
221
|
* @category schemas
|
|
135
222
|
* @since 0.1.0
|
|
136
223
|
*/
|
|
137
|
-
export declare const VolumeOptions:
|
|
138
|
-
/** Maximum number of filesystem nodes. Omission leaves the count unbounded. */
|
|
139
|
-
readonly maxEntries: Schema.optionalKey<Schema.Finite>;
|
|
140
|
-
/** Maximum combined regular-file content in bytes. */
|
|
141
|
-
readonly maxBytes: Schema.optionalKey<Schema.ByteSize>;
|
|
142
|
-
/** Maximum content size of one regular file in bytes. */
|
|
143
|
-
readonly maxFileBytes: Schema.optionalKey<Schema.ByteSize>;
|
|
144
|
-
/** Maximum encoded byte length of an absolute or relative path. */
|
|
145
|
-
readonly maxPathBytes: Schema.optionalKey<Schema.ByteSize>;
|
|
146
|
-
}>;
|
|
224
|
+
export declare const VolumeOptions: typeof VfsModel.VolumeOptions;
|
|
147
225
|
/**
|
|
148
226
|
* Capacity and path limits for a volume.
|
|
149
227
|
*
|
|
@@ -154,22 +232,33 @@ export type VolumeOptions = typeof VolumeOptions.Type;
|
|
|
154
232
|
/**
|
|
155
233
|
* Schema for filesystem node metadata with bigint inode, size, and nanosecond fields.
|
|
156
234
|
*
|
|
235
|
+
* @example
|
|
236
|
+
* ```ts
|
|
237
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
238
|
+
* import { Effect } from "effect"
|
|
239
|
+
*
|
|
240
|
+
* // Sizes and inode numbers are bigint; times are nanosecond timestamps.
|
|
241
|
+
* const program = Effect.gen(function*() {
|
|
242
|
+
* const caller = yield* (yield* Vfs.make()).caller()
|
|
243
|
+
*
|
|
244
|
+
* yield* caller.writeFile("/f", new Uint8Array([1, 2, 3]), {
|
|
245
|
+
* access: "write",
|
|
246
|
+
* create: "exclusive"
|
|
247
|
+
* })
|
|
248
|
+
*
|
|
249
|
+
* const metadata = yield* caller.stat("/f")
|
|
250
|
+
*
|
|
251
|
+
* return [metadata.kind, metadata.size, metadata.nlink]
|
|
252
|
+
* })
|
|
253
|
+
*
|
|
254
|
+
* Effect.runPromise(program).then(console.log)
|
|
255
|
+
* // [ 'file', 3n, 1 ]
|
|
256
|
+
* ```
|
|
257
|
+
*
|
|
157
258
|
* @category schemas
|
|
158
259
|
* @since 0.1.0
|
|
159
260
|
*/
|
|
160
|
-
export declare const Metadata:
|
|
161
|
-
readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]>;
|
|
162
|
-
readonly ino: Schema.BigInt;
|
|
163
|
-
readonly nlink: Schema.Finite;
|
|
164
|
-
readonly size: Schema.BigInt;
|
|
165
|
-
readonly uid: Schema.Finite;
|
|
166
|
-
readonly gid: Schema.Finite;
|
|
167
|
-
readonly mode: Schema.Finite;
|
|
168
|
-
readonly atimeNs: Schema.BigInt;
|
|
169
|
-
readonly mtimeNs: Schema.BigInt;
|
|
170
|
-
readonly ctimeNs: Schema.BigInt;
|
|
171
|
-
readonly birthtimeNs: Schema.BigInt;
|
|
172
|
-
}>;
|
|
261
|
+
export declare const Metadata: typeof VfsModel.Metadata;
|
|
173
262
|
/**
|
|
174
263
|
* Metadata for a directory, regular file, or symbolic link.
|
|
175
264
|
*
|
|
@@ -177,6 +266,63 @@ export declare const Metadata: Schema.Struct<{
|
|
|
177
266
|
* @since 0.1.0
|
|
178
267
|
*/
|
|
179
268
|
export type Metadata = typeof Metadata.Type;
|
|
269
|
+
/**
|
|
270
|
+
* An opaque identity for one object in one live volume.
|
|
271
|
+
*
|
|
272
|
+
* @category models
|
|
273
|
+
* @since 0.1.0
|
|
274
|
+
*/
|
|
275
|
+
export interface ObjectReference {
|
|
276
|
+
readonly [ObjectReferenceId]: true;
|
|
277
|
+
}
|
|
278
|
+
/**
|
|
279
|
+
* A value and the revision of the object from the same coordinated observation.
|
|
280
|
+
*
|
|
281
|
+
* @example
|
|
282
|
+
* ```ts
|
|
283
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
284
|
+
* import { Effect } from "effect"
|
|
285
|
+
*
|
|
286
|
+
* // References survive renames, and each observation carries the revision it
|
|
287
|
+
* // matched, so a caller can tell whether what it read is still current.
|
|
288
|
+
* const program = Effect.gen(function*() {
|
|
289
|
+
* const caller = yield* (yield* Vfs.make()).caller()
|
|
290
|
+
*
|
|
291
|
+
* yield* caller.mkdir("/work")
|
|
292
|
+
*
|
|
293
|
+
* const root = yield* caller.rootReference
|
|
294
|
+
* const work = yield* caller.lookupReference(root, new TextEncoder().encode("work"))
|
|
295
|
+
*
|
|
296
|
+
* const before = yield* caller.observeMetadata(work)
|
|
297
|
+
*
|
|
298
|
+
* yield* caller.rename("/work", "/moved")
|
|
299
|
+
*
|
|
300
|
+
* const after = yield* caller.observeMetadata(work)
|
|
301
|
+
*
|
|
302
|
+
* return [before.value.kind, after.value.kind]
|
|
303
|
+
* })
|
|
304
|
+
*
|
|
305
|
+
* Effect.runPromise(program).then(console.log)
|
|
306
|
+
* // [ 'directory', 'directory' ]
|
|
307
|
+
* ```
|
|
308
|
+
*
|
|
309
|
+
* @category models
|
|
310
|
+
* @since 0.1.0
|
|
311
|
+
*/
|
|
312
|
+
export interface ObjectObservation<A> {
|
|
313
|
+
readonly value: A;
|
|
314
|
+
readonly revision: bigint;
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* One owned directory name paired with the referenced child object.
|
|
318
|
+
*
|
|
319
|
+
* @category models
|
|
320
|
+
* @since 0.1.0
|
|
321
|
+
*/
|
|
322
|
+
export interface DirectoryEntry {
|
|
323
|
+
readonly name: Uint8Array;
|
|
324
|
+
readonly reference: ObjectReference;
|
|
325
|
+
}
|
|
180
326
|
/**
|
|
181
327
|
* Resolves a relative path from a live directory handle instead of the caller's directory.
|
|
182
328
|
*
|
|
@@ -190,6 +336,36 @@ export interface RelativeOptions {
|
|
|
190
336
|
/**
|
|
191
337
|
* Controls the base directory and whether metadata operations follow the final symbolic link.
|
|
192
338
|
*
|
|
339
|
+
* @example
|
|
340
|
+
* ```ts
|
|
341
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
342
|
+
* import { Effect } from "effect"
|
|
343
|
+
*
|
|
344
|
+
* const program = Effect.gen(function*() {
|
|
345
|
+
* const caller = yield* (yield* Vfs.make()).caller()
|
|
346
|
+
*
|
|
347
|
+
* yield* caller.writeFile("/target", new Uint8Array([1]), {
|
|
348
|
+
* access: "write",
|
|
349
|
+
* create: "exclusive",
|
|
350
|
+
* mode: 0o644
|
|
351
|
+
* })
|
|
352
|
+
* yield* caller.symlink("/target", "/link")
|
|
353
|
+
*
|
|
354
|
+
* // Metadata operations follow the final link by default, so this reaches the
|
|
355
|
+
* // target; opting out changes the link itself and leaves the target alone.
|
|
356
|
+
* yield* caller.chmod("/link", 0o600)
|
|
357
|
+
* const followed = (yield* caller.stat("/target")).mode & 0o777
|
|
358
|
+
*
|
|
359
|
+
* yield* caller.chmod("/link", 0o777, { followFinalSymlink: false })
|
|
360
|
+
* const unchanged = (yield* caller.stat("/target")).mode & 0o777
|
|
361
|
+
*
|
|
362
|
+
* return [followed.toString(8), unchanged.toString(8)]
|
|
363
|
+
* })
|
|
364
|
+
*
|
|
365
|
+
* Effect.runPromise(program).then(console.log)
|
|
366
|
+
* // [ '600', '600' ]
|
|
367
|
+
* ```
|
|
368
|
+
*
|
|
193
369
|
* @category models
|
|
194
370
|
* @since 0.1.0
|
|
195
371
|
*/
|
|
@@ -203,10 +379,7 @@ export interface MetadataOptions extends RelativeOptions {
|
|
|
203
379
|
* @category schemas
|
|
204
380
|
* @since 0.1.0
|
|
205
381
|
*/
|
|
206
|
-
export declare const OwnerUpdate:
|
|
207
|
-
readonly uid: Schema.optionalKey<Schema.Finite>;
|
|
208
|
-
readonly gid: Schema.optionalKey<Schema.Finite>;
|
|
209
|
-
}>;
|
|
382
|
+
export declare const OwnerUpdate: typeof VfsModel.OwnerUpdate;
|
|
210
383
|
/**
|
|
211
384
|
* An owner update for `chown` operations.
|
|
212
385
|
*
|
|
@@ -220,38 +393,42 @@ export type OwnerUpdate = typeof OwnerUpdate.Type;
|
|
|
220
393
|
* @category schemas
|
|
221
394
|
* @since 0.1.0
|
|
222
395
|
*/
|
|
223
|
-
export declare const TimeUpdate:
|
|
224
|
-
readonly kind: Schema.Literal<"now">;
|
|
225
|
-
}>, Schema.Struct<{
|
|
226
|
-
readonly kind: Schema.Literal<"omit">;
|
|
227
|
-
}>, Schema.Struct<{
|
|
228
|
-
readonly kind: Schema.Literal<"value">;
|
|
229
|
-
readonly nanoseconds: Schema.BigInt;
|
|
230
|
-
}>]>;
|
|
396
|
+
export declare const TimeUpdate: typeof VfsModel.TimeUpdate;
|
|
231
397
|
/**
|
|
232
398
|
* Schema for independent access and modification time updates.
|
|
233
399
|
*
|
|
400
|
+
* @example
|
|
401
|
+
* ```ts
|
|
402
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
403
|
+
* import { Effect } from "effect"
|
|
404
|
+
*
|
|
405
|
+
* const program = Effect.gen(function*() {
|
|
406
|
+
* const caller = yield* (yield* Vfs.make()).caller()
|
|
407
|
+
*
|
|
408
|
+
* yield* caller.writeFile("/f", new Uint8Array([1]), {
|
|
409
|
+
* access: "write",
|
|
410
|
+
* create: "exclusive"
|
|
411
|
+
* })
|
|
412
|
+
*
|
|
413
|
+
* // Each field is set to a value, to the current clock, or left alone.
|
|
414
|
+
* yield* caller.utimes("/f", {
|
|
415
|
+
* access: { kind: "value", nanoseconds: 1_000n },
|
|
416
|
+
* modification: { kind: "omit" }
|
|
417
|
+
* })
|
|
418
|
+
*
|
|
419
|
+
* const metadata = yield* caller.stat("/f")
|
|
420
|
+
*
|
|
421
|
+
* return [metadata.atimeNs, metadata.mtimeNs === 1_000n]
|
|
422
|
+
* })
|
|
423
|
+
*
|
|
424
|
+
* Effect.runPromise(program).then(console.log)
|
|
425
|
+
* // [ 1000n, false ]
|
|
426
|
+
* ```
|
|
427
|
+
*
|
|
234
428
|
* @category schemas
|
|
235
429
|
* @since 0.1.0
|
|
236
430
|
*/
|
|
237
|
-
export declare const Times:
|
|
238
|
-
readonly access: Schema.Union<readonly [Schema.Struct<{
|
|
239
|
-
readonly kind: Schema.Literal<"now">;
|
|
240
|
-
}>, Schema.Struct<{
|
|
241
|
-
readonly kind: Schema.Literal<"omit">;
|
|
242
|
-
}>, Schema.Struct<{
|
|
243
|
-
readonly kind: Schema.Literal<"value">;
|
|
244
|
-
readonly nanoseconds: Schema.BigInt;
|
|
245
|
-
}>]>;
|
|
246
|
-
readonly modification: Schema.Union<readonly [Schema.Struct<{
|
|
247
|
-
readonly kind: Schema.Literal<"now">;
|
|
248
|
-
}>, Schema.Struct<{
|
|
249
|
-
readonly kind: Schema.Literal<"omit">;
|
|
250
|
-
}>, Schema.Struct<{
|
|
251
|
-
readonly kind: Schema.Literal<"value">;
|
|
252
|
-
readonly nanoseconds: Schema.BigInt;
|
|
253
|
-
}>]>;
|
|
254
|
-
}>;
|
|
431
|
+
export declare const Times: typeof VfsModel.Times;
|
|
255
432
|
/**
|
|
256
433
|
* Access and modification time updates for `utimes` operations.
|
|
257
434
|
*
|
|
@@ -262,14 +439,40 @@ export type Times = typeof Times.Type;
|
|
|
262
439
|
/**
|
|
263
440
|
* A scoped directory capability that can be used for metadata and relative lookup.
|
|
264
441
|
*
|
|
442
|
+
* @example
|
|
443
|
+
* ```ts
|
|
444
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
445
|
+
* import { Effect } from "effect"
|
|
446
|
+
*
|
|
447
|
+
* // The handle anchors relative paths to the directory itself, so a rename in
|
|
448
|
+
* // between does not send the write somewhere else.
|
|
449
|
+
* const program = Effect.gen(function*() {
|
|
450
|
+
* const caller = yield* (yield* Vfs.make()).caller()
|
|
451
|
+
*
|
|
452
|
+
* yield* caller.mkdir("/project")
|
|
453
|
+
*
|
|
454
|
+
* const directory = yield* caller.openDirectory("/project")
|
|
455
|
+
*
|
|
456
|
+
* yield* caller.rename("/project", "/renamed")
|
|
457
|
+
* yield* caller.writeFile("notes.txt", new TextEncoder().encode("kept"), {
|
|
458
|
+
* access: "write",
|
|
459
|
+
* create: "exclusive",
|
|
460
|
+
* relativeTo: directory
|
|
461
|
+
* })
|
|
462
|
+
*
|
|
463
|
+
* return yield* caller.readDirectory("/renamed")
|
|
464
|
+
* }).pipe(Effect.scoped)
|
|
465
|
+
*
|
|
466
|
+
* Effect.runPromise(program).then(console.log)
|
|
467
|
+
* // [ 'notes.txt' ]
|
|
468
|
+
* ```
|
|
469
|
+
*
|
|
265
470
|
* @category models
|
|
266
471
|
* @since 0.1.0
|
|
267
472
|
*/
|
|
268
473
|
export interface DirectoryHandle {
|
|
269
474
|
readonly [DirectoryHandleId]: true;
|
|
270
|
-
/** Reads metadata for the directory while the handle remains open. */
|
|
271
475
|
readonly stat: Effect.Effect<Metadata, FsError>;
|
|
272
|
-
/** Closes the handle. A repeated explicit close fails; scope cleanup remains safe. */
|
|
273
476
|
readonly close: Effect.Effect<void, FsError>;
|
|
274
477
|
}
|
|
275
478
|
/**
|
|
@@ -278,7 +481,7 @@ export interface DirectoryHandle {
|
|
|
278
481
|
* @category schemas
|
|
279
482
|
* @since 0.1.0
|
|
280
483
|
*/
|
|
281
|
-
export declare const SeekMode:
|
|
484
|
+
export declare const SeekMode: typeof VfsModel.SeekMode;
|
|
282
485
|
/**
|
|
283
486
|
* The origin used by a file handle seek operation.
|
|
284
487
|
*
|
|
@@ -289,23 +492,34 @@ export type SeekMode = typeof SeekMode.Type;
|
|
|
289
492
|
/**
|
|
290
493
|
* Schema for file access, creation, append, truncate, and symlink behavior.
|
|
291
494
|
*
|
|
495
|
+
* @example
|
|
496
|
+
* ```ts
|
|
497
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
498
|
+
* import { Effect } from "effect"
|
|
499
|
+
*
|
|
500
|
+
* // `access` is required; `create` decides whether a missing file is an error.
|
|
501
|
+
* const program = Effect.gen(function*() {
|
|
502
|
+
* const caller = yield* (yield* Vfs.make()).caller()
|
|
503
|
+
*
|
|
504
|
+
* const handle = yield* caller.open("/log.txt", {
|
|
505
|
+
* access: "readWrite",
|
|
506
|
+
* create: "ifMissing",
|
|
507
|
+
* append: true
|
|
508
|
+
* })
|
|
509
|
+
*
|
|
510
|
+
* yield* handle.write(new Uint8Array([1, 2]))
|
|
511
|
+
*
|
|
512
|
+
* return (yield* handle.stat).size
|
|
513
|
+
* }).pipe(Effect.scoped)
|
|
514
|
+
*
|
|
515
|
+
* Effect.runPromise(program).then(console.log)
|
|
516
|
+
* // 2n
|
|
517
|
+
* ```
|
|
518
|
+
*
|
|
292
519
|
* @category schemas
|
|
293
520
|
* @since 0.1.0
|
|
294
521
|
*/
|
|
295
|
-
export declare const OpenSettings:
|
|
296
|
-
/** Permitted operations on the returned handle. */
|
|
297
|
-
readonly access: Schema.Literals<readonly ["read", "write", "readWrite"]>;
|
|
298
|
-
/** Creation policy. Defaults to `"never"`. */
|
|
299
|
-
readonly create: Schema.optionalKey<Schema.Literals<readonly ["never", "ifMissing", "exclusive"]>>;
|
|
300
|
-
/** Requested mode for a new file, before applying the caller's umask. */
|
|
301
|
-
readonly mode: Schema.optionalKey<Schema.Finite>;
|
|
302
|
-
/** Write at the current end of file regardless of the handle cursor. */
|
|
303
|
-
readonly append: Schema.optionalKey<Schema.Boolean>;
|
|
304
|
-
/** Truncate an existing regular file to zero bytes during open. */
|
|
305
|
-
readonly truncate: Schema.optionalKey<Schema.Boolean>;
|
|
306
|
-
/** Follow the final symbolic link. Defaults to `true`. */
|
|
307
|
-
readonly followFinalSymlink: Schema.optionalKey<Schema.Boolean>;
|
|
308
|
-
}>;
|
|
522
|
+
export declare const OpenSettings: typeof VfsModel.OpenSettings;
|
|
309
523
|
/**
|
|
310
524
|
* Options for acquiring a scoped file handle.
|
|
311
525
|
*
|
|
@@ -313,34 +527,75 @@ export declare const OpenSettings: Schema.Struct<{
|
|
|
313
527
|
* @since 0.1.0
|
|
314
528
|
*/
|
|
315
529
|
export type OpenOptions = typeof OpenSettings.Type & RelativeOptions;
|
|
316
|
-
declare const WriteFileSettings: Schema.Struct<{
|
|
317
|
-
/** Permitted operations on the returned handle. */
|
|
318
|
-
readonly access: Schema.Literals<readonly ["read", "write", "readWrite"]>;
|
|
319
|
-
/** Creation policy. Defaults to `"never"`. */
|
|
320
|
-
readonly create: Schema.optionalKey<Schema.Literals<readonly ["never", "ifMissing", "exclusive"]>>;
|
|
321
|
-
/** Requested mode for a new file, before applying the caller's umask. */
|
|
322
|
-
readonly mode: Schema.optionalKey<Schema.Finite>;
|
|
323
|
-
/** Write at the current end of file regardless of the handle cursor. */
|
|
324
|
-
readonly append: Schema.optionalKey<Schema.Boolean>;
|
|
325
|
-
/** Truncate an existing regular file to zero bytes during open. */
|
|
326
|
-
readonly truncate: Schema.optionalKey<Schema.Boolean>;
|
|
327
|
-
/** Follow the final symbolic link. Defaults to `true`. */
|
|
328
|
-
readonly followFinalSymlink: Schema.optionalKey<Schema.Boolean>;
|
|
329
|
-
/** Replace the final symbolic link itself instead of its target. */
|
|
330
|
-
readonly replaceFinalSymlink: Schema.optionalKey<Schema.Boolean>;
|
|
331
|
-
/** Mode to apply after replacing an existing file. */
|
|
332
|
-
readonly finalMode: Schema.optionalKey<Schema.Finite>;
|
|
333
|
-
}>;
|
|
334
530
|
/**
|
|
335
531
|
* Options for an atomic whole-file write, including replacement and final mode controls.
|
|
336
532
|
*
|
|
533
|
+
* @example
|
|
534
|
+
* ```ts
|
|
535
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
536
|
+
* import { Effect } from "effect"
|
|
537
|
+
*
|
|
538
|
+
* const program = Effect.gen(function*() {
|
|
539
|
+
* const caller = yield* (yield* Vfs.make()).caller()
|
|
540
|
+
* const bytes = new TextEncoder().encode("v2")
|
|
541
|
+
*
|
|
542
|
+
* // `access` is required. `create: "exclusive"` refuses to replace an entry,
|
|
543
|
+
* // while `truncate` overwrites one that already exists.
|
|
544
|
+
* yield* caller.writeFile("/app.conf", new TextEncoder().encode("v1"), {
|
|
545
|
+
* access: "write",
|
|
546
|
+
* create: "exclusive",
|
|
547
|
+
* mode: 0o640
|
|
548
|
+
* })
|
|
549
|
+
*
|
|
550
|
+
* const refused = yield* caller.writeFile("/app.conf", bytes, {
|
|
551
|
+
* access: "write",
|
|
552
|
+
* create: "exclusive"
|
|
553
|
+
* }).pipe(
|
|
554
|
+
* Effect.as("replaced"),
|
|
555
|
+
* Effect.catchTag("FsError", (error) => Effect.succeed(error.code))
|
|
556
|
+
* )
|
|
557
|
+
*
|
|
558
|
+
* yield* caller.writeFile("/app.conf", bytes, { access: "write", truncate: true })
|
|
559
|
+
*
|
|
560
|
+
* return [refused, new TextDecoder().decode(yield* caller.readFile("/app.conf"))]
|
|
561
|
+
* })
|
|
562
|
+
*
|
|
563
|
+
* Effect.runPromise(program).then(console.log)
|
|
564
|
+
* // [ 'AlreadyExists', 'v2' ]
|
|
565
|
+
* ```
|
|
566
|
+
*
|
|
337
567
|
* @category models
|
|
338
568
|
* @since 0.1.0
|
|
339
569
|
*/
|
|
340
|
-
export type WriteFileOptions =
|
|
570
|
+
export type WriteFileOptions = VfsModel.WriteFileOptions;
|
|
341
571
|
/**
|
|
342
572
|
* A scoped regular-file capability with an independent bigint cursor.
|
|
343
573
|
*
|
|
574
|
+
* @example
|
|
575
|
+
* ```ts
|
|
576
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
577
|
+
* import { Effect } from "effect"
|
|
578
|
+
*
|
|
579
|
+
* const program = Effect.gen(function*() {
|
|
580
|
+
* const caller = yield* (yield* Vfs.make()).caller()
|
|
581
|
+
*
|
|
582
|
+
* const handle = yield* caller.open("/data.bin", {
|
|
583
|
+
* access: "readWrite",
|
|
584
|
+
* create: "exclusive"
|
|
585
|
+
* })
|
|
586
|
+
*
|
|
587
|
+
* yield* handle.write(new Uint8Array([1, 2, 3, 4]))
|
|
588
|
+
*
|
|
589
|
+
* // `pread` reads at an offset and leaves the cursor where writing left it.
|
|
590
|
+
* const slice = yield* handle.pread(2, 1n)
|
|
591
|
+
*
|
|
592
|
+
* return [slice, yield* handle.seek(0n, "current")]
|
|
593
|
+
* }).pipe(Effect.scoped)
|
|
594
|
+
*
|
|
595
|
+
* Effect.runPromise(program).then(console.log)
|
|
596
|
+
* // [ Uint8Array(2) [ 2, 3 ], 4n ]
|
|
597
|
+
* ```
|
|
598
|
+
*
|
|
344
599
|
* @category models
|
|
345
600
|
* @since 0.1.0
|
|
346
601
|
*/
|
|
@@ -368,18 +623,108 @@ export interface FileHandle {
|
|
|
368
623
|
/**
|
|
369
624
|
* A filesystem caller with its own identity, creation mask, and current directory.
|
|
370
625
|
*
|
|
626
|
+
* @example
|
|
627
|
+
* ```ts
|
|
628
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
629
|
+
* import { Effect } from "effect"
|
|
630
|
+
*
|
|
631
|
+
* // Whole-file reads and writes need no scope. `writeFile` always takes an
|
|
632
|
+
* // options bag; `access` is required and `create` decides what a missing file means.
|
|
633
|
+
* const program = Effect.gen(function*() {
|
|
634
|
+
* const volume = yield* Vfs.make()
|
|
635
|
+
* const caller = yield* volume.caller()
|
|
636
|
+
*
|
|
637
|
+
* yield* caller.mkdir("/work")
|
|
638
|
+
* yield* caller.writeFile("/work/notes.txt", new TextEncoder().encode("hello"), {
|
|
639
|
+
* access: "write",
|
|
640
|
+
* create: "exclusive"
|
|
641
|
+
* })
|
|
642
|
+
*
|
|
643
|
+
* return yield* caller.readDirectory("/work")
|
|
644
|
+
* })
|
|
645
|
+
*
|
|
646
|
+
* Effect.runPromise(program).then(console.log)
|
|
647
|
+
* // [ 'notes.txt' ]
|
|
648
|
+
* ```
|
|
649
|
+
*
|
|
650
|
+
* @example
|
|
651
|
+
* ```ts
|
|
652
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
653
|
+
* import { Effect } from "effect"
|
|
654
|
+
*
|
|
655
|
+
* // Reach for a handle when a whole-file write will not do. A handle carries a
|
|
656
|
+
* // cursor and lives until its scope closes.
|
|
657
|
+
* const program = Effect.gen(function*() {
|
|
658
|
+
* const caller = yield* (yield* Vfs.make()).caller()
|
|
659
|
+
*
|
|
660
|
+
* const handle = yield* caller.open("/log.txt", {
|
|
661
|
+
* access: "readWrite",
|
|
662
|
+
* create: "exclusive"
|
|
663
|
+
* })
|
|
664
|
+
*
|
|
665
|
+
* yield* handle.write(new TextEncoder().encode("first "))
|
|
666
|
+
* yield* handle.write(new TextEncoder().encode("second"))
|
|
667
|
+
* yield* handle.seek(0n, "start")
|
|
668
|
+
*
|
|
669
|
+
* return new TextDecoder().decode(yield* handle.read(64))
|
|
670
|
+
* }).pipe(Effect.scoped)
|
|
671
|
+
*
|
|
672
|
+
* Effect.runPromise(program).then(console.log)
|
|
673
|
+
* // first second
|
|
674
|
+
* ```
|
|
675
|
+
*
|
|
676
|
+
* @example
|
|
677
|
+
* ```ts
|
|
678
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
679
|
+
* import { Effect } from "effect"
|
|
680
|
+
*
|
|
681
|
+
* // A derived caller holds the directory's identity, not its path, so renaming
|
|
682
|
+
* // the directory does not redirect later relative writes.
|
|
683
|
+
* const program = Effect.gen(function*() {
|
|
684
|
+
* const caller = yield* (yield* Vfs.make()).caller()
|
|
685
|
+
*
|
|
686
|
+
* yield* caller.mkdir("/build")
|
|
687
|
+
*
|
|
688
|
+
* const work = yield* caller.withDirectory("/build")
|
|
689
|
+
*
|
|
690
|
+
* yield* caller.rename("/build", "/dist")
|
|
691
|
+
* yield* work.writeFile("out.txt", new TextEncoder().encode("done"), {
|
|
692
|
+
* access: "write",
|
|
693
|
+
* create: "exclusive"
|
|
694
|
+
* })
|
|
695
|
+
*
|
|
696
|
+
* return yield* caller.readDirectory("/dist")
|
|
697
|
+
* }).pipe(Effect.scoped)
|
|
698
|
+
*
|
|
699
|
+
* Effect.runPromise(program).then(console.log)
|
|
700
|
+
* // [ 'out.txt' ]
|
|
701
|
+
* ```
|
|
702
|
+
*
|
|
703
|
+
* @see The filesystem model at `/concepts/filesystem-model`.
|
|
371
704
|
* @category models
|
|
372
705
|
* @since 0.1.0
|
|
373
706
|
*/
|
|
374
707
|
export interface Caller {
|
|
375
708
|
readonly [CallerId]: true;
|
|
709
|
+
/** Returns the stable reference for this volume's root directory. */
|
|
710
|
+
readonly rootReference: Effect.Effect<ObjectReference, FsError>;
|
|
711
|
+
/** Looks up one byte-preserving child name from a referenced directory. */
|
|
712
|
+
readonly lookupReference: (directory: ObjectReference, name: Uint8Array) => Effect.Effect<ObjectReference, FsError>;
|
|
713
|
+
/** Returns a referenced directory's current parent. The root is its own parent. */
|
|
714
|
+
readonly parentReference: (directory: ObjectReference) => Effect.Effect<ObjectReference, FsError>;
|
|
715
|
+
/** Reads metadata and its matching live revision. Requires no permission on the object, as `stat` does not. */
|
|
716
|
+
readonly observeMetadata: (reference: ObjectReference) => Effect.Effect<ObjectObservation<Metadata>, FsError>;
|
|
717
|
+
/** Reads owned directory entries and their matching directory revision. */
|
|
718
|
+
readonly observeDirectory: (reference: ObjectReference) => Effect.Effect<ObjectObservation<ReadonlyArray<DirectoryEntry>>, FsError>;
|
|
719
|
+
/** Reads an owned symbolic-link target through a stable reference. Requires no permission on the link. */
|
|
720
|
+
readonly readLinkReference: (reference: ObjectReference) => Effect.Effect<Uint8Array, FsError>;
|
|
721
|
+
/** Opens a referenced regular file for reading. */
|
|
722
|
+
readonly openReference: (reference: ObjectReference) => Effect.Effect<FileHandle, FsError, Scope.Scope>;
|
|
376
723
|
/** Reads metadata, following the final symbolic link by default. */
|
|
377
724
|
readonly stat: (path: PathInput, options?: RelativeOptions) => Effect.Effect<Metadata, FsError>;
|
|
378
725
|
/** Atomically moves an entry within this volume without replacing a non-empty directory. */
|
|
379
726
|
readonly rename: (source: PathInput, destination: PathInput, options?: {
|
|
380
|
-
/** Base directory for a relative source path. */
|
|
381
727
|
readonly sourceRelativeTo?: DirectoryHandle;
|
|
382
|
-
/** Base directory for a relative destination path. */
|
|
383
728
|
readonly destinationRelativeTo?: DirectoryHandle;
|
|
384
729
|
}) => Effect.Effect<void, FsError>;
|
|
385
730
|
/** Reads and returns an owned copy of a regular file's complete contents. */
|
|
@@ -406,24 +751,21 @@ export interface Caller {
|
|
|
406
751
|
readonly lstat: (path: PathInput, options?: RelativeOptions) => Effect.Effect<Metadata, FsError>;
|
|
407
752
|
/** Creates a hard link to an existing non-directory entry. */
|
|
408
753
|
readonly link: (source: PathInput, destination: PathInput, options?: {
|
|
409
|
-
/** Base directory for a relative source path. */
|
|
410
754
|
readonly sourceRelativeTo?: DirectoryHandle;
|
|
411
|
-
/** Base directory for a relative destination path. */
|
|
412
755
|
readonly destinationRelativeTo?: DirectoryHandle;
|
|
413
|
-
/** Link to the final symbolic link's target instead of the link itself. */
|
|
414
756
|
readonly followSourceSymlink?: boolean;
|
|
415
757
|
}) => Effect.Effect<void, FsError>;
|
|
416
758
|
/** Creates a symbolic link. The target bytes are stored without resolving them. */
|
|
417
759
|
readonly symlink: (target: PathInput, path: PathInput, options?: RelativeOptions) => Effect.Effect<void, FsError>;
|
|
418
|
-
/** Reads a symbolic-link target as UTF-8, failing
|
|
760
|
+
/** Reads a symbolic-link target as UTF-8, failing `UnrepresentableName` for other bytes. See `readLinkBytes`. */
|
|
419
761
|
readonly readLink: (path: PathInput, options?: RelativeOptions) => Effect.Effect<string, FsError>;
|
|
420
762
|
/** Reads a symbolic-link target as owned bytes. */
|
|
421
763
|
readonly readLinkBytes: (path: PathInput, options?: RelativeOptions) => Effect.Effect<Uint8Array, FsError>;
|
|
422
|
-
/** Reads directory names as UTF-8, failing
|
|
764
|
+
/** Reads directory names as UTF-8, failing `UnrepresentableName` for other bytes. See `readDirectoryBytes`. */
|
|
423
765
|
readonly readDirectory: (path: PathInput, options?: RelativeOptions) => Effect.Effect<ReadonlyArray<string>, FsError>;
|
|
424
766
|
/** Reads directory names as owned byte arrays. */
|
|
425
767
|
readonly readDirectoryBytes: (path: PathInput, options?: RelativeOptions) => Effect.Effect<ReadonlyArray<Uint8Array>, FsError>;
|
|
426
|
-
/** Resolves links and normalizes a path as UTF-8. */
|
|
768
|
+
/** Resolves links and normalizes a path as UTF-8, failing `UnrepresentableName` for other bytes. See `realPathBytes`. */
|
|
427
769
|
readonly realPath: (path: PathInput, options?: RelativeOptions) => Effect.Effect<string, FsError>;
|
|
428
770
|
/** Resolves links and normalizes a path without requiring UTF-8 names. */
|
|
429
771
|
readonly realPathBytes: (path: PathInput, options?: RelativeOptions) => Effect.Effect<BytePath, FsError>;
|
|
@@ -435,7 +777,6 @@ export interface Caller {
|
|
|
435
777
|
readonly rmdir: (path: PathInput, options?: RelativeOptions) => Effect.Effect<void, FsError>;
|
|
436
778
|
/** Creates one directory. Parent directories must already exist. */
|
|
437
779
|
readonly mkdir: (path: PathInput, options?: RelativeOptions & {
|
|
438
|
-
/** Requested mode before applying the caller's umask. Defaults to `0o777`. */
|
|
439
780
|
readonly mode?: number;
|
|
440
781
|
}) => Effect.Effect<void, FsError>;
|
|
441
782
|
/** Creates a scoped caller whose current directory is the resolved directory identity. */
|
|
@@ -446,6 +787,37 @@ export interface Caller {
|
|
|
446
787
|
/**
|
|
447
788
|
* A committed namespace or content change emitted by a volume watch stream.
|
|
448
789
|
*
|
|
790
|
+
* @example
|
|
791
|
+
* ```ts
|
|
792
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
793
|
+
* import { Effect, Fiber, Stream } from "effect"
|
|
794
|
+
*
|
|
795
|
+
* const program = Effect.gen(function*() {
|
|
796
|
+
* const volume = yield* Vfs.make()
|
|
797
|
+
* const caller = yield* volume.caller()
|
|
798
|
+
*
|
|
799
|
+
* const watcher = yield* volume.watch.pipe(
|
|
800
|
+
* Effect.flatMap((stream) => Stream.runCollect(Stream.take(stream, 2))),
|
|
801
|
+
* Effect.forkChild({ startImmediately: true })
|
|
802
|
+
* )
|
|
803
|
+
*
|
|
804
|
+
* yield* caller.mkdir("/logs")
|
|
805
|
+
* yield* caller.rmdir("/logs")
|
|
806
|
+
*
|
|
807
|
+
* // Each event pairs the kind of change with the absolute path it happened to.
|
|
808
|
+
* const events = yield* Fiber.join(watcher)
|
|
809
|
+
*
|
|
810
|
+
* return yield* Effect.forEach(events, (change) =>
|
|
811
|
+
* Effect.map(
|
|
812
|
+
* Vfs.pathToBytes(change.path),
|
|
813
|
+
* (bytes) => `${change._tag} ${new TextDecoder().decode(bytes)}`
|
|
814
|
+
* ))
|
|
815
|
+
* }).pipe(Effect.scoped)
|
|
816
|
+
*
|
|
817
|
+
* Effect.runPromise(program).then(console.log)
|
|
818
|
+
* // [ 'Create /logs', 'Remove /logs' ]
|
|
819
|
+
* ```
|
|
820
|
+
*
|
|
449
821
|
* @category models
|
|
450
822
|
* @since 0.1.0
|
|
451
823
|
*/
|
|
@@ -461,7 +833,7 @@ export interface Change {
|
|
|
461
833
|
* @category schemas
|
|
462
834
|
* @since 0.1.0
|
|
463
835
|
*/
|
|
464
|
-
export declare const OverlayNodeKind:
|
|
836
|
+
export declare const OverlayNodeKind: typeof VfsModel.OverlayNodeKind;
|
|
465
837
|
/**
|
|
466
838
|
* A filesystem entry kind reported by an overlay summary.
|
|
467
839
|
*
|
|
@@ -475,7 +847,7 @@ export type OverlayNodeKind = typeof OverlayNodeKind.Type;
|
|
|
475
847
|
* @category schemas
|
|
476
848
|
* @since 0.1.0
|
|
477
849
|
*/
|
|
478
|
-
export declare const OverlayDifference:
|
|
850
|
+
export declare const OverlayDifference: typeof VfsModel.OverlayDifference;
|
|
479
851
|
/**
|
|
480
852
|
* A content, ownership, permission, or timestamp field that differs from the overlay base.
|
|
481
853
|
*
|
|
@@ -494,27 +866,7 @@ export type OverlayDifference = typeof OverlayDifference.Type;
|
|
|
494
866
|
* @category schemas
|
|
495
867
|
* @since 0.1.0
|
|
496
868
|
*/
|
|
497
|
-
export declare const OverlayChange:
|
|
498
|
-
readonly path: Schema.declare<BytePath, BytePath>;
|
|
499
|
-
readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]>;
|
|
500
|
-
}>, Schema.TaggedStruct<"Removed", {
|
|
501
|
-
readonly path: Schema.declare<BytePath, BytePath>;
|
|
502
|
-
readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]>;
|
|
503
|
-
}>, Schema.TaggedStruct<"Replaced", {
|
|
504
|
-
readonly path: Schema.declare<BytePath, BytePath>;
|
|
505
|
-
readonly beforeKind: Schema.Literals<readonly ["directory", "file", "symlink"]>;
|
|
506
|
-
readonly afterKind: Schema.Literals<readonly ["directory", "file", "symlink"]>;
|
|
507
|
-
readonly differences: Schema.$Array<Schema.Literals<readonly ["content", "mode", "uid", "gid", "atimeNs", "mtimeNs", "ctimeNs", "birthtimeNs"]>>;
|
|
508
|
-
}>, Schema.TaggedStruct<"Renamed", {
|
|
509
|
-
readonly from: Schema.declare<BytePath, BytePath>;
|
|
510
|
-
readonly to: Schema.declare<BytePath, BytePath>;
|
|
511
|
-
readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]>;
|
|
512
|
-
readonly differences: Schema.$Array<Schema.Literals<readonly ["content", "mode", "uid", "gid", "atimeNs", "mtimeNs", "ctimeNs", "birthtimeNs"]>>;
|
|
513
|
-
}>, Schema.TaggedStruct<"Updated", {
|
|
514
|
-
readonly path: Schema.declare<BytePath, BytePath>;
|
|
515
|
-
readonly kind: Schema.Literals<readonly ["directory", "file", "symlink"]>;
|
|
516
|
-
readonly differences: Schema.$Array<Schema.Literals<readonly ["content", "mode", "uid", "gid", "atimeNs", "mtimeNs", "ctimeNs", "birthtimeNs"]>>;
|
|
517
|
-
}>]>;
|
|
869
|
+
export declare const OverlayChange: typeof VfsModel.OverlayChange;
|
|
518
870
|
/**
|
|
519
871
|
* A path-oriented final-state difference from an overlay's immutable base.
|
|
520
872
|
*
|
|
@@ -525,13 +877,32 @@ export type OverlayChange = typeof OverlayChange.Type;
|
|
|
525
877
|
/**
|
|
526
878
|
* Schema for overlay summary filtering.
|
|
527
879
|
*
|
|
880
|
+
* @example
|
|
881
|
+
* ```ts
|
|
882
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
883
|
+
* import { Effect } from "effect"
|
|
884
|
+
*
|
|
885
|
+
* const program = Effect.gen(function*() {
|
|
886
|
+
* const base = yield* Vfs.make()
|
|
887
|
+
* const workspace = yield* Vfs.makeOverlay(yield* base.snapshot)
|
|
888
|
+
*
|
|
889
|
+
* yield* (yield* workspace.caller()).mkdir("/out")
|
|
890
|
+
*
|
|
891
|
+
* // Timestamps are excluded by default, since they change on every write.
|
|
892
|
+
* const plain = yield* workspace.changes()
|
|
893
|
+
* const timed = yield* workspace.changes({ includeTimestamps: true })
|
|
894
|
+
*
|
|
895
|
+
* return [plain.length, timed.length]
|
|
896
|
+
* })
|
|
897
|
+
*
|
|
898
|
+
* Effect.runPromise(program).then(console.log)
|
|
899
|
+
* // [ 1, 2 ]
|
|
900
|
+
* ```
|
|
901
|
+
*
|
|
528
902
|
* @category schemas
|
|
529
903
|
* @since 0.1.0
|
|
530
904
|
*/
|
|
531
|
-
export declare const OverlayChangesOptions:
|
|
532
|
-
/** Include access, modification, change, and birth-time differences. Defaults to `false`. */
|
|
533
|
-
readonly includeTimestamps: Schema.optionalKey<Schema.Boolean>;
|
|
534
|
-
}>;
|
|
905
|
+
export declare const OverlayChangesOptions: typeof VfsModel.OverlayChangesOptions;
|
|
535
906
|
/**
|
|
536
907
|
* Filtering options for an overlay final-difference summary.
|
|
537
908
|
*
|
|
@@ -542,6 +913,36 @@ export type OverlayChangesOptions = typeof OverlayChangesOptions.Type;
|
|
|
542
913
|
/**
|
|
543
914
|
* A complete snapshot and final-difference summary captured from one committed state.
|
|
544
915
|
*
|
|
916
|
+
* @example
|
|
917
|
+
* ```ts
|
|
918
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
919
|
+
* import { Effect } from "effect"
|
|
920
|
+
*
|
|
921
|
+
* const program = Effect.gen(function*() {
|
|
922
|
+
* const base = yield* Vfs.make()
|
|
923
|
+
* const workspace = yield* Vfs.makeOverlay(yield* base.snapshot)
|
|
924
|
+
*
|
|
925
|
+
* yield* (yield* workspace.caller()).writeFile("/out.txt", new TextEncoder().encode("built"), {
|
|
926
|
+
* access: "write",
|
|
927
|
+
* create: "exclusive"
|
|
928
|
+
* })
|
|
929
|
+
*
|
|
930
|
+
* // One committed state: the summary describes exactly the snapshot beside it,
|
|
931
|
+
* // so restoring from it cannot disagree with the change list.
|
|
932
|
+
* const captured = yield* workspace.capture()
|
|
933
|
+
* const restored = yield* Vfs.fromSnapshot(captured.snapshot)
|
|
934
|
+
* const reader = yield* restored.caller()
|
|
935
|
+
*
|
|
936
|
+
* return [
|
|
937
|
+
* captured.changes.map((change) => change._tag),
|
|
938
|
+
* new TextDecoder().decode(yield* reader.readFile("/out.txt"))
|
|
939
|
+
* ]
|
|
940
|
+
* })
|
|
941
|
+
*
|
|
942
|
+
* Effect.runPromise(program).then(console.log)
|
|
943
|
+
* // [ [ 'Added' ], 'built' ]
|
|
944
|
+
* ```
|
|
945
|
+
*
|
|
545
946
|
* @category models
|
|
546
947
|
* @since 0.1.0
|
|
547
948
|
*/
|
|
@@ -554,6 +955,32 @@ export interface OverlayCapture {
|
|
|
554
955
|
/**
|
|
555
956
|
* An isolated virtual filesystem namespace that creates callers, snapshots, and watch streams.
|
|
556
957
|
*
|
|
958
|
+
* @example
|
|
959
|
+
* ```ts
|
|
960
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
961
|
+
* import { Effect, Fiber, Option, Stream } from "effect"
|
|
962
|
+
*
|
|
963
|
+
* const program = Effect.gen(function*() {
|
|
964
|
+
* const volume = yield* Vfs.make()
|
|
965
|
+
* const caller = yield* volume.caller()
|
|
966
|
+
*
|
|
967
|
+
* // `watch` reports future changes only; nothing is replayed.
|
|
968
|
+
* const watcher = yield* volume.watch.pipe(
|
|
969
|
+
* Effect.flatMap(Stream.runHead),
|
|
970
|
+
* Effect.forkChild({ startImmediately: true })
|
|
971
|
+
* )
|
|
972
|
+
*
|
|
973
|
+
* yield* caller.mkdir("/logs")
|
|
974
|
+
*
|
|
975
|
+
* const change = yield* Fiber.join(watcher)
|
|
976
|
+
*
|
|
977
|
+
* return Option.getOrElse(Option.map(change, (event) => event._tag), () => "none")
|
|
978
|
+
* }).pipe(Effect.scoped)
|
|
979
|
+
*
|
|
980
|
+
* Effect.runPromise(program).then(console.log)
|
|
981
|
+
* // Create
|
|
982
|
+
* ```
|
|
983
|
+
*
|
|
557
984
|
* @category models
|
|
558
985
|
* @since 0.1.0
|
|
559
986
|
*/
|
|
@@ -569,6 +996,30 @@ export interface Volume {
|
|
|
569
996
|
/**
|
|
570
997
|
* An ordinary volume with final-state inspection relative to one immutable snapshot base.
|
|
571
998
|
*
|
|
999
|
+
* @example
|
|
1000
|
+
* ```ts
|
|
1001
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
1002
|
+
* import { Effect } from "effect"
|
|
1003
|
+
*
|
|
1004
|
+
* const program = Effect.gen(function*() {
|
|
1005
|
+
* const base = yield* Vfs.make()
|
|
1006
|
+
* const workspace = yield* Vfs.makeOverlay(yield* base.snapshot)
|
|
1007
|
+
*
|
|
1008
|
+
* yield* (yield* workspace.caller()).writeFile("/added.txt", new Uint8Array([1]), {
|
|
1009
|
+
* access: "write",
|
|
1010
|
+
* create: "exclusive"
|
|
1011
|
+
* })
|
|
1012
|
+
*
|
|
1013
|
+
* // `capture` pairs one committed snapshot with the summary describing it.
|
|
1014
|
+
* const captured = yield* workspace.capture()
|
|
1015
|
+
*
|
|
1016
|
+
* return captured.changes.map((change) => change._tag)
|
|
1017
|
+
* })
|
|
1018
|
+
*
|
|
1019
|
+
* Effect.runPromise(program).then(console.log)
|
|
1020
|
+
* // [ 'Added' ]
|
|
1021
|
+
* ```
|
|
1022
|
+
*
|
|
572
1023
|
* @category models
|
|
573
1024
|
* @since 0.1.0
|
|
574
1025
|
*/
|
|
@@ -578,18 +1029,73 @@ export interface OverlayVolume extends Volume {
|
|
|
578
1029
|
/** Captures one committed state when this reusable effect executes. */
|
|
579
1030
|
readonly capture: (options?: OverlayChangesOptions) => Effect.Effect<OverlayCapture, ConfigurationError | ImageError>;
|
|
580
1031
|
}
|
|
581
|
-
declare const CurrentFileSystem_base: Context.ServiceClass<CurrentFileSystem, "@effect-vfs/core/CurrentFileSystem", Caller>;
|
|
582
1032
|
/**
|
|
583
1033
|
* Optional Effect service for providing an existing filesystem caller.
|
|
584
1034
|
*
|
|
1035
|
+
* @example
|
|
1036
|
+
* ```ts
|
|
1037
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
1038
|
+
* import { Effect } from "effect"
|
|
1039
|
+
*
|
|
1040
|
+
* // Code depending on the service does not choose the volume it runs against.
|
|
1041
|
+
* const readVersion = Effect.gen(function*() {
|
|
1042
|
+
* const caller = yield* Vfs.CurrentFileSystem
|
|
1043
|
+
*
|
|
1044
|
+
* return yield* caller.readFile("/version")
|
|
1045
|
+
* })
|
|
1046
|
+
*
|
|
1047
|
+
* const program = Effect.gen(function*() {
|
|
1048
|
+
* const volume = yield* Vfs.make()
|
|
1049
|
+
* const caller = yield* volume.caller()
|
|
1050
|
+
*
|
|
1051
|
+
* yield* caller.writeFile("/version", new Uint8Array([49]), {
|
|
1052
|
+
* access: "write",
|
|
1053
|
+
* create: "exclusive"
|
|
1054
|
+
* })
|
|
1055
|
+
*
|
|
1056
|
+
* return yield* readVersion.pipe(Effect.provideService(Vfs.CurrentFileSystem, caller))
|
|
1057
|
+
* })
|
|
1058
|
+
*
|
|
1059
|
+
* Effect.runPromise(program).then(console.log)
|
|
1060
|
+
* // Uint8Array(1) [ 49 ]
|
|
1061
|
+
* ```
|
|
1062
|
+
*
|
|
1063
|
+
* @see The testing guide at `/guides/testing-with-an-isolated-filesystem`.
|
|
585
1064
|
* @category services
|
|
586
1065
|
* @since 0.1.0
|
|
587
1066
|
*/
|
|
588
|
-
export declare
|
|
589
|
-
|
|
1067
|
+
export declare const CurrentFileSystem: typeof VfsModel.CurrentFileSystem;
|
|
1068
|
+
/** @internal */
|
|
1069
|
+
export type CurrentFileSystem = VfsModel.CurrentFileSystem;
|
|
590
1070
|
/**
|
|
591
1071
|
* Encodes a snapshot as owned UTF-8 JSON bytes using the version 1 snapshot format.
|
|
592
1072
|
*
|
|
1073
|
+
* @example
|
|
1074
|
+
* ```ts
|
|
1075
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
1076
|
+
* import { Effect } from "effect"
|
|
1077
|
+
*
|
|
1078
|
+
* // Bytes are portable: store or transmit them, then decode under explicit limits.
|
|
1079
|
+
* const program = Effect.gen(function*() {
|
|
1080
|
+
* const volume = yield* Vfs.make()
|
|
1081
|
+
* const caller = yield* volume.caller()
|
|
1082
|
+
*
|
|
1083
|
+
* yield* caller.writeFile("/f", new Uint8Array([1, 2, 3]), {
|
|
1084
|
+
* access: "write",
|
|
1085
|
+
* create: "exclusive"
|
|
1086
|
+
* })
|
|
1087
|
+
*
|
|
1088
|
+
* const bytes = yield* Vfs.encodeSnapshot(yield* volume.snapshot)
|
|
1089
|
+
*
|
|
1090
|
+
* // The bytes are UTF-8 JSON carrying the version 1 snapshot format.
|
|
1091
|
+
* return JSON.parse(new TextDecoder().decode(bytes)).version
|
|
1092
|
+
* })
|
|
1093
|
+
*
|
|
1094
|
+
* Effect.runPromise(program).then(console.log)
|
|
1095
|
+
* // 1
|
|
1096
|
+
* ```
|
|
1097
|
+
*
|
|
1098
|
+
* @see {@link decodeSnapshot} for reading the bytes back under limits.
|
|
593
1099
|
* @category serialization
|
|
594
1100
|
* @since 0.1.0
|
|
595
1101
|
*/
|
|
@@ -597,45 +1103,131 @@ export declare const encodeSnapshot: (snapshot: Snapshot) => Effect.Effect<Uint8
|
|
|
597
1103
|
/**
|
|
598
1104
|
* Decodes version 1 snapshot bytes while enforcing explicit input and payload limits.
|
|
599
1105
|
*
|
|
1106
|
+
* @example
|
|
1107
|
+
* ```ts
|
|
1108
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
1109
|
+
* import { ByteSize, Effect } from "effect"
|
|
1110
|
+
*
|
|
1111
|
+
* // Limits are mandatory: decoding is the boundary where untrusted input arrives.
|
|
1112
|
+
* const program = Effect.gen(function*() {
|
|
1113
|
+
* const volume = yield* Vfs.make()
|
|
1114
|
+
* const bytes = yield* Vfs.encodeSnapshot(yield* volume.snapshot)
|
|
1115
|
+
*
|
|
1116
|
+
* return yield* Vfs.decodeSnapshot(bytes, {
|
|
1117
|
+
* maxEncodedBytes: ByteSize.bytes(1),
|
|
1118
|
+
* maxRecords: 10_000,
|
|
1119
|
+
* maxEntries: 10_000,
|
|
1120
|
+
* maxDecodedBytes: ByteSize.megabytes(16)
|
|
1121
|
+
* }).pipe(
|
|
1122
|
+
* Effect.as("accepted"),
|
|
1123
|
+
* Effect.catchTag("ImageError", (error) => Effect.succeed([error.code, error.field]))
|
|
1124
|
+
* )
|
|
1125
|
+
* })
|
|
1126
|
+
*
|
|
1127
|
+
* Effect.runPromise(program).then(console.log)
|
|
1128
|
+
* // [ 'LimitExceeded', 'encodedBytes' ]
|
|
1129
|
+
* ```
|
|
1130
|
+
*
|
|
1131
|
+
* @see {@link DecodeLimits} for what each bound protects.
|
|
600
1132
|
* @category serialization
|
|
601
1133
|
* @since 0.1.0
|
|
602
1134
|
*/
|
|
603
1135
|
export declare const decodeSnapshot: (input: Uint8Array, limits: DecodeLimits) => Effect.Effect<Snapshot, ImageError>;
|
|
604
1136
|
/**
|
|
605
1137
|
* Computes an exact portable delta between two immutable snapshots.
|
|
606
|
-
* Requires the platform-neutral `Crypto.Crypto` service for base identity.
|
|
1138
|
+
* Requires the platform-neutral `Crypto.Crypto` service for base identity. Provide
|
|
1139
|
+
* `NodeCrypto.layer`, `BunCrypto.layer`, or your own via `Crypto.make`.
|
|
607
1140
|
*
|
|
1141
|
+
* @example
|
|
1142
|
+
* ```ts
|
|
1143
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
1144
|
+
* import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
1145
|
+
* import { Effect } from "effect"
|
|
1146
|
+
*
|
|
1147
|
+
* // Two snapshots in, one opaque delta out. `inspectSnapshotDelta` reads it.
|
|
1148
|
+
* const program = Effect.gen(function*() {
|
|
1149
|
+
* const volume = yield* Vfs.make()
|
|
1150
|
+
* const caller = yield* volume.caller()
|
|
1151
|
+
* const base = yield* volume.snapshot
|
|
1152
|
+
*
|
|
1153
|
+
* yield* caller.writeFile("/added.txt", new Uint8Array([1]), {
|
|
1154
|
+
* access: "write",
|
|
1155
|
+
* create: "exclusive"
|
|
1156
|
+
* })
|
|
1157
|
+
*
|
|
1158
|
+
* const delta = yield* Vfs.diffSnapshots(base, yield* volume.snapshot)
|
|
1159
|
+
*
|
|
1160
|
+
* const changes = yield* Vfs.inspectSnapshotDelta(base, delta)
|
|
1161
|
+
*
|
|
1162
|
+
* return new TextDecoder().decode(yield* Vfs.pathToBytes(changes[0]!.path))
|
|
1163
|
+
* }).pipe(Effect.provide(NodeCrypto.layer))
|
|
1164
|
+
*
|
|
1165
|
+
* Effect.runPromise(program).then(console.log)
|
|
1166
|
+
* // /added.txt
|
|
1167
|
+
* ```
|
|
1168
|
+
*
|
|
1169
|
+
* @see {@link inspectSnapshotDelta} to read a delta, {@link applySnapshotDelta} to apply one.
|
|
608
1170
|
* @category snapshots
|
|
609
1171
|
* @since 0.1.0
|
|
610
1172
|
*/
|
|
611
1173
|
export declare const diffSnapshots: (base: Snapshot, target: Snapshot, limits?: {
|
|
612
|
-
readonly maxEncodedBytes: ByteSize.ByteSize;
|
|
613
|
-
readonly maxIdentityBytes: ByteSize.ByteSize;
|
|
1174
|
+
readonly maxEncodedBytes: import("effect/ByteSize").ByteSize;
|
|
1175
|
+
readonly maxIdentityBytes: import("effect/ByteSize").ByteSize;
|
|
614
1176
|
readonly maxDeltaRecords: number;
|
|
615
|
-
readonly maxDecodedDeltaBytes: ByteSize.ByteSize;
|
|
1177
|
+
readonly maxDecodedDeltaBytes: import("effect/ByteSize").ByteSize;
|
|
616
1178
|
readonly maxBaseRecords: number;
|
|
617
1179
|
readonly maxTargetRecords: number;
|
|
618
1180
|
readonly maxEntries: number;
|
|
619
1181
|
readonly maxOutputRecords: number;
|
|
620
|
-
readonly maxOutputBytes: ByteSize.ByteSize;
|
|
1182
|
+
readonly maxOutputBytes: import("effect/ByteSize").ByteSize;
|
|
621
1183
|
readonly maxInheritedRecords: number;
|
|
622
|
-
} | undefined) => Effect.Effect<SnapshotDeltaModel.SnapshotDelta, ConfigurationError | ImageError | import("effect/PlatformError").PlatformError, import("effect/Crypto").Crypto>;
|
|
1184
|
+
} | undefined) => Effect.Effect<SnapshotDeltaModel.SnapshotDelta, VfsModel.ConfigurationError | ImageError | import("effect/PlatformError").PlatformError, import("effect/Crypto").Crypto>;
|
|
623
1185
|
/**
|
|
624
1186
|
* Verifies an exact snapshot delta against its base and derives an owned path-oriented summary.
|
|
625
|
-
* Requires the platform-neutral `Crypto.Crypto` service for base identity.
|
|
1187
|
+
* Requires the platform-neutral `Crypto.Crypto` service for base identity. Provide
|
|
1188
|
+
* `NodeCrypto.layer`, `BunCrypto.layer`, or your own via `Crypto.make`.
|
|
1189
|
+
*
|
|
1190
|
+
* @example
|
|
1191
|
+
* ```ts
|
|
1192
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
1193
|
+
* import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
1194
|
+
* import { Effect } from "effect"
|
|
1195
|
+
*
|
|
1196
|
+
* // Inspection verifies the delta against its base before describing it.
|
|
1197
|
+
* const program = Effect.gen(function*() {
|
|
1198
|
+
* const volume = yield* Vfs.make()
|
|
1199
|
+
* const caller = yield* volume.caller()
|
|
1200
|
+
* const base = yield* volume.snapshot
|
|
1201
|
+
*
|
|
1202
|
+
* yield* caller.mkdir("/logs")
|
|
1203
|
+
*
|
|
1204
|
+
* const delta = yield* Vfs.diffSnapshots(base, yield* volume.snapshot)
|
|
1205
|
+
* const changes = yield* Vfs.inspectSnapshotDelta(base, delta)
|
|
1206
|
+
*
|
|
1207
|
+
* return changes.map((change) =>
|
|
1208
|
+
* change._tag === "Updated" ? change.afterKind : change.kind)
|
|
1209
|
+
* }).pipe(Effect.provide(NodeCrypto.layer))
|
|
1210
|
+
*
|
|
1211
|
+
* Effect.runPromise(program).then(console.log)
|
|
1212
|
+
* // [ 'directory' ]
|
|
1213
|
+
* ```
|
|
1214
|
+
*
|
|
1215
|
+
* @see {@link SnapshotChangesOptions} for including timestamp differences.
|
|
1216
|
+
* @category snapshots
|
|
1217
|
+
* @since 0.1.0
|
|
626
1218
|
*/
|
|
627
1219
|
export declare const inspectSnapshotDelta: (base: Snapshot, delta: SnapshotDeltaModel.SnapshotDelta, options?: {
|
|
628
1220
|
readonly includeTimestamps?: boolean;
|
|
629
1221
|
} | undefined, limits?: {
|
|
630
|
-
readonly maxEncodedBytes: ByteSize.ByteSize;
|
|
631
|
-
readonly maxIdentityBytes: ByteSize.ByteSize;
|
|
1222
|
+
readonly maxEncodedBytes: import("effect/ByteSize").ByteSize;
|
|
1223
|
+
readonly maxIdentityBytes: import("effect/ByteSize").ByteSize;
|
|
632
1224
|
readonly maxDeltaRecords: number;
|
|
633
|
-
readonly maxDecodedDeltaBytes: ByteSize.ByteSize;
|
|
1225
|
+
readonly maxDecodedDeltaBytes: import("effect/ByteSize").ByteSize;
|
|
634
1226
|
readonly maxBaseRecords: number;
|
|
635
1227
|
readonly maxTargetRecords: number;
|
|
636
1228
|
readonly maxEntries: number;
|
|
637
1229
|
readonly maxOutputRecords: number;
|
|
638
|
-
readonly maxOutputBytes: ByteSize.ByteSize;
|
|
1230
|
+
readonly maxOutputBytes: import("effect/ByteSize").ByteSize;
|
|
639
1231
|
readonly maxInheritedRecords: number;
|
|
640
1232
|
} | undefined) => Effect.Effect<readonly ({
|
|
641
1233
|
readonly _tag: "Added";
|
|
@@ -651,29 +1243,92 @@ export declare const inspectSnapshotDelta: (base: Snapshot, delta: SnapshotDelta
|
|
|
651
1243
|
readonly beforeKind: "directory" | "file" | "symlink";
|
|
652
1244
|
readonly afterKind: "directory" | "file" | "symlink";
|
|
653
1245
|
readonly differences: readonly ("atimeNs" | "birthtimeNs" | "content" | "ctimeNs" | "gid" | "hardLinks" | "kind" | "mode" | "mtimeNs" | "target" | "uid")[];
|
|
654
|
-
})[], ConfigurationError | ImageError | import("effect/PlatformError").PlatformError | SnapshotDeltaModel.SnapshotDeltaError, import("effect/Crypto").Crypto>;
|
|
1246
|
+
})[], VfsModel.ConfigurationError | ImageError | import("effect/PlatformError").PlatformError | SnapshotDeltaModel.SnapshotDeltaError, import("effect/Crypto").Crypto>;
|
|
655
1247
|
/**
|
|
656
1248
|
* Applies an exact delta to its semantically matching base and returns a new snapshot.
|
|
657
|
-
* Requires the platform-neutral `Crypto.Crypto` service for base identity.
|
|
1249
|
+
* Requires the platform-neutral `Crypto.Crypto` service for base identity. Provide
|
|
1250
|
+
* `NodeCrypto.layer`, `BunCrypto.layer`, or your own via `Crypto.make`.
|
|
658
1251
|
*
|
|
1252
|
+
* @example
|
|
1253
|
+
* ```ts
|
|
1254
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
1255
|
+
* import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
1256
|
+
* import { Effect } from "effect"
|
|
1257
|
+
*
|
|
1258
|
+
* const program = Effect.gen(function*() {
|
|
1259
|
+
* const volume = yield* Vfs.make()
|
|
1260
|
+
* const caller = yield* volume.caller()
|
|
1261
|
+
* const base = yield* volume.snapshot
|
|
1262
|
+
*
|
|
1263
|
+
* yield* caller.writeFile("/f", new Uint8Array([7]), {
|
|
1264
|
+
* access: "write",
|
|
1265
|
+
* create: "exclusive"
|
|
1266
|
+
* })
|
|
1267
|
+
*
|
|
1268
|
+
* const delta = yield* Vfs.diffSnapshots(base, yield* volume.snapshot)
|
|
1269
|
+
*
|
|
1270
|
+
* // Applying the delta to the same base reproduces the target snapshot.
|
|
1271
|
+
* const applied = yield* Vfs.applySnapshotDelta(base, delta)
|
|
1272
|
+
* const restored = yield* Vfs.fromSnapshot(applied)
|
|
1273
|
+
* const reader = yield* restored.caller()
|
|
1274
|
+
*
|
|
1275
|
+
* return yield* reader.readFile("/f")
|
|
1276
|
+
* }).pipe(Effect.provide(NodeCrypto.layer))
|
|
1277
|
+
*
|
|
1278
|
+
* Effect.runPromise(program).then(console.log)
|
|
1279
|
+
* // Uint8Array(1) [ 7 ]
|
|
1280
|
+
* ```
|
|
1281
|
+
*
|
|
1282
|
+
* @see {@link SnapshotDeltaFromBytes} for moving a delta across a boundary.
|
|
659
1283
|
* @category snapshots
|
|
660
1284
|
* @since 0.1.0
|
|
661
1285
|
*/
|
|
662
1286
|
export declare const applySnapshotDelta: (base: Snapshot, delta: SnapshotDeltaModel.SnapshotDelta, limits?: {
|
|
663
|
-
readonly maxEncodedBytes: ByteSize.ByteSize;
|
|
664
|
-
readonly maxIdentityBytes: ByteSize.ByteSize;
|
|
1287
|
+
readonly maxEncodedBytes: import("effect/ByteSize").ByteSize;
|
|
1288
|
+
readonly maxIdentityBytes: import("effect/ByteSize").ByteSize;
|
|
665
1289
|
readonly maxDeltaRecords: number;
|
|
666
|
-
readonly maxDecodedDeltaBytes: ByteSize.ByteSize;
|
|
1290
|
+
readonly maxDecodedDeltaBytes: import("effect/ByteSize").ByteSize;
|
|
667
1291
|
readonly maxBaseRecords: number;
|
|
668
1292
|
readonly maxTargetRecords: number;
|
|
669
1293
|
readonly maxEntries: number;
|
|
670
1294
|
readonly maxOutputRecords: number;
|
|
671
|
-
readonly maxOutputBytes: ByteSize.ByteSize;
|
|
1295
|
+
readonly maxOutputBytes: import("effect/ByteSize").ByteSize;
|
|
672
1296
|
readonly maxInheritedRecords: number;
|
|
673
|
-
} | undefined) => Effect.Effect<Snapshot, ConfigurationError | ImageError | import("effect/PlatformError").PlatformError | SnapshotDeltaModel.SnapshotDeltaError, import("effect/Crypto").Crypto>;
|
|
1297
|
+
} | undefined) => Effect.Effect<Snapshot, VfsModel.ConfigurationError | ImageError | import("effect/PlatformError").PlatformError | SnapshotDeltaModel.SnapshotDeltaError, import("effect/Crypto").Crypto>;
|
|
674
1298
|
/**
|
|
675
1299
|
* Creates an Effect Schema codec between owned bytes and opaque snapshot deltas.
|
|
676
1300
|
* Omission uses `SnapshotDeltaLimits.default`.
|
|
1301
|
+
*
|
|
1302
|
+
* @example
|
|
1303
|
+
* ```ts
|
|
1304
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
1305
|
+
* import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
1306
|
+
* import { Effect, Schema } from "effect"
|
|
1307
|
+
*
|
|
1308
|
+
* // Omitting limits uses `SnapshotDeltaLimits.default`.
|
|
1309
|
+
* const codec = Vfs.SnapshotDeltaFromBytes()
|
|
1310
|
+
*
|
|
1311
|
+
* const program = Effect.gen(function*() {
|
|
1312
|
+
* const volume = yield* Vfs.make()
|
|
1313
|
+
* const base = yield* volume.snapshot
|
|
1314
|
+
*
|
|
1315
|
+
* yield* (yield* volume.caller()).mkdir("/out")
|
|
1316
|
+
*
|
|
1317
|
+
* const delta = yield* Vfs.diffSnapshots(base, yield* volume.snapshot)
|
|
1318
|
+
* const bytes = yield* Schema.encodeEffect(codec)(delta)
|
|
1319
|
+
*
|
|
1320
|
+
* // The delta survives the trip through bytes and still applies to its base.
|
|
1321
|
+
* const decoded = yield* Schema.decodeEffect(codec)(bytes)
|
|
1322
|
+
*
|
|
1323
|
+
* return (yield* Vfs.inspectSnapshotDelta(base, decoded)).map((change) => change._tag)
|
|
1324
|
+
* }).pipe(Effect.provide(NodeCrypto.layer))
|
|
1325
|
+
*
|
|
1326
|
+
* Effect.runPromise(program).then(console.log)
|
|
1327
|
+
* // [ 'Added' ]
|
|
1328
|
+
* ```
|
|
1329
|
+
*
|
|
1330
|
+
* @category schemas
|
|
1331
|
+
* @since 0.1.0
|
|
677
1332
|
*/
|
|
678
1333
|
export declare const SnapshotDeltaFromBytes: (limits?: SnapshotDeltaModel.SnapshotDeltaLimits) => Schema.decodeTo<Schema.declare<SnapshotDeltaModel.SnapshotDelta, SnapshotDeltaModel.SnapshotDelta>, Schema.Uint8Array, never, never>;
|
|
679
1334
|
/**
|
|
@@ -683,17 +1338,77 @@ export declare const SnapshotDeltaFromBytes: (limits?: SnapshotDeltaModel.Snapsh
|
|
|
683
1338
|
*
|
|
684
1339
|
* Shared-memory-backed and detached views fail with `InvalidArgument`.
|
|
685
1340
|
*
|
|
1341
|
+
* @example
|
|
1342
|
+
* ```ts
|
|
1343
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
1344
|
+
* import { Effect } from "effect"
|
|
1345
|
+
*
|
|
1346
|
+
* // Any byte except NUL is accepted. Empty, NUL-bearing, detached, and shared-memory
|
|
1347
|
+
* // input all fail with `InvalidArgument`.
|
|
1348
|
+
* const program = Effect.gen(function*() {
|
|
1349
|
+
* const path = yield* Vfs.pathFromBytes(new Uint8Array([47, 116, 109, 112]))
|
|
1350
|
+
*
|
|
1351
|
+
* const rejected = yield* Vfs.pathFromBytes(new Uint8Array([47, 0])).pipe(
|
|
1352
|
+
* Effect.as("accepted"),
|
|
1353
|
+
* Effect.catchTag("FsError", (error) => Effect.succeed(error.code))
|
|
1354
|
+
* )
|
|
1355
|
+
*
|
|
1356
|
+
* return [yield* Vfs.pathToBytes(path), rejected]
|
|
1357
|
+
* })
|
|
1358
|
+
*
|
|
1359
|
+
* Effect.runPromise(program).then(console.log)
|
|
1360
|
+
* // [ Uint8Array(4) [ 47, 116, 109, 112 ], 'InvalidArgument' ]
|
|
1361
|
+
* ```
|
|
1362
|
+
*
|
|
686
1363
|
* @category constructors
|
|
687
1364
|
* @since 0.1.0
|
|
688
1365
|
*/
|
|
689
|
-
export declare const pathFromBytes: (bytes: Uint8Array
|
|
1366
|
+
export declare const pathFromBytes: (bytes: Uint8Array) => Effect.Effect<BytePath, FsError>;
|
|
690
1367
|
/**
|
|
691
1368
|
* Copies the bytes held by an opaque byte path.
|
|
692
1369
|
*
|
|
693
1370
|
* @category getters
|
|
694
1371
|
* @since 0.1.0
|
|
695
1372
|
*/
|
|
696
|
-
export declare const pathToBytes: (path: BytePath) => Effect.Effect<Uint8Array
|
|
1373
|
+
export declare const pathToBytes: (path: BytePath) => Effect.Effect<Uint8Array, FsError>;
|
|
1374
|
+
/**
|
|
1375
|
+
* Schema for a complete fixture namespace with optional metadata and forward hard links.
|
|
1376
|
+
*
|
|
1377
|
+
* **Details**
|
|
1378
|
+
*
|
|
1379
|
+
* Fixture paths must be absolute, unique, and explicitly include their parent directories.
|
|
1380
|
+
*
|
|
1381
|
+
* @example
|
|
1382
|
+
* ```ts
|
|
1383
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
1384
|
+
* import { Effect, Schema } from "effect"
|
|
1385
|
+
*
|
|
1386
|
+
* // Fixtures must list every parent directory before its children.
|
|
1387
|
+
* const program = Schema.decodeUnknownEffect(Vfs.Fixture)({
|
|
1388
|
+
* entries: [
|
|
1389
|
+
* { kind: "file", path: "/etc/hosts", bytes: new TextEncoder().encode("127.0.0.1") }
|
|
1390
|
+
* ]
|
|
1391
|
+
* }).pipe(
|
|
1392
|
+
* Effect.flatMap(Vfs.fromFixture),
|
|
1393
|
+
* Effect.as("built"),
|
|
1394
|
+
* Effect.catchTag("ImageError", (error) => Effect.succeed(`rejected: ${error.code}`))
|
|
1395
|
+
* )
|
|
1396
|
+
*
|
|
1397
|
+
* Effect.runPromise(program).then(console.log)
|
|
1398
|
+
* // rejected: InvalidStructure
|
|
1399
|
+
* ```
|
|
1400
|
+
*
|
|
1401
|
+
* @category schemas
|
|
1402
|
+
* @since 0.1.0
|
|
1403
|
+
*/
|
|
1404
|
+
export declare const Fixture: typeof VfsModel.Fixture;
|
|
1405
|
+
/**
|
|
1406
|
+
* A complete filesystem fixture accepted by `fromFixture`.
|
|
1407
|
+
*
|
|
1408
|
+
* @category models
|
|
1409
|
+
* @since 0.1.0
|
|
1410
|
+
*/
|
|
1411
|
+
export type Fixture = typeof Fixture.Type;
|
|
697
1412
|
/**
|
|
698
1413
|
* Creates a fresh empty volume and captures the current Effect `Clock`.
|
|
699
1414
|
*
|
|
@@ -702,27 +1417,71 @@ export declare const pathToBytes: (path: BytePath) => Effect.Effect<Uint8Array<A
|
|
|
702
1417
|
* Each execution creates independent storage. Snapshot image failures cannot
|
|
703
1418
|
* arise because this constructor does not accept persisted input.
|
|
704
1419
|
*
|
|
1420
|
+
* @example
|
|
1421
|
+
* ```ts
|
|
1422
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
1423
|
+
* import { Effect } from "effect"
|
|
1424
|
+
*
|
|
1425
|
+
* const program = Effect.gen(function*() {
|
|
1426
|
+
* const volume = yield* Vfs.make()
|
|
1427
|
+
* const caller = yield* volume.caller()
|
|
1428
|
+
*
|
|
1429
|
+
* yield* caller.mkdir("/work")
|
|
1430
|
+
* yield* caller.writeFile("/work/notes.txt", new Uint8Array([104, 105]), { access: "write", create: "exclusive" })
|
|
1431
|
+
*
|
|
1432
|
+
* return yield* caller.readFile("/work/notes.txt")
|
|
1433
|
+
* })
|
|
1434
|
+
*
|
|
1435
|
+
* Effect.runPromise(program).then(console.log)
|
|
1436
|
+
* // Uint8Array(2) [ 104, 105 ]
|
|
1437
|
+
* ```
|
|
1438
|
+
*
|
|
1439
|
+
* @see The testing guide at `/guides/testing-with-an-isolated-filesystem`.
|
|
705
1440
|
* @category constructors
|
|
706
1441
|
* @since 0.1.0
|
|
707
1442
|
*/
|
|
708
|
-
export declare const make: (options?:
|
|
709
|
-
readonly maxEntries?: number;
|
|
710
|
-
readonly maxBytes?: ByteSize.ByteSize;
|
|
711
|
-
readonly maxFileBytes?: ByteSize.ByteSize;
|
|
712
|
-
readonly maxPathBytes?: ByteSize.ByteSize;
|
|
713
|
-
} | undefined) => Effect.Effect<Volume, ConfigurationError, never>;
|
|
1443
|
+
export declare const make: (options?: VolumeOptions) => Effect.Effect<Volume, ConfigurationError>;
|
|
714
1444
|
/**
|
|
715
1445
|
* Restores a fresh volume from an opaque snapshot under the supplied destination limits.
|
|
716
1446
|
*
|
|
1447
|
+
* @example
|
|
1448
|
+
* ```ts
|
|
1449
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
1450
|
+
* import { Effect } from "effect"
|
|
1451
|
+
*
|
|
1452
|
+
* const program = Effect.gen(function*() {
|
|
1453
|
+
* const original = yield* Vfs.make()
|
|
1454
|
+
* const source = yield* original.caller()
|
|
1455
|
+
*
|
|
1456
|
+
* yield* source.writeFile("/seed.txt", new TextEncoder().encode("one"), {
|
|
1457
|
+
* access: "write",
|
|
1458
|
+
* create: "exclusive"
|
|
1459
|
+
* })
|
|
1460
|
+
*
|
|
1461
|
+
* const restored = yield* Vfs.fromSnapshot(yield* original.snapshot)
|
|
1462
|
+
* const copy = yield* restored.caller()
|
|
1463
|
+
*
|
|
1464
|
+
* // Writing to the restore leaves the original untouched: the two are independent.
|
|
1465
|
+
* yield* copy.writeFile("/seed.txt", new TextEncoder().encode("two"), {
|
|
1466
|
+
* access: "write",
|
|
1467
|
+
* truncate: true
|
|
1468
|
+
* })
|
|
1469
|
+
*
|
|
1470
|
+
* return [
|
|
1471
|
+
* new TextDecoder().decode(yield* source.readFile("/seed.txt")),
|
|
1472
|
+
* new TextDecoder().decode(yield* copy.readFile("/seed.txt"))
|
|
1473
|
+
* ]
|
|
1474
|
+
* })
|
|
1475
|
+
*
|
|
1476
|
+
* Effect.runPromise(program).then(console.log)
|
|
1477
|
+
* // [ 'one', 'two' ]
|
|
1478
|
+
* ```
|
|
1479
|
+
*
|
|
1480
|
+
* @see {@link makeOverlay} for a copy-on-write workspace over the same base.
|
|
717
1481
|
* @category constructors
|
|
718
1482
|
* @since 0.1.0
|
|
719
1483
|
*/
|
|
720
|
-
export declare const fromSnapshot: (snapshot: Snapshot, options?:
|
|
721
|
-
readonly maxEntries?: number;
|
|
722
|
-
readonly maxBytes?: ByteSize.ByteSize;
|
|
723
|
-
readonly maxFileBytes?: ByteSize.ByteSize;
|
|
724
|
-
readonly maxPathBytes?: ByteSize.ByteSize;
|
|
725
|
-
} | undefined) => Effect.Effect<Volume, ConfigurationError | ImageError, never>;
|
|
1484
|
+
export declare const fromSnapshot: (snapshot: Snapshot, options?: VolumeOptions) => Effect.Effect<Volume, ConfigurationError | ImageError>;
|
|
726
1485
|
/**
|
|
727
1486
|
* Creates an isolated writable volume relative to one immutable snapshot base.
|
|
728
1487
|
*
|
|
@@ -736,149 +1495,77 @@ export declare const fromSnapshot: (snapshot: Snapshot, options?: {
|
|
|
736
1495
|
* Invalid base snapshots fail with `ImageError`; invalid volume limits fail
|
|
737
1496
|
* with `ConfigurationError`. Each execution creates a fresh workspace.
|
|
738
1497
|
*
|
|
739
|
-
* @
|
|
740
|
-
*
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
readonly maxEntries?: number;
|
|
744
|
-
readonly maxBytes?: ByteSize.ByteSize;
|
|
745
|
-
readonly maxFileBytes?: ByteSize.ByteSize;
|
|
746
|
-
readonly maxPathBytes?: ByteSize.ByteSize;
|
|
747
|
-
} | undefined) => Effect.Effect<OverlayVolume, ConfigurationError | ImageError, never>;
|
|
748
|
-
/**
|
|
749
|
-
* Schema for a complete fixture namespace with optional metadata and forward hard links.
|
|
1498
|
+
* @example
|
|
1499
|
+
* ```ts
|
|
1500
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
1501
|
+
* import { Effect } from "effect"
|
|
750
1502
|
*
|
|
751
|
-
*
|
|
1503
|
+
* const program = Effect.gen(function*() {
|
|
1504
|
+
* const base = yield* Vfs.make()
|
|
1505
|
+
* const origin = yield* base.caller()
|
|
752
1506
|
*
|
|
753
|
-
*
|
|
1507
|
+
* yield* origin.writeFile("/config.json", new TextEncoder().encode("{}"), {
|
|
1508
|
+
* access: "write",
|
|
1509
|
+
* create: "exclusive"
|
|
1510
|
+
* })
|
|
754
1511
|
*
|
|
755
|
-
*
|
|
756
|
-
* @since 0.1.0
|
|
757
|
-
*/
|
|
758
|
-
export declare const Fixture: Schema.Struct<{
|
|
759
|
-
readonly rootMetadata: Schema.optionalKey<Schema.Struct<{
|
|
760
|
-
readonly uid: Schema.optionalKey<Schema.Finite>;
|
|
761
|
-
readonly gid: Schema.optionalKey<Schema.Finite>;
|
|
762
|
-
readonly mode: Schema.optionalKey<Schema.Finite>;
|
|
763
|
-
readonly atimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
764
|
-
readonly mtimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
765
|
-
readonly ctimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
766
|
-
readonly birthtimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
767
|
-
}>>;
|
|
768
|
-
readonly entries: Schema.$Array<Schema.Union<readonly [Schema.Struct<{
|
|
769
|
-
readonly kind: Schema.Literal<"directory">;
|
|
770
|
-
readonly path: Schema.Union<readonly [Schema.String, Schema.declare<BytePath, BytePath>]>;
|
|
771
|
-
readonly metadata: Schema.optionalKey<Schema.Struct<{
|
|
772
|
-
readonly uid: Schema.optionalKey<Schema.Finite>;
|
|
773
|
-
readonly gid: Schema.optionalKey<Schema.Finite>;
|
|
774
|
-
readonly mode: Schema.optionalKey<Schema.Finite>;
|
|
775
|
-
readonly atimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
776
|
-
readonly mtimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
777
|
-
readonly ctimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
778
|
-
readonly birthtimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
779
|
-
}>>;
|
|
780
|
-
}>, Schema.Struct<{
|
|
781
|
-
readonly kind: Schema.Literal<"file">;
|
|
782
|
-
readonly path: Schema.Union<readonly [Schema.String, Schema.declare<BytePath, BytePath>]>;
|
|
783
|
-
readonly bytes: Schema.Uint8Array;
|
|
784
|
-
readonly metadata: Schema.optionalKey<Schema.Struct<{
|
|
785
|
-
readonly uid: Schema.optionalKey<Schema.Finite>;
|
|
786
|
-
readonly gid: Schema.optionalKey<Schema.Finite>;
|
|
787
|
-
readonly mode: Schema.optionalKey<Schema.Finite>;
|
|
788
|
-
readonly atimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
789
|
-
readonly mtimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
790
|
-
readonly ctimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
791
|
-
readonly birthtimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
792
|
-
}>>;
|
|
793
|
-
}>, Schema.Struct<{
|
|
794
|
-
readonly kind: Schema.Literal<"symlink">;
|
|
795
|
-
readonly path: Schema.Union<readonly [Schema.String, Schema.declare<BytePath, BytePath>]>;
|
|
796
|
-
readonly target: Schema.Union<readonly [Schema.String, Schema.declare<BytePath, BytePath>]>;
|
|
797
|
-
readonly metadata: Schema.optionalKey<Schema.Struct<{
|
|
798
|
-
readonly uid: Schema.optionalKey<Schema.Finite>;
|
|
799
|
-
readonly gid: Schema.optionalKey<Schema.Finite>;
|
|
800
|
-
readonly mode: Schema.optionalKey<Schema.Finite>;
|
|
801
|
-
readonly atimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
802
|
-
readonly mtimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
803
|
-
readonly ctimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
804
|
-
readonly birthtimeNs: Schema.optionalKey<Schema.BigInt>;
|
|
805
|
-
}>>;
|
|
806
|
-
}>, Schema.Struct<{
|
|
807
|
-
readonly kind: Schema.Literal<"hardLink">;
|
|
808
|
-
readonly path: Schema.Union<readonly [Schema.String, Schema.declare<BytePath, BytePath>]>;
|
|
809
|
-
readonly target: Schema.Union<readonly [Schema.String, Schema.declare<BytePath, BytePath>]>;
|
|
810
|
-
}>]>>;
|
|
811
|
-
}>;
|
|
812
|
-
/**
|
|
813
|
-
* A complete filesystem fixture accepted by `fromFixture`.
|
|
1512
|
+
* const workspace = yield* Vfs.makeOverlay(yield* base.snapshot)
|
|
814
1513
|
*
|
|
815
|
-
*
|
|
1514
|
+
* yield* (yield* workspace.caller()).writeFile("/config.json", new TextEncoder().encode("[]"), {
|
|
1515
|
+
* access: "write",
|
|
1516
|
+
* truncate: true
|
|
1517
|
+
* })
|
|
1518
|
+
*
|
|
1519
|
+
* // The write landed in the workspace only; the base still reads its own value.
|
|
1520
|
+
* return [
|
|
1521
|
+
* new TextDecoder().decode(yield* origin.readFile("/config.json")),
|
|
1522
|
+
* (yield* workspace.changes()).map((change) => change._tag)
|
|
1523
|
+
* ]
|
|
1524
|
+
* })
|
|
1525
|
+
*
|
|
1526
|
+
* Effect.runPromise(program).then(console.log)
|
|
1527
|
+
* // [ '{}', [ 'Updated' ] ]
|
|
1528
|
+
* ```
|
|
1529
|
+
*
|
|
1530
|
+
* @see The overlay guide at `/guides/overlay-filesystems`.
|
|
1531
|
+
* @category constructors
|
|
816
1532
|
* @since 0.1.0
|
|
817
1533
|
*/
|
|
818
|
-
export
|
|
1534
|
+
export declare const makeOverlay: (base: Snapshot, options?: VolumeOptions) => Effect.Effect<OverlayVolume, ConfigurationError | ImageError>;
|
|
819
1535
|
/**
|
|
820
1536
|
* Builds a fresh volume from a validated final-state fixture.
|
|
821
1537
|
*
|
|
1538
|
+
* @example
|
|
1539
|
+
* ```ts
|
|
1540
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
1541
|
+
* import { Effect } from "effect"
|
|
1542
|
+
*
|
|
1543
|
+
* const encoder = new TextEncoder()
|
|
1544
|
+
*
|
|
1545
|
+
* const program = Effect.gen(function*() {
|
|
1546
|
+
* // Parent directories must be listed explicitly, before their children.
|
|
1547
|
+
* const volume = yield* Vfs.fromFixture({
|
|
1548
|
+
* entries: [
|
|
1549
|
+
* { kind: "directory", path: "/project" },
|
|
1550
|
+
* {
|
|
1551
|
+
* kind: "file",
|
|
1552
|
+
* path: "/project/package.json",
|
|
1553
|
+
* bytes: encoder.encode(`{"version":"1.2.3"}`)
|
|
1554
|
+
* },
|
|
1555
|
+
* { kind: "symlink", path: "/project/latest", target: "/project/package.json" }
|
|
1556
|
+
* ]
|
|
1557
|
+
* })
|
|
1558
|
+
*
|
|
1559
|
+
* return yield* (yield* volume.caller()).readDirectory("/project")
|
|
1560
|
+
* })
|
|
1561
|
+
*
|
|
1562
|
+
* Effect.runPromise(program).then(console.log)
|
|
1563
|
+
* // [ 'package.json', 'latest' ]
|
|
1564
|
+
* ```
|
|
1565
|
+
*
|
|
1566
|
+
* @see The fixtures guide at `/guides/fixtures-and-snapshots`.
|
|
822
1567
|
* @category constructors
|
|
823
1568
|
* @since 0.1.0
|
|
824
1569
|
*/
|
|
825
|
-
export declare const fromFixture: (fixture:
|
|
826
|
-
readonly rootMetadata?: {
|
|
827
|
-
readonly uid?: number;
|
|
828
|
-
readonly gid?: number;
|
|
829
|
-
readonly mode?: number;
|
|
830
|
-
readonly atimeNs?: bigint;
|
|
831
|
-
readonly mtimeNs?: bigint;
|
|
832
|
-
readonly ctimeNs?: bigint;
|
|
833
|
-
readonly birthtimeNs?: bigint;
|
|
834
|
-
};
|
|
835
|
-
readonly entries: readonly ({
|
|
836
|
-
readonly kind: "directory";
|
|
837
|
-
readonly path: string | BytePath;
|
|
838
|
-
readonly metadata?: {
|
|
839
|
-
readonly uid?: number;
|
|
840
|
-
readonly gid?: number;
|
|
841
|
-
readonly mode?: number;
|
|
842
|
-
readonly atimeNs?: bigint;
|
|
843
|
-
readonly mtimeNs?: bigint;
|
|
844
|
-
readonly ctimeNs?: bigint;
|
|
845
|
-
readonly birthtimeNs?: bigint;
|
|
846
|
-
};
|
|
847
|
-
} | {
|
|
848
|
-
readonly kind: "file";
|
|
849
|
-
readonly path: string | BytePath;
|
|
850
|
-
readonly bytes: Uint8Array<ArrayBufferLike>;
|
|
851
|
-
readonly metadata?: {
|
|
852
|
-
readonly uid?: number;
|
|
853
|
-
readonly gid?: number;
|
|
854
|
-
readonly mode?: number;
|
|
855
|
-
readonly atimeNs?: bigint;
|
|
856
|
-
readonly mtimeNs?: bigint;
|
|
857
|
-
readonly ctimeNs?: bigint;
|
|
858
|
-
readonly birthtimeNs?: bigint;
|
|
859
|
-
};
|
|
860
|
-
} | {
|
|
861
|
-
readonly kind: "symlink";
|
|
862
|
-
readonly path: string | BytePath;
|
|
863
|
-
readonly target: string | BytePath;
|
|
864
|
-
readonly metadata?: {
|
|
865
|
-
readonly uid?: number;
|
|
866
|
-
readonly gid?: number;
|
|
867
|
-
readonly mode?: number;
|
|
868
|
-
readonly atimeNs?: bigint;
|
|
869
|
-
readonly mtimeNs?: bigint;
|
|
870
|
-
readonly ctimeNs?: bigint;
|
|
871
|
-
readonly birthtimeNs?: bigint;
|
|
872
|
-
};
|
|
873
|
-
} | {
|
|
874
|
-
readonly kind: "hardLink";
|
|
875
|
-
readonly path: string | BytePath;
|
|
876
|
-
readonly target: string | BytePath;
|
|
877
|
-
})[];
|
|
878
|
-
}, options?: {
|
|
879
|
-
readonly maxEntries?: number;
|
|
880
|
-
readonly maxBytes?: ByteSize.ByteSize;
|
|
881
|
-
readonly maxFileBytes?: ByteSize.ByteSize;
|
|
882
|
-
readonly maxPathBytes?: ByteSize.ByteSize;
|
|
883
|
-
} | undefined) => Effect.Effect<Volume, ConfigurationError | ImageError, never>;
|
|
1570
|
+
export declare const fromFixture: (fixture: Fixture, options?: VolumeOptions) => Effect.Effect<Volume, ConfigurationError | ImageError>;
|
|
884
1571
|
//# sourceMappingURL=VirtualFileSystem.d.ts.map
|