@effect-vfs/core 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -21
- package/dist/BytePath.d.ts +115 -4
- package/dist/BytePath.d.ts.map +1 -1
- package/dist/BytePath.js +155 -0
- package/dist/Caller.d.ts +391 -0
- package/dist/Caller.d.ts.map +1 -0
- package/dist/Caller.js +199 -0
- package/dist/FileHandle.d.ts +50 -0
- package/dist/FileHandle.d.ts.map +1 -0
- package/dist/FileHandle.js +28 -0
- package/dist/Fixture.d.ts +155 -0
- package/dist/Fixture.d.ts.map +1 -0
- package/dist/Fixture.js +65 -0
- package/dist/LiveVolume.d.ts +13 -20
- package/dist/LiveVolume.d.ts.map +1 -1
- package/dist/LiveVolume.js +19 -14
- package/dist/Metadata.d.ts +186 -0
- package/dist/Metadata.d.ts.map +1 -0
- package/dist/Metadata.js +125 -0
- package/dist/Snapshot.d.ts +32 -53
- package/dist/Snapshot.d.ts.map +1 -1
- package/dist/Snapshot.js +27 -49
- package/dist/SnapshotDelta.d.ts +24 -39
- package/dist/SnapshotDelta.d.ts.map +1 -1
- package/dist/SnapshotDelta.js +14 -35
- package/dist/Target.d.ts +225 -0
- package/dist/Target.d.ts.map +1 -0
- package/dist/Target.js +92 -0
- package/dist/Testing.d.ts +132 -0
- package/dist/Testing.d.ts.map +1 -0
- package/dist/Testing.js +100 -0
- package/dist/VfsError.d.ts +200 -0
- package/dist/VfsError.d.ts.map +1 -0
- package/dist/VfsError.js +165 -0
- package/dist/VirtualFileSystem.d.ts +676 -353
- package/dist/VirtualFileSystem.d.ts.map +1 -1
- package/dist/VirtualFileSystem.js +487 -121
- package/dist/Volume.d.ts +211 -0
- package/dist/Volume.d.ts.map +1 -0
- package/dist/Volume.js +172 -0
- package/dist/Watch.d.ts +68 -0
- package/dist/Watch.d.ts.map +1 -0
- package/dist/Watch.js +46 -0
- package/dist/index.d.ts +56 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +56 -1
- package/dist/internal/budget.d.ts +90 -0
- package/dist/internal/budget.d.ts.map +1 -0
- package/dist/internal/budget.js +70 -0
- package/dist/internal/bytePath.d.ts +2 -0
- package/dist/internal/bytePath.d.ts.map +1 -1
- package/dist/internal/bytePath.js +3 -2
- package/dist/internal/bytes.d.ts +3 -0
- package/dist/internal/bytes.d.ts.map +1 -1
- package/dist/internal/bytes.js +4 -2
- package/dist/internal/canonicalBase64.d.ts +1 -4
- package/dist/internal/canonicalBase64.d.ts.map +1 -1
- package/dist/internal/canonicalBase64.js +9 -13
- package/dist/internal/errors.d.ts +47 -5
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/errors.js +80 -36
- package/dist/internal/fixture.d.ts +2 -2
- package/dist/internal/fixture.d.ts.map +1 -1
- package/dist/internal/fixture.js +57 -74
- package/dist/internal/hex128.d.ts +12 -0
- package/dist/internal/hex128.d.ts.map +1 -0
- package/dist/internal/hex128.js +7 -0
- package/dist/internal/hmac.d.ts +5 -0
- package/dist/internal/hmac.d.ts.map +1 -0
- package/dist/internal/hmac.js +150 -0
- package/dist/internal/image.d.ts +14 -117
- package/dist/internal/image.d.ts.map +1 -1
- package/dist/internal/image.js +137 -167
- package/dist/internal/inodeTable.d.ts +20 -0
- package/dist/internal/inodeTable.d.ts.map +1 -0
- package/dist/internal/inodeTable.js +48 -0
- package/dist/internal/lines.d.ts +36 -0
- package/dist/internal/lines.d.ts.map +1 -0
- package/dist/internal/lines.js +115 -0
- package/dist/internal/liveImage.d.ts +19 -278
- package/dist/internal/liveImage.d.ts.map +1 -1
- package/dist/internal/liveImage.js +75 -166
- package/dist/internal/merkle.d.ts +45 -0
- package/dist/internal/merkle.d.ts.map +1 -0
- package/dist/internal/merkle.js +179 -0
- package/dist/internal/metadata.d.ts +2 -13
- package/dist/internal/metadata.d.ts.map +1 -1
- package/dist/internal/metadata.js +5 -15
- package/dist/internal/overlayDiff.d.ts +2 -2
- package/dist/internal/overlayDiff.d.ts.map +1 -1
- package/dist/internal/overlayDiff.js +4 -15
- package/dist/internal/path.d.ts +17 -5
- package/dist/internal/path.d.ts.map +1 -1
- package/dist/internal/path.js +38 -24
- package/dist/internal/snapshotDelta.d.ts +61 -60
- package/dist/internal/snapshotDelta.d.ts.map +1 -1
- package/dist/internal/snapshotDelta.js +605 -503
- package/dist/internal/snapshotDeltaModel.d.ts.map +1 -1
- package/dist/internal/snapshotDeltaModel.js +0 -1
- package/dist/internal/testSeams.d.ts +17 -0
- package/dist/internal/testSeams.d.ts.map +1 -0
- package/dist/internal/testSeams.js +20 -0
- package/dist/internal/tree.d.ts +225 -0
- package/dist/internal/tree.d.ts.map +1 -0
- package/dist/internal/tree.js +385 -0
- package/dist/internal/turnstile.d.ts +8 -0
- package/dist/internal/turnstile.d.ts.map +1 -0
- package/dist/internal/turnstile.js +43 -0
- package/dist/internal/virtualFileSystem.d.ts +44 -488
- package/dist/internal/virtualFileSystem.d.ts.map +1 -1
- package/dist/internal/virtualFileSystem.js +2111 -2466
- package/dist/internal/volumeState.d.ts +111 -0
- package/dist/internal/volumeState.d.ts.map +1 -0
- package/dist/internal/volumeState.js +202 -0
- package/dist/internal/watchHub.d.ts +15 -6
- package/dist/internal/watchHub.d.ts.map +1 -1
- package/dist/internal/watchHub.js +56 -42
- package/package.json +54 -10
- package/dist/VirtualFileSystemError.d.ts +0 -54
- package/dist/VirtualFileSystemError.d.ts.map +0 -1
- package/dist/VirtualFileSystemError.js +0 -49
- package/dist/internal/overlayContent.d.ts +0 -15
- package/dist/internal/overlayContent.d.ts.map +0 -1
- package/dist/internal/overlayContent.js +0 -29
- package/dist/internal/stagedState.d.ts +0 -22
- package/dist/internal/stagedState.d.ts.map +0 -1
- package/dist/internal/stagedState.js +0 -62
- package/dist/internal/testHooks.d.ts +0 -20
- package/dist/internal/testHooks.d.ts.map +0 -1
- package/dist/internal/testHooks.js +0 -23
package/dist/Target.d.ts
ADDED
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a caller names what it operates on. A `Target` is a path, an object
|
|
3
|
+
* reference, or an open handle; an `Entry` is a child of a directory target
|
|
4
|
+
* called `name`. Every verb takes one or the other, so the three addressing
|
|
5
|
+
* modes share one body and one option vocabulary.
|
|
6
|
+
*
|
|
7
|
+
* @since 0.6.0
|
|
8
|
+
*/
|
|
9
|
+
import * as Data from "effect/Data";
|
|
10
|
+
import * as Schema from "effect/Schema";
|
|
11
|
+
import type { DirectoryHandle, FileHandle, ObjectReference, PathInput } from "./VirtualFileSystem.js";
|
|
12
|
+
/**
|
|
13
|
+
* A target: a path resolved from the caller's working directory or a base
|
|
14
|
+
* handle, an object reference, or an open handle.
|
|
15
|
+
*
|
|
16
|
+
* @category models
|
|
17
|
+
* @since 0.6.0
|
|
18
|
+
*/
|
|
19
|
+
export type Target = Data.TaggedEnum<{
|
|
20
|
+
readonly Path: {
|
|
21
|
+
readonly path: PathInput;
|
|
22
|
+
/** A live directory handle from the same volume that a relative path starts from. */
|
|
23
|
+
readonly relativeTo?: DirectoryHandle;
|
|
24
|
+
/** Whether a final symbolic link is followed. Defaults to `true`. */
|
|
25
|
+
readonly followFinalSymlink?: boolean;
|
|
26
|
+
};
|
|
27
|
+
readonly Reference: {
|
|
28
|
+
readonly reference: ObjectReference;
|
|
29
|
+
};
|
|
30
|
+
readonly Handle: {
|
|
31
|
+
readonly handle: FileHandle | DirectoryHandle;
|
|
32
|
+
};
|
|
33
|
+
}>;
|
|
34
|
+
declare const enumeration: {
|
|
35
|
+
readonly $is: <Tag extends "Handle" | "Path" | "Reference">(tag: Tag) => (u: unknown) => u is Extract<{
|
|
36
|
+
readonly _tag: "Handle";
|
|
37
|
+
readonly handle: FileHandle | DirectoryHandle;
|
|
38
|
+
}, {
|
|
39
|
+
readonly _tag: Tag;
|
|
40
|
+
}> | Extract<{
|
|
41
|
+
readonly _tag: "Path";
|
|
42
|
+
readonly path: PathInput;
|
|
43
|
+
readonly relativeTo?: DirectoryHandle;
|
|
44
|
+
readonly followFinalSymlink?: boolean;
|
|
45
|
+
}, {
|
|
46
|
+
readonly _tag: Tag;
|
|
47
|
+
}> | Extract<{
|
|
48
|
+
readonly _tag: "Reference";
|
|
49
|
+
readonly reference: ObjectReference;
|
|
50
|
+
}, {
|
|
51
|
+
readonly _tag: Tag;
|
|
52
|
+
}>;
|
|
53
|
+
readonly $match: {
|
|
54
|
+
<Cases extends { readonly [Tag in "Handle" | "Path" | "Reference"]: (args: Extract<{
|
|
55
|
+
readonly _tag: "Handle";
|
|
56
|
+
readonly handle: FileHandle | DirectoryHandle;
|
|
57
|
+
} | {
|
|
58
|
+
readonly _tag: "Path";
|
|
59
|
+
readonly path: PathInput;
|
|
60
|
+
readonly relativeTo?: DirectoryHandle;
|
|
61
|
+
readonly followFinalSymlink?: boolean;
|
|
62
|
+
} | {
|
|
63
|
+
readonly _tag: "Reference";
|
|
64
|
+
readonly reference: ObjectReference;
|
|
65
|
+
}, {
|
|
66
|
+
readonly _tag: Tag;
|
|
67
|
+
}>) => any; }>(cases: Cases): (value: {
|
|
68
|
+
readonly _tag: "Handle";
|
|
69
|
+
readonly handle: FileHandle | DirectoryHandle;
|
|
70
|
+
} | {
|
|
71
|
+
readonly _tag: "Path";
|
|
72
|
+
readonly path: PathInput;
|
|
73
|
+
readonly relativeTo?: DirectoryHandle;
|
|
74
|
+
readonly followFinalSymlink?: boolean;
|
|
75
|
+
} | {
|
|
76
|
+
readonly _tag: "Reference";
|
|
77
|
+
readonly reference: ObjectReference;
|
|
78
|
+
}) => import("effect/Unify").Unify<ReturnType<Cases["Handle" | "Path" | "Reference"]>>;
|
|
79
|
+
<Cases extends { readonly [Tag in "Handle" | "Path" | "Reference"]: (args: Extract<{
|
|
80
|
+
readonly _tag: "Handle";
|
|
81
|
+
readonly handle: FileHandle | DirectoryHandle;
|
|
82
|
+
} | {
|
|
83
|
+
readonly _tag: "Path";
|
|
84
|
+
readonly path: PathInput;
|
|
85
|
+
readonly relativeTo?: DirectoryHandle;
|
|
86
|
+
readonly followFinalSymlink?: boolean;
|
|
87
|
+
} | {
|
|
88
|
+
readonly _tag: "Reference";
|
|
89
|
+
readonly reference: ObjectReference;
|
|
90
|
+
}, {
|
|
91
|
+
readonly _tag: Tag;
|
|
92
|
+
}>) => any; }>(value: {
|
|
93
|
+
readonly _tag: "Handle";
|
|
94
|
+
readonly handle: FileHandle | DirectoryHandle;
|
|
95
|
+
} | {
|
|
96
|
+
readonly _tag: "Path";
|
|
97
|
+
readonly path: PathInput;
|
|
98
|
+
readonly relativeTo?: DirectoryHandle;
|
|
99
|
+
readonly followFinalSymlink?: boolean;
|
|
100
|
+
} | {
|
|
101
|
+
readonly _tag: "Reference";
|
|
102
|
+
readonly reference: ObjectReference;
|
|
103
|
+
}, cases: Cases): import("effect/Unify").Unify<ReturnType<Cases["Handle" | "Path" | "Reference"]>>;
|
|
104
|
+
};
|
|
105
|
+
readonly Handle: Data.TaggedEnum.ConstructorFrom<{
|
|
106
|
+
readonly _tag: "Handle";
|
|
107
|
+
readonly handle: FileHandle | DirectoryHandle;
|
|
108
|
+
}, "_tag">;
|
|
109
|
+
readonly Path: Data.TaggedEnum.ConstructorFrom<{
|
|
110
|
+
readonly _tag: "Path";
|
|
111
|
+
readonly path: PathInput;
|
|
112
|
+
readonly relativeTo?: DirectoryHandle;
|
|
113
|
+
readonly followFinalSymlink?: boolean;
|
|
114
|
+
}, "_tag">;
|
|
115
|
+
readonly Reference: Data.TaggedEnum.ConstructorFrom<{
|
|
116
|
+
readonly _tag: "Reference";
|
|
117
|
+
readonly reference: ObjectReference;
|
|
118
|
+
}, "_tag">;
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* Whether a value is a `Target`.
|
|
122
|
+
*
|
|
123
|
+
* @category predicates
|
|
124
|
+
* @since 0.6.0
|
|
125
|
+
*/
|
|
126
|
+
export declare const isTarget: (value: unknown) => value is Target;
|
|
127
|
+
/**
|
|
128
|
+
* Anything a verb accepts where it takes a target: a `Target`, or the bare
|
|
129
|
+
* path, reference, or handle it would wrap.
|
|
130
|
+
*
|
|
131
|
+
* @category models
|
|
132
|
+
* @since 0.6.0
|
|
133
|
+
*/
|
|
134
|
+
export type TargetInput = Target | PathInput | ObjectReference | FileHandle | DirectoryHandle;
|
|
135
|
+
/**
|
|
136
|
+
* Constructors and matchers for `Target`, plus `of`, which wraps a bare path,
|
|
137
|
+
* reference, or handle.
|
|
138
|
+
*
|
|
139
|
+
* @category constructors
|
|
140
|
+
* @since 0.6.0
|
|
141
|
+
*/
|
|
142
|
+
export declare const Target: typeof enumeration & {
|
|
143
|
+
readonly of: (input: TargetInput) => Target;
|
|
144
|
+
};
|
|
145
|
+
/**
|
|
146
|
+
* A directory entry name as bytes or as a string encoded as UTF-8: one to 255
|
|
147
|
+
* bytes with no NUL and no slash. `.` and `..` are well formed but reserved.
|
|
148
|
+
*
|
|
149
|
+
* @category models
|
|
150
|
+
* @since 0.6.0
|
|
151
|
+
*/
|
|
152
|
+
export type NameInput = string | Uint8Array;
|
|
153
|
+
/**
|
|
154
|
+
* Schema for a well-formed entry name, as bytes.
|
|
155
|
+
*
|
|
156
|
+
* @category schemas
|
|
157
|
+
* @since 0.6.0
|
|
158
|
+
*/
|
|
159
|
+
export declare const Name: Schema.Uint8Array;
|
|
160
|
+
/**
|
|
161
|
+
* A well-formed entry name, as bytes.
|
|
162
|
+
*
|
|
163
|
+
* @category models
|
|
164
|
+
* @since 0.6.0
|
|
165
|
+
*/
|
|
166
|
+
export type Name = typeof Name.Type;
|
|
167
|
+
/**
|
|
168
|
+
* A child of a directory target, called `name`.
|
|
169
|
+
*
|
|
170
|
+
* @category models
|
|
171
|
+
* @since 0.6.0
|
|
172
|
+
*/
|
|
173
|
+
export interface Entry {
|
|
174
|
+
readonly directory: Target;
|
|
175
|
+
readonly name: NameInput;
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Anything a verb accepts where it takes an entry: an `Entry`, or a path (bare
|
|
179
|
+
* or as a path target with a base) whose final component is the name and
|
|
180
|
+
* whose prefix is the directory.
|
|
181
|
+
*
|
|
182
|
+
* @category models
|
|
183
|
+
* @since 0.6.0
|
|
184
|
+
*/
|
|
185
|
+
export type EntryInput = Entry | PathInput | PathTarget;
|
|
186
|
+
/**
|
|
187
|
+
* The path variant of `Target`.
|
|
188
|
+
*
|
|
189
|
+
* @category models
|
|
190
|
+
* @since 0.6.0
|
|
191
|
+
*/
|
|
192
|
+
export type PathTarget = Extract<Target, {
|
|
193
|
+
readonly _tag: "Path";
|
|
194
|
+
}>;
|
|
195
|
+
/**
|
|
196
|
+
* Whether a value is an `Entry`.
|
|
197
|
+
*
|
|
198
|
+
* @category predicates
|
|
199
|
+
* @since 0.6.0
|
|
200
|
+
*/
|
|
201
|
+
export declare const isEntry: (value: unknown) => value is Entry;
|
|
202
|
+
/**
|
|
203
|
+
* Names a child of a directory. The directory may be given bare.
|
|
204
|
+
*
|
|
205
|
+
* @example
|
|
206
|
+
* ```ts
|
|
207
|
+
* import { Entry, Target } from "@effect-vfs/core/Target"
|
|
208
|
+
*
|
|
209
|
+
* const explicit = Entry(Target.Path({ path: "/var" }), "log")
|
|
210
|
+
* const bare = Entry("/var", "log")
|
|
211
|
+
* ```
|
|
212
|
+
*
|
|
213
|
+
* @category constructors
|
|
214
|
+
* @since 0.6.0
|
|
215
|
+
*/
|
|
216
|
+
export declare const Entry: (directory: TargetInput, name: NameInput) => Entry;
|
|
217
|
+
/**
|
|
218
|
+
* Whether a path input is a byte path rather than a string.
|
|
219
|
+
*
|
|
220
|
+
* @category predicates
|
|
221
|
+
* @since 0.6.0
|
|
222
|
+
*/
|
|
223
|
+
export declare const isBytePathInput: (input: PathInput) => boolean;
|
|
224
|
+
export {};
|
|
225
|
+
//# sourceMappingURL=Target.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Target.d.ts","sourceRoot":"","sources":["../src/Target.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,IAAI,MAAM,aAAa,CAAA;AAEnC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC,OAAO,KAAK,EAAE,eAAe,EAAE,UAAU,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAErG;;;;;;GAMG;AACH,MAAM,MAAM,MAAM,GAAG,IAAI,CAAC,UAAU,CAAC;IACnC,QAAQ,CAAC,IAAI,EAAE;QACb,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;QACxB,qFAAqF;QACrF,QAAQ,CAAC,UAAU,CAAC,EAAE,eAAe,CAAA;QACrC,qEAAqE;QACrE,QAAQ,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAA;KACtC,CAAA;IACD,QAAQ,CAAC,SAAS,EAAE;QAAE,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAA;KAAE,CAAA;IAC3D,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,UAAU,GAAG,eAAe,CAAA;KAAE,CAAA;CACnE,CAAC,CAAA;AAQF,QAAA,MAAM,WAAW;;;yBATqB,UAAU,GAAG,eAAe;;;;;uBAP/C,SAAS;8BAEF,eAAe;sCAEP,OAAO;;;;;4BAEG,eAAe;;;;;;;6BACrB,UAAU,GAAG,eAAe;;;2BAP/C,SAAS;kCAEF,eAAe;0CAEP,OAAO;;;gCAEG,eAAe;;;;;;;;;;;;;;;;;6BACrB,UAAU,GAAG,eAAe;;;2BAP/C,SAAS;kCAEF,eAAe;0CAEP,OAAO;;;gCAEG,eAAe;;;;;;;;;;;;;;;;;;yBACrB,UAAU,GAAG,eAAe;;;;uBAP/C,SAAS;8BAEF,eAAe;sCAEP,OAAO;;;;4BAEG,eAAe;;CAUd,CAAA;AAE7C;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,UAAW,OAAO,KAAG,KAAK,IAAI,MACiE,CAAA;AAEpH;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,SAAS,GAAG,eAAe,GAAG,UAAU,GAAG,eAAe,CAAA;AAE7F;;;;;;GAMG;AACH,eAAO,MAAM,MAAM,EAAE,OAAO,WAAW,GAAG;IAAE,QAAQ,CAAC,EAAE,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,MAAM,CAAA;CAiBtF,CAAA;AAED;;;;;;GAMG;AACH,MAAM,MAAM,SAAS,GAAG,MAAM,GAAG,UAAU,CAAA;AAE3C;;;;;GAKG;AACH,eAAO,MAAM,IAAI,mBAYhB,CAAA;AAED;;;;;GAKG;AACH,MAAM,MAAM,IAAI,GAAG,OAAO,IAAI,CAAC,IAAI,CAAA;AAEnC;;;;;GAKG;AACH,MAAM,WAAW,KAAK;IACpB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;IAC1B,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAA;CACzB;AAED;;;;;;;GAOG;AACH,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,SAAS,GAAG,UAAU,CAAA;AAEvD;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,CAAC,MAAM,EAAE;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC,CAAA;AAEnE;;;;;GAKG;AACH,eAAO,MAAM,OAAO,UAAW,OAAO,KAAG,KAAK,IAAI,KAC8D,CAAA;AAEhH;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,KAAK,cAAe,WAAW,QAAQ,SAAS,KAAG,KAAoD,CAAA;AAEpH;;;;;GAKG;AACH,eAAO,MAAM,eAAe,UAAW,SAAS,KAAG,OAAqC,CAAA"}
|
package/dist/Target.js
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a caller names what it operates on. A `Target` is a path, an object
|
|
3
|
+
* reference, or an open handle; an `Entry` is a child of a directory target
|
|
4
|
+
* called `name`. Every verb takes one or the other, so the three addressing
|
|
5
|
+
* modes share one body and one option vocabulary.
|
|
6
|
+
*
|
|
7
|
+
* @since 0.6.0
|
|
8
|
+
*/
|
|
9
|
+
import * as Data from "effect/Data";
|
|
10
|
+
import * as Predicate from "effect/Predicate";
|
|
11
|
+
import * as Schema from "effect/Schema";
|
|
12
|
+
import { DirectoryHandleId, FileHandleId } from "./FileHandle.js";
|
|
13
|
+
import * as Internal from "./internal/bytePath.js";
|
|
14
|
+
const isFileHandle = (value) => Predicate.hasProperty(FileHandleId)(value) && value[FileHandleId] === true;
|
|
15
|
+
const isDirectoryHandle = (value) => Predicate.hasProperty(DirectoryHandleId)(value) && value[DirectoryHandleId] === true;
|
|
16
|
+
const enumeration = Data.taggedEnum();
|
|
17
|
+
/**
|
|
18
|
+
* Whether a value is a `Target`.
|
|
19
|
+
*
|
|
20
|
+
* @category predicates
|
|
21
|
+
* @since 0.6.0
|
|
22
|
+
*/
|
|
23
|
+
export const isTarget = (value) => Predicate.isTagged("Path")(value) || Predicate.isTagged("Reference")(value) || Predicate.isTagged("Handle")(value);
|
|
24
|
+
/**
|
|
25
|
+
* Constructors and matchers for `Target`, plus `of`, which wraps a bare path,
|
|
26
|
+
* reference, or handle.
|
|
27
|
+
*
|
|
28
|
+
* @category constructors
|
|
29
|
+
* @since 0.6.0
|
|
30
|
+
*/
|
|
31
|
+
export const Target = {
|
|
32
|
+
Path: enumeration.Path,
|
|
33
|
+
Reference: enumeration.Reference,
|
|
34
|
+
Handle: enumeration.Handle,
|
|
35
|
+
$is: enumeration.$is,
|
|
36
|
+
$match: enumeration.$match,
|
|
37
|
+
// A string or byte path is a path and a handle is a handle; anything else is taken for a reference, so a forged
|
|
38
|
+
// token reaches the registry and is reported as one.
|
|
39
|
+
of: (input) => {
|
|
40
|
+
if (isTarget(input))
|
|
41
|
+
return input;
|
|
42
|
+
if (isFileHandle(input) || isDirectoryHandle(input))
|
|
43
|
+
return enumeration.Handle({ handle: input });
|
|
44
|
+
if (Predicate.isString(input) || Internal.isBytePath(input))
|
|
45
|
+
return enumeration.Path({ path: input });
|
|
46
|
+
return enumeration.Reference({ reference: input });
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* Schema for a well-formed entry name, as bytes.
|
|
51
|
+
*
|
|
52
|
+
* @category schemas
|
|
53
|
+
* @since 0.6.0
|
|
54
|
+
*/
|
|
55
|
+
export const Name = Schema.Uint8Array.check(Schema.makeFilter((bytes) => bytes.length === 0
|
|
56
|
+
? "must not be empty"
|
|
57
|
+
: bytes.length > 255
|
|
58
|
+
? "must be at most 255 bytes"
|
|
59
|
+
: bytes.includes(0)
|
|
60
|
+
? "must not contain NUL"
|
|
61
|
+
: bytes.includes(0x2f)
|
|
62
|
+
? "must not contain a slash"
|
|
63
|
+
: undefined));
|
|
64
|
+
/**
|
|
65
|
+
* Whether a value is an `Entry`.
|
|
66
|
+
*
|
|
67
|
+
* @category predicates
|
|
68
|
+
* @since 0.6.0
|
|
69
|
+
*/
|
|
70
|
+
export const isEntry = (value) => Predicate.hasProperty(value, "directory") && Predicate.hasProperty(value, "name") && isTarget(value.directory);
|
|
71
|
+
/**
|
|
72
|
+
* Names a child of a directory. The directory may be given bare.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* import { Entry, Target } from "@effect-vfs/core/Target"
|
|
77
|
+
*
|
|
78
|
+
* const explicit = Entry(Target.Path({ path: "/var" }), "log")
|
|
79
|
+
* const bare = Entry("/var", "log")
|
|
80
|
+
* ```
|
|
81
|
+
*
|
|
82
|
+
* @category constructors
|
|
83
|
+
* @since 0.6.0
|
|
84
|
+
*/
|
|
85
|
+
export const Entry = (directory, name) => ({ directory: Target.of(directory), name });
|
|
86
|
+
/**
|
|
87
|
+
* Whether a path input is a byte path rather than a string.
|
|
88
|
+
*
|
|
89
|
+
* @category predicates
|
|
90
|
+
* @since 0.6.0
|
|
91
|
+
*/
|
|
92
|
+
export const isBytePathInput = (input) => Internal.isBytePath(input);
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Test helpers over the `Volume` and `Caller` services: a fresh volume with a
|
|
3
|
+
* root caller as one layer, callers with other credentials on the volume in
|
|
4
|
+
* context, and collecting a known number of watch changes.
|
|
5
|
+
*
|
|
6
|
+
* **Details**
|
|
7
|
+
*
|
|
8
|
+
* The helpers are plain Effect values with no test-runner dependency. Each
|
|
9
|
+
* build of `layer` is a fresh volume, so provide it to every test that needs
|
|
10
|
+
* its own state, and share one build only between tests that do not
|
|
11
|
+
* interfere.
|
|
12
|
+
*
|
|
13
|
+
* @since 0.6.0
|
|
14
|
+
*/
|
|
15
|
+
import type * as Crypto from "effect/Crypto";
|
|
16
|
+
import * as Effect from "effect/Effect";
|
|
17
|
+
import * as Layer from "effect/Layer";
|
|
18
|
+
import type * as Scope from "effect/Scope";
|
|
19
|
+
import * as Stream from "effect/Stream";
|
|
20
|
+
import type { Identity, RootCallerOptions } from "./Caller.js";
|
|
21
|
+
import type { Fixture } from "./Fixture.js";
|
|
22
|
+
import type { VfsError } from "./VfsError.js";
|
|
23
|
+
import { Caller, Volume } from "./VirtualFileSystem.js";
|
|
24
|
+
import type { VolumeOptions } from "./Volume.js";
|
|
25
|
+
/**
|
|
26
|
+
* Options of a test volume layer.
|
|
27
|
+
*
|
|
28
|
+
* @category models
|
|
29
|
+
* @since 0.6.0
|
|
30
|
+
*/
|
|
31
|
+
export interface LayerOptions {
|
|
32
|
+
/** Entries the volume starts with; without one the volume starts empty. */
|
|
33
|
+
readonly fixture?: Fixture;
|
|
34
|
+
/** Limits and identity of the volume. */
|
|
35
|
+
readonly volume?: VolumeOptions;
|
|
36
|
+
/** Credentials and umask of the root caller the layer provides. */
|
|
37
|
+
readonly caller?: RootCallerOptions;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* A fresh volume and a root caller on it, as one layer.
|
|
41
|
+
*
|
|
42
|
+
* **Details**
|
|
43
|
+
*
|
|
44
|
+
* Every build constructs a new volume, from `fixture` when one is given. The
|
|
45
|
+
* layer fails with the `VfsError` the constructor or the caller reports for
|
|
46
|
+
* invalid options. Provide `Crypto.Crypto` to build the volume.
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```ts
|
|
50
|
+
* import * as BunCrypto from "@effect/platform-bun/BunCrypto"
|
|
51
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
52
|
+
* import * as Testing from "@effect-vfs/core/Testing"
|
|
53
|
+
* import { Effect } from "effect"
|
|
54
|
+
*
|
|
55
|
+
* const program = Effect.gen(function*() {
|
|
56
|
+
* const fs = yield* Vfs.Caller
|
|
57
|
+
* yield* fs.mkdir("/work")
|
|
58
|
+
*
|
|
59
|
+
* return (yield* fs.readDirectory("/")).value.map((entry) => new TextDecoder().decode(entry.name))
|
|
60
|
+
* })
|
|
61
|
+
*
|
|
62
|
+
* // Each provide builds its own volume, so neither run sees the other's writes.
|
|
63
|
+
* Effect.runPromise(program.pipe(Effect.provide(Testing.layer()), Effect.provide(BunCrypto.layer))).then(console.log)
|
|
64
|
+
* // [ 'work' ]
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
67
|
+
* @category layers
|
|
68
|
+
* @since 0.6.0
|
|
69
|
+
*/
|
|
70
|
+
export declare const layer: (options?: LayerOptions) => Layer.Layer<Volume | Caller, VfsError, Crypto.Crypto>;
|
|
71
|
+
/**
|
|
72
|
+
* A root caller with `identity` on the volume in context.
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* import * as BunCrypto from "@effect/platform-bun/BunCrypto"
|
|
77
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
78
|
+
* import * as Testing from "@effect-vfs/core/Testing"
|
|
79
|
+
* import { Effect } from "effect"
|
|
80
|
+
*
|
|
81
|
+
* const program = Effect.gen(function*() {
|
|
82
|
+
* yield* (yield* Vfs.Caller).mkdir("/private", { mode: 0o700 })
|
|
83
|
+
*
|
|
84
|
+
* const guest = yield* Testing.callerAs({ uid: 1000, gid: 1000, groups: [], privileged: false })
|
|
85
|
+
*
|
|
86
|
+
* return (yield* Effect.flip(guest.readDirectory("/private"))).code
|
|
87
|
+
* })
|
|
88
|
+
*
|
|
89
|
+
* Effect.runPromise(program.pipe(Effect.provide(Testing.layer()), Effect.provide(BunCrypto.layer))).then(console.log)
|
|
90
|
+
* // AccessDenied
|
|
91
|
+
* ```
|
|
92
|
+
*
|
|
93
|
+
* @category constructors
|
|
94
|
+
* @since 0.6.0
|
|
95
|
+
*/
|
|
96
|
+
export declare const callerAs: (identity: Identity, options?: Omit<RootCallerOptions, "identity">) => Effect.Effect<Caller, VfsError, Volume>;
|
|
97
|
+
/**
|
|
98
|
+
* Starts collecting the first `n` elements of `stream` and returns an effect
|
|
99
|
+
* that waits for them.
|
|
100
|
+
*
|
|
101
|
+
* **Details**
|
|
102
|
+
*
|
|
103
|
+
* The collection runs in a fiber of the current scope, so the returned effect
|
|
104
|
+
* can be awaited after the writes that produce the changes. Open the watch
|
|
105
|
+
* stream before writing: a volume publishes only changes committed after the
|
|
106
|
+
* subscription.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* ```ts
|
|
110
|
+
* import * as BunCrypto from "@effect/platform-bun/BunCrypto"
|
|
111
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
112
|
+
* import * as Testing from "@effect-vfs/core/Testing"
|
|
113
|
+
* import { Effect } from "effect"
|
|
114
|
+
*
|
|
115
|
+
* const program = Effect.gen(function*() {
|
|
116
|
+
* const volume = yield* Vfs.Volume
|
|
117
|
+
* const changes = yield* Testing.collectChanges(yield* volume.watch(), 1)
|
|
118
|
+
*
|
|
119
|
+
* yield* (yield* Vfs.Caller).mkdir("/work")
|
|
120
|
+
*
|
|
121
|
+
* return (yield* changes).map((change) => change._tag)
|
|
122
|
+
* })
|
|
123
|
+
*
|
|
124
|
+
* Effect.runPromise(program.pipe(Effect.scoped, Effect.provide(Testing.layer()), Effect.provide(BunCrypto.layer))).then(console.log)
|
|
125
|
+
* // [ 'Create' ]
|
|
126
|
+
* ```
|
|
127
|
+
*
|
|
128
|
+
* @category combinators
|
|
129
|
+
* @since 0.6.0
|
|
130
|
+
*/
|
|
131
|
+
export declare const collectChanges: <A, E, R>(stream: Stream.Stream<A, E, R>, n: number) => Effect.Effect<Effect.Effect<Array<A>, E>, never, R | Scope.Scope>;
|
|
132
|
+
//# sourceMappingURL=Testing.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"Testing.d.ts","sourceRoot":"","sources":["../src/Testing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAA;AAC5C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,KAAK,MAAM,cAAc,CAAA;AACrC,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAC9D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC3C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAC7C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAA;AACvD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAEhD;;;;;GAKG;AACH,MAAM,WAAW,YAAY;IAC3B,2EAA2E;IAC3E,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B,yCAAyC;IACzC,QAAQ,CAAC,MAAM,CAAC,EAAE,aAAa,CAAA;IAC/B,mEAAmE;IACnE,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,CAAA;CACpC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,eAAO,MAAM,KAAK,aAAc,YAAY,KAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,EAAE,MAAM,CAAC,MAAM,CAOhG,CAAA;AAEH;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,QAAQ,aACT,QAAQ,YACR,IAAI,CAAC,iBAAiB,EAAE,UAAU,CAAC,KAC5C,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,QAAQ,EAAE,MAAM,CACoC,CAAA;AAE7E;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,cAAc,GAAI,CAAC,EAAE,CAAC,EAAE,CAAC,UAC5B,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAC3B,MAAM,KACR,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,KAAK,CAAC,KAAK,CAIhE,CAAA"}
|
package/dist/Testing.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import * as Effect from "effect/Effect";
|
|
2
|
+
import * as Fiber from "effect/Fiber";
|
|
3
|
+
import * as Layer from "effect/Layer";
|
|
4
|
+
import * as Stream from "effect/Stream";
|
|
5
|
+
import { Caller, Volume } from "./VirtualFileSystem.js";
|
|
6
|
+
/**
|
|
7
|
+
* A fresh volume and a root caller on it, as one layer.
|
|
8
|
+
*
|
|
9
|
+
* **Details**
|
|
10
|
+
*
|
|
11
|
+
* Every build constructs a new volume, from `fixture` when one is given. The
|
|
12
|
+
* layer fails with the `VfsError` the constructor or the caller reports for
|
|
13
|
+
* invalid options. Provide `Crypto.Crypto` to build the volume.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* import * as BunCrypto from "@effect/platform-bun/BunCrypto"
|
|
18
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
19
|
+
* import * as Testing from "@effect-vfs/core/Testing"
|
|
20
|
+
* import { Effect } from "effect"
|
|
21
|
+
*
|
|
22
|
+
* const program = Effect.gen(function*() {
|
|
23
|
+
* const fs = yield* Vfs.Caller
|
|
24
|
+
* yield* fs.mkdir("/work")
|
|
25
|
+
*
|
|
26
|
+
* return (yield* fs.readDirectory("/")).value.map((entry) => new TextDecoder().decode(entry.name))
|
|
27
|
+
* })
|
|
28
|
+
*
|
|
29
|
+
* // Each provide builds its own volume, so neither run sees the other's writes.
|
|
30
|
+
* Effect.runPromise(program.pipe(Effect.provide(Testing.layer()), Effect.provide(BunCrypto.layer))).then(console.log)
|
|
31
|
+
* // [ 'work' ]
|
|
32
|
+
* ```
|
|
33
|
+
*
|
|
34
|
+
* @category layers
|
|
35
|
+
* @since 0.6.0
|
|
36
|
+
*/
|
|
37
|
+
export const layer = (options) => Caller.layer(options?.caller).pipe(Layer.provideMerge(options?.fixture === undefined
|
|
38
|
+
? Volume.layer(options?.volume)
|
|
39
|
+
: Volume.layerFromFixture(options.fixture, options.volume)));
|
|
40
|
+
/**
|
|
41
|
+
* A root caller with `identity` on the volume in context.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```ts
|
|
45
|
+
* import * as BunCrypto from "@effect/platform-bun/BunCrypto"
|
|
46
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
47
|
+
* import * as Testing from "@effect-vfs/core/Testing"
|
|
48
|
+
* import { Effect } from "effect"
|
|
49
|
+
*
|
|
50
|
+
* const program = Effect.gen(function*() {
|
|
51
|
+
* yield* (yield* Vfs.Caller).mkdir("/private", { mode: 0o700 })
|
|
52
|
+
*
|
|
53
|
+
* const guest = yield* Testing.callerAs({ uid: 1000, gid: 1000, groups: [], privileged: false })
|
|
54
|
+
*
|
|
55
|
+
* return (yield* Effect.flip(guest.readDirectory("/private"))).code
|
|
56
|
+
* })
|
|
57
|
+
*
|
|
58
|
+
* Effect.runPromise(program.pipe(Effect.provide(Testing.layer()), Effect.provide(BunCrypto.layer))).then(console.log)
|
|
59
|
+
* // AccessDenied
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @category constructors
|
|
63
|
+
* @since 0.6.0
|
|
64
|
+
*/
|
|
65
|
+
export const callerAs = (identity, options) => Effect.flatMap(Volume, (volume) => volume.caller({ ...options, identity }));
|
|
66
|
+
/**
|
|
67
|
+
* Starts collecting the first `n` elements of `stream` and returns an effect
|
|
68
|
+
* that waits for them.
|
|
69
|
+
*
|
|
70
|
+
* **Details**
|
|
71
|
+
*
|
|
72
|
+
* The collection runs in a fiber of the current scope, so the returned effect
|
|
73
|
+
* can be awaited after the writes that produce the changes. Open the watch
|
|
74
|
+
* stream before writing: a volume publishes only changes committed after the
|
|
75
|
+
* subscription.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```ts
|
|
79
|
+
* import * as BunCrypto from "@effect/platform-bun/BunCrypto"
|
|
80
|
+
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
81
|
+
* import * as Testing from "@effect-vfs/core/Testing"
|
|
82
|
+
* import { Effect } from "effect"
|
|
83
|
+
*
|
|
84
|
+
* const program = Effect.gen(function*() {
|
|
85
|
+
* const volume = yield* Vfs.Volume
|
|
86
|
+
* const changes = yield* Testing.collectChanges(yield* volume.watch(), 1)
|
|
87
|
+
*
|
|
88
|
+
* yield* (yield* Vfs.Caller).mkdir("/work")
|
|
89
|
+
*
|
|
90
|
+
* return (yield* changes).map((change) => change._tag)
|
|
91
|
+
* })
|
|
92
|
+
*
|
|
93
|
+
* Effect.runPromise(program.pipe(Effect.scoped, Effect.provide(Testing.layer()), Effect.provide(BunCrypto.layer))).then(console.log)
|
|
94
|
+
* // [ 'Create' ]
|
|
95
|
+
* ```
|
|
96
|
+
*
|
|
97
|
+
* @category combinators
|
|
98
|
+
* @since 0.6.0
|
|
99
|
+
*/
|
|
100
|
+
export const collectChanges = (stream, n) => Stream.runCollect(Stream.take(stream, n)).pipe(Effect.forkScoped({ startImmediately: true }), Effect.map(Fiber.join));
|