@effect-vfs/core 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (130) hide show
  1. package/README.md +34 -31
  2. package/dist/BytePath.d.ts +114 -4
  3. package/dist/BytePath.d.ts.map +1 -1
  4. package/dist/BytePath.js +155 -0
  5. package/dist/Caller.d.ts +391 -0
  6. package/dist/Caller.d.ts.map +1 -0
  7. package/dist/Caller.js +199 -0
  8. package/dist/FileHandle.d.ts +50 -0
  9. package/dist/FileHandle.d.ts.map +1 -0
  10. package/dist/FileHandle.js +28 -0
  11. package/dist/Fixture.d.ts +155 -0
  12. package/dist/Fixture.d.ts.map +1 -0
  13. package/dist/Fixture.js +65 -0
  14. package/dist/LiveVolume.d.ts +11 -21
  15. package/dist/LiveVolume.d.ts.map +1 -1
  16. package/dist/LiveVolume.js +17 -14
  17. package/dist/Metadata.d.ts +186 -0
  18. package/dist/Metadata.d.ts.map +1 -0
  19. package/dist/Metadata.js +125 -0
  20. package/dist/Snapshot.d.ts +31 -53
  21. package/dist/Snapshot.d.ts.map +1 -1
  22. package/dist/Snapshot.js +26 -49
  23. package/dist/SnapshotDelta.d.ts +24 -39
  24. package/dist/SnapshotDelta.d.ts.map +1 -1
  25. package/dist/SnapshotDelta.js +14 -35
  26. package/dist/Target.d.ts +225 -0
  27. package/dist/Target.d.ts.map +1 -0
  28. package/dist/Target.js +92 -0
  29. package/dist/Testing.d.ts +128 -0
  30. package/dist/Testing.d.ts.map +1 -0
  31. package/dist/Testing.js +111 -0
  32. package/dist/VfsError.d.ts +199 -0
  33. package/dist/VfsError.d.ts.map +1 -0
  34. package/dist/VfsError.js +164 -0
  35. package/dist/VirtualFileSystem.d.ts +628 -347
  36. package/dist/VirtualFileSystem.d.ts.map +1 -1
  37. package/dist/VirtualFileSystem.js +448 -115
  38. package/dist/Volume.d.ts +211 -0
  39. package/dist/Volume.d.ts.map +1 -0
  40. package/dist/Volume.js +172 -0
  41. package/dist/Watch.d.ts +68 -0
  42. package/dist/Watch.d.ts.map +1 -0
  43. package/dist/Watch.js +46 -0
  44. package/dist/index.d.ts +56 -1
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +56 -1
  47. package/dist/internal/budget.d.ts +90 -0
  48. package/dist/internal/budget.d.ts.map +1 -0
  49. package/dist/internal/budget.js +70 -0
  50. package/dist/internal/bytePath.d.ts +2 -0
  51. package/dist/internal/bytePath.d.ts.map +1 -1
  52. package/dist/internal/bytePath.js +3 -2
  53. package/dist/internal/bytes.d.ts +3 -0
  54. package/dist/internal/bytes.d.ts.map +1 -1
  55. package/dist/internal/bytes.js +4 -2
  56. package/dist/internal/canonicalBase64.d.ts +1 -4
  57. package/dist/internal/canonicalBase64.d.ts.map +1 -1
  58. package/dist/internal/canonicalBase64.js +9 -13
  59. package/dist/internal/errors.d.ts +47 -5
  60. package/dist/internal/errors.d.ts.map +1 -1
  61. package/dist/internal/errors.js +80 -36
  62. package/dist/internal/fixture.d.ts +2 -2
  63. package/dist/internal/fixture.d.ts.map +1 -1
  64. package/dist/internal/fixture.js +57 -74
  65. package/dist/internal/hex128.d.ts +12 -0
  66. package/dist/internal/hex128.d.ts.map +1 -0
  67. package/dist/internal/hex128.js +7 -0
  68. package/dist/internal/hmac.d.ts +5 -0
  69. package/dist/internal/hmac.d.ts.map +1 -0
  70. package/dist/internal/hmac.js +150 -0
  71. package/dist/internal/image.d.ts +14 -117
  72. package/dist/internal/image.d.ts.map +1 -1
  73. package/dist/internal/image.js +137 -167
  74. package/dist/internal/inodeTable.d.ts +20 -0
  75. package/dist/internal/inodeTable.d.ts.map +1 -0
  76. package/dist/internal/inodeTable.js +48 -0
  77. package/dist/internal/lines.d.ts +36 -0
  78. package/dist/internal/lines.d.ts.map +1 -0
  79. package/dist/internal/lines.js +115 -0
  80. package/dist/internal/liveImage.d.ts +19 -278
  81. package/dist/internal/liveImage.d.ts.map +1 -1
  82. package/dist/internal/liveImage.js +75 -166
  83. package/dist/internal/merkle.d.ts +45 -0
  84. package/dist/internal/merkle.d.ts.map +1 -0
  85. package/dist/internal/merkle.js +179 -0
  86. package/dist/internal/metadata.d.ts +2 -13
  87. package/dist/internal/metadata.d.ts.map +1 -1
  88. package/dist/internal/metadata.js +5 -15
  89. package/dist/internal/overlayDiff.d.ts +2 -2
  90. package/dist/internal/overlayDiff.d.ts.map +1 -1
  91. package/dist/internal/overlayDiff.js +0 -3
  92. package/dist/internal/path.d.ts +17 -5
  93. package/dist/internal/path.d.ts.map +1 -1
  94. package/dist/internal/path.js +38 -24
  95. package/dist/internal/snapshotDelta.d.ts +61 -60
  96. package/dist/internal/snapshotDelta.d.ts.map +1 -1
  97. package/dist/internal/snapshotDelta.js +612 -504
  98. package/dist/internal/snapshotDeltaModel.d.ts.map +1 -1
  99. package/dist/internal/snapshotDeltaModel.js +0 -1
  100. package/dist/internal/stagedState.d.ts +11 -15
  101. package/dist/internal/stagedState.d.ts.map +1 -1
  102. package/dist/internal/stagedState.js +15 -59
  103. package/dist/internal/testSeams.d.ts +17 -0
  104. package/dist/internal/testSeams.d.ts.map +1 -0
  105. package/dist/internal/testSeams.js +20 -0
  106. package/dist/internal/tree.d.ts +225 -0
  107. package/dist/internal/tree.d.ts.map +1 -0
  108. package/dist/internal/tree.js +385 -0
  109. package/dist/internal/turnstile.d.ts +8 -0
  110. package/dist/internal/turnstile.d.ts.map +1 -0
  111. package/dist/internal/turnstile.js +43 -0
  112. package/dist/internal/virtualFileSystem.d.ts +39 -489
  113. package/dist/internal/virtualFileSystem.d.ts.map +1 -1
  114. package/dist/internal/virtualFileSystem.js +2112 -2465
  115. package/dist/internal/volumeState.d.ts +110 -0
  116. package/dist/internal/volumeState.d.ts.map +1 -0
  117. package/dist/internal/volumeState.js +204 -0
  118. package/dist/internal/watchHub.d.ts +15 -6
  119. package/dist/internal/watchHub.d.ts.map +1 -1
  120. package/dist/internal/watchHub.js +56 -42
  121. package/package.json +48 -4
  122. package/dist/VirtualFileSystemError.d.ts +0 -54
  123. package/dist/VirtualFileSystemError.d.ts.map +0 -1
  124. package/dist/VirtualFileSystemError.js +0 -49
  125. package/dist/internal/overlayContent.d.ts +0 -15
  126. package/dist/internal/overlayContent.d.ts.map +0 -1
  127. package/dist/internal/overlayContent.js +0 -29
  128. package/dist/internal/testHooks.d.ts +0 -20
  129. package/dist/internal/testHooks.d.ts.map +0 -1
  130. package/dist/internal/testHooks.js +0 -23
@@ -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,128 @@
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 * as Effect from "effect/Effect";
16
+ import * as Layer from "effect/Layer";
17
+ import type * as Scope from "effect/Scope";
18
+ import * as Stream from "effect/Stream";
19
+ import type { Identity, RootCallerOptions } from "./Caller.js";
20
+ import type { Fixture } from "./Fixture.js";
21
+ import type { VfsError } from "./VfsError.js";
22
+ import { Caller, Volume } from "./VirtualFileSystem.js";
23
+ import type { VolumeOptions } from "./Volume.js";
24
+ /**
25
+ * Options of a test volume layer.
26
+ *
27
+ * @category models
28
+ * @since 0.6.0
29
+ */
30
+ export interface LayerOptions {
31
+ /** Entries the volume starts with; without one the volume starts empty. */
32
+ readonly fixture?: Fixture;
33
+ /** Limits and identity of the volume. */
34
+ readonly volume?: VolumeOptions;
35
+ /** Credentials and umask of the root caller the layer provides. */
36
+ readonly caller?: RootCallerOptions;
37
+ }
38
+ /**
39
+ * A fresh volume and a root caller on it, as one layer.
40
+ *
41
+ * **Details**
42
+ *
43
+ * Every build constructs a new volume, from `fixture` when one is given. The
44
+ * layer fails with the `VfsError` the constructor or the caller reports for
45
+ * invalid options.
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
50
+ * import * as Testing from "@effect-vfs/core/Testing"
51
+ * import { Effect } from "effect"
52
+ *
53
+ * const program = Effect.gen(function*() {
54
+ * const fs = yield* Vfs.Caller
55
+ * yield* fs.mkdir("/work")
56
+ *
57
+ * return (yield* fs.readDirectory("/")).value.map((entry) => new TextDecoder().decode(entry.name))
58
+ * })
59
+ *
60
+ * // Each provide builds its own volume, so neither run sees the other's writes.
61
+ * Effect.runPromise(program.pipe(Effect.provide(Testing.layer()))).then(console.log)
62
+ * // [ 'work' ]
63
+ * ```
64
+ *
65
+ * @category layers
66
+ * @since 0.6.0
67
+ */
68
+ export declare const layer: (options?: LayerOptions) => Layer.Layer<Volume | Caller, VfsError>;
69
+ /**
70
+ * A root caller with `identity` on the volume in context.
71
+ *
72
+ * @example
73
+ * ```ts
74
+ * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
75
+ * import * as Testing from "@effect-vfs/core/Testing"
76
+ * import { Effect } from "effect"
77
+ *
78
+ * const program = Effect.gen(function*() {
79
+ * yield* (yield* Vfs.Caller).mkdir("/private", { mode: 0o700 })
80
+ *
81
+ * const guest = yield* Testing.callerAs({ uid: 1000, gid: 1000, groups: [], privileged: false })
82
+ *
83
+ * return (yield* Effect.flip(guest.readDirectory("/private"))).code
84
+ * })
85
+ *
86
+ * Effect.runPromise(program.pipe(Effect.provide(Testing.layer()))).then(console.log)
87
+ * // AccessDenied
88
+ * ```
89
+ *
90
+ * @category constructors
91
+ * @since 0.6.0
92
+ */
93
+ export declare const callerAs: (identity: Identity, options?: Omit<RootCallerOptions, "identity">) => Effect.Effect<Caller, VfsError, Volume>;
94
+ /**
95
+ * Starts collecting the first `n` elements of `stream` and returns an effect
96
+ * that waits for them.
97
+ *
98
+ * **Details**
99
+ *
100
+ * The collection runs in a fiber of the current scope, so the returned effect
101
+ * can be awaited after the writes that produce the changes. Open the watch
102
+ * stream before writing: a volume publishes only changes committed after the
103
+ * subscription.
104
+ *
105
+ * @example
106
+ * ```ts
107
+ * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
108
+ * import * as Testing from "@effect-vfs/core/Testing"
109
+ * import { Effect } from "effect"
110
+ *
111
+ * const program = Effect.gen(function*() {
112
+ * const volume = yield* Vfs.Volume
113
+ * const changes = yield* Testing.collectChanges(yield* volume.watch(), 1)
114
+ *
115
+ * yield* (yield* Vfs.Caller).mkdir("/work")
116
+ *
117
+ * return (yield* changes).map((change) => change._tag)
118
+ * })
119
+ *
120
+ * Effect.runPromise(program.pipe(Effect.scoped, Effect.provide(Testing.layer()))).then(console.log)
121
+ * // [ 'Create' ]
122
+ * ```
123
+ *
124
+ * @category combinators
125
+ * @since 0.6.0
126
+ */
127
+ 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>;
128
+ //# 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,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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,eAAO,MAAM,KAAK,aAAc,YAAY,KAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,MAAM,EAAE,QAAQ,CAOjF,CAAA;AAEH;;;;;;;;;;;;;;;;;;;;;;;GAuBG;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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;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"}
@@ -0,0 +1,111 @@
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 * as Effect from "effect/Effect";
16
+ import * as Fiber from "effect/Fiber";
17
+ import * as Layer from "effect/Layer";
18
+ import * as Stream from "effect/Stream";
19
+ import { Caller, Volume } from "./VirtualFileSystem.js";
20
+ /**
21
+ * A fresh volume and a root caller on it, as one layer.
22
+ *
23
+ * **Details**
24
+ *
25
+ * Every build constructs a new volume, from `fixture` when one is given. The
26
+ * layer fails with the `VfsError` the constructor or the caller reports for
27
+ * invalid options.
28
+ *
29
+ * @example
30
+ * ```ts
31
+ * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
32
+ * import * as Testing from "@effect-vfs/core/Testing"
33
+ * import { Effect } from "effect"
34
+ *
35
+ * const program = Effect.gen(function*() {
36
+ * const fs = yield* Vfs.Caller
37
+ * yield* fs.mkdir("/work")
38
+ *
39
+ * return (yield* fs.readDirectory("/")).value.map((entry) => new TextDecoder().decode(entry.name))
40
+ * })
41
+ *
42
+ * // Each provide builds its own volume, so neither run sees the other's writes.
43
+ * Effect.runPromise(program.pipe(Effect.provide(Testing.layer()))).then(console.log)
44
+ * // [ 'work' ]
45
+ * ```
46
+ *
47
+ * @category layers
48
+ * @since 0.6.0
49
+ */
50
+ export const layer = (options) => Caller.layer(options?.caller).pipe(Layer.provideMerge(options?.fixture === undefined
51
+ ? Volume.layer(options?.volume)
52
+ : Volume.layerFromFixture(options.fixture, options.volume)));
53
+ /**
54
+ * A root caller with `identity` on the volume in context.
55
+ *
56
+ * @example
57
+ * ```ts
58
+ * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
59
+ * import * as Testing from "@effect-vfs/core/Testing"
60
+ * import { Effect } from "effect"
61
+ *
62
+ * const program = Effect.gen(function*() {
63
+ * yield* (yield* Vfs.Caller).mkdir("/private", { mode: 0o700 })
64
+ *
65
+ * const guest = yield* Testing.callerAs({ uid: 1000, gid: 1000, groups: [], privileged: false })
66
+ *
67
+ * return (yield* Effect.flip(guest.readDirectory("/private"))).code
68
+ * })
69
+ *
70
+ * Effect.runPromise(program.pipe(Effect.provide(Testing.layer()))).then(console.log)
71
+ * // AccessDenied
72
+ * ```
73
+ *
74
+ * @category constructors
75
+ * @since 0.6.0
76
+ */
77
+ export const callerAs = (identity, options) => Effect.flatMap(Volume, (volume) => volume.caller({ ...options, identity }));
78
+ /**
79
+ * Starts collecting the first `n` elements of `stream` and returns an effect
80
+ * that waits for them.
81
+ *
82
+ * **Details**
83
+ *
84
+ * The collection runs in a fiber of the current scope, so the returned effect
85
+ * can be awaited after the writes that produce the changes. Open the watch
86
+ * stream before writing: a volume publishes only changes committed after the
87
+ * subscription.
88
+ *
89
+ * @example
90
+ * ```ts
91
+ * import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
92
+ * import * as Testing from "@effect-vfs/core/Testing"
93
+ * import { Effect } from "effect"
94
+ *
95
+ * const program = Effect.gen(function*() {
96
+ * const volume = yield* Vfs.Volume
97
+ * const changes = yield* Testing.collectChanges(yield* volume.watch(), 1)
98
+ *
99
+ * yield* (yield* Vfs.Caller).mkdir("/work")
100
+ *
101
+ * return (yield* changes).map((change) => change._tag)
102
+ * })
103
+ *
104
+ * Effect.runPromise(program.pipe(Effect.scoped, Effect.provide(Testing.layer()))).then(console.log)
105
+ * // [ 'Create' ]
106
+ * ```
107
+ *
108
+ * @category combinators
109
+ * @since 0.6.0
110
+ */
111
+ export const collectChanges = (stream, n) => Stream.runCollect(Stream.take(stream, n)).pipe(Effect.forkScoped({ startImmediately: true }), Effect.map(Fiber.join));