@effect-vfs/core 0.7.0 → 0.8.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 +15 -238
- package/dist/LiveVolume.d.ts.map +1 -1
- package/dist/LiveVolume.js +9 -9
- package/dist/Search.d.ts +730 -0
- package/dist/Search.d.ts.map +1 -0
- package/dist/Search.js +568 -0
- package/dist/SnapshotDelta.d.ts +234 -0
- package/dist/SnapshotDelta.d.ts.map +1 -1
- package/dist/SnapshotDelta.js +107 -0
- package/dist/VirtualFileSystem.d.ts +53 -1
- package/dist/VirtualFileSystem.d.ts.map +1 -1
- package/dist/VirtualFileSystem.js +59 -1
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/internal/canonicalBase64.js +3 -3
- package/dist/internal/fixture.js +3 -3
- package/dist/internal/glob.d.ts +66 -0
- package/dist/internal/glob.d.ts.map +1 -0
- package/dist/internal/glob.js +248 -0
- package/dist/internal/image.d.ts +7 -1
- package/dist/internal/image.d.ts.map +1 -1
- package/dist/internal/image.js +9 -7
- package/dist/internal/liveImage.d.ts +1 -1
- package/dist/internal/liveImage.d.ts.map +1 -1
- package/dist/internal/liveImage.js +2 -10
- package/dist/internal/overlayDiff.js +2 -2
- package/dist/internal/path.d.ts.map +1 -1
- package/dist/internal/path.js +3 -3
- package/dist/internal/resolution.d.ts +135 -0
- package/dist/internal/resolution.d.ts.map +1 -0
- package/dist/internal/resolution.js +277 -0
- package/dist/internal/search.d.ts +24 -0
- package/dist/internal/search.d.ts.map +1 -0
- package/dist/internal/search.js +78 -0
- package/dist/internal/searchContent.d.ts +77 -0
- package/dist/internal/searchContent.d.ts.map +1 -0
- package/dist/internal/searchContent.js +223 -0
- package/dist/internal/searchModel.d.ts +83 -0
- package/dist/internal/searchModel.d.ts.map +1 -0
- package/dist/internal/searchModel.js +23 -0
- package/dist/internal/snapshotDelta.d.ts +470 -1
- package/dist/internal/snapshotDelta.d.ts.map +1 -1
- package/dist/internal/snapshotDelta.js +60 -26
- package/dist/internal/snapshotMerge.d.ts +54 -0
- package/dist/internal/snapshotMerge.d.ts.map +1 -0
- package/dist/internal/snapshotMerge.js +959 -0
- package/dist/internal/tokenRegistry.d.ts +77 -0
- package/dist/internal/tokenRegistry.d.ts.map +1 -0
- package/dist/internal/tokenRegistry.js +128 -0
- package/dist/internal/tree.js +2 -2
- package/dist/internal/virtualFileSystem.d.ts +32 -17
- package/dist/internal/virtualFileSystem.d.ts.map +1 -1
- package/dist/internal/virtualFileSystem.js +640 -993
- package/dist/internal/volumeLimits.d.ts +53 -0
- package/dist/internal/volumeLimits.d.ts.map +1 -0
- package/dist/internal/volumeLimits.js +63 -0
- package/dist/internal/volumeState.d.ts +2 -0
- package/dist/internal/volumeState.d.ts.map +1 -1
- package/dist/internal/volumeState.js +9 -0
- package/package.json +10 -6
package/README.md
CHANGED
|
@@ -16,29 +16,17 @@ for the exact permission, path, timestamp, quota, and atomicity rules.
|
|
|
16
16
|
npm install @effect-vfs/core@latest
|
|
17
17
|
```
|
|
18
18
|
|
|
19
|
-
The package declares its exact Effect version as a peer dependency. Volume constructors and snapshot delta functions require Effect
|
|
19
|
+
The package declares its exact Effect version as a peer dependency. Volume constructors and snapshot delta functions require Effect's `Crypto` service. On Node, install the matching
|
|
20
20
|
provider and supply `NodeCrypto.layer` when running these effects:
|
|
21
21
|
|
|
22
22
|
```sh
|
|
23
23
|
npm install "@effect/platform-node-shared@$(npm view @effect-vfs/core peerDependencies.effect)"
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
-
## The mental model
|
|
27
|
-
|
|
28
|
-
The API has three levels:
|
|
29
|
-
|
|
30
|
-
- A `Volume` owns one isolated namespace and its file contents.
|
|
31
|
-
- A `Caller` accesses that volume with its own identity, umask, and current directory.
|
|
32
|
-
- File and directory handles are scoped capabilities. Effect closes them when their scope ends.
|
|
33
|
-
|
|
34
|
-
Callers created from the same volume see the same files. A new volume starts with independent state. This separation
|
|
35
|
-
lets you model access by several users without reaching for process globals or the host filesystem.
|
|
36
|
-
|
|
37
26
|
## Create an isolated workspace
|
|
38
27
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
lifetime are explicit values that can be composed in one Effect program.
|
|
28
|
+
A volume owns the files. A caller supplies identity, permissions, and a working directory. Scoped handles close when
|
|
29
|
+
their Effect scope ends. This example gives an unprivileged caller access to one directory in a bounded volume.
|
|
42
30
|
|
|
43
31
|
```ts
|
|
44
32
|
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
@@ -89,230 +77,19 @@ callers default to umask `0o022`; the developer's `0o027` mask turns the request
|
|
|
89
77
|
The root package exports `VirtualFileSystem` as a namespace. The equivalent direct module import is
|
|
90
78
|
`import * as Vfs from "@effect-vfs/core/VirtualFileSystem"`.
|
|
91
79
|
|
|
92
|
-
##
|
|
93
|
-
|
|
94
|
-
JavaScript strings cannot represent every filename allowed by a byte-oriented filesystem. `BytePath` keeps arbitrary
|
|
95
|
-
non-NUL path bytes intact. This matters when reproducing archives, protocol fixtures, or Unix directory trees that
|
|
96
|
-
contain names which are not valid UTF-8.
|
|
97
|
-
|
|
98
|
-
```ts
|
|
99
|
-
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
100
|
-
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
101
|
-
import { Effect } from "effect"
|
|
102
|
-
|
|
103
|
-
const program = Effect.gen(function*() {
|
|
104
|
-
const volume = yield* Vfs.make()
|
|
105
|
-
const fs = yield* volume.caller()
|
|
106
|
-
|
|
107
|
-
// Absolute path whose final component is the single byte 0xff.
|
|
108
|
-
const opaquePath = yield* Vfs.pathFromBytes(new Uint8Array([0x2f, 0xff]))
|
|
109
|
-
yield* fs.writeFile(opaquePath, new Uint8Array([1, 2, 3]), {
|
|
110
|
-
access: "write",
|
|
111
|
-
create: "exclusive"
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
const listing = yield* fs.readDirectory("/")
|
|
115
|
-
const roundTrip = yield* Vfs.pathToBytes(opaquePath)
|
|
116
|
-
return { names: listing.value.map((entry) => Array.from(entry.name)), roundTrip: Array.from(roundTrip) }
|
|
117
|
-
})
|
|
118
|
-
|
|
119
|
-
console.log(await Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))))
|
|
120
|
-
// { names: [[255]], roundTrip: [47, 255] }
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
The constructors and byte-returning operations copy their buffers, so later mutation cannot change stored paths.
|
|
124
|
-
Directory listings, symbolic-link targets, and resolved paths come back as bytes; decoding them to text is the caller's
|
|
125
|
-
choice, and a strict decoder reports a name that is not valid UTF-8.
|
|
126
|
-
|
|
127
|
-
## Build fixtures and restore snapshots
|
|
128
|
-
|
|
129
|
-
Fixtures make tests deterministic without a setup sequence. Snapshots let you capture that prepared state, serialize
|
|
130
|
-
it, and create independent workspaces from the same image. This is useful for test isolation, preview environments, and
|
|
131
|
-
resettable sandboxes.
|
|
132
|
-
|
|
133
|
-
```ts
|
|
134
|
-
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
135
|
-
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
136
|
-
import { Effect } from "effect"
|
|
137
|
-
import * as ByteSize from "effect/ByteSize"
|
|
138
|
-
|
|
139
|
-
const decodeLimits = {
|
|
140
|
-
maxEncodedBytes: ByteSize.megabytes(1),
|
|
141
|
-
maxRecords: 1_000,
|
|
142
|
-
maxEntries: 1_000,
|
|
143
|
-
maxDecodedBytes: ByteSize.megabytes(1)
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const program = Effect.gen(function*() {
|
|
147
|
-
const template = yield* Vfs.fromFixture({
|
|
148
|
-
entries: [
|
|
149
|
-
{ kind: "directory", path: "/project" },
|
|
150
|
-
{
|
|
151
|
-
kind: "file",
|
|
152
|
-
path: "/project/settings.json",
|
|
153
|
-
bytes: new TextEncoder().encode("{\"theme\":\"dark\"}")
|
|
154
|
-
}
|
|
155
|
-
]
|
|
156
|
-
})
|
|
157
|
-
|
|
158
|
-
const encoded = yield* Vfs.encodeSnapshot(yield* template.snapshot)
|
|
159
|
-
const snapshot = yield* Vfs.decodeSnapshot(encoded, decodeLimits)
|
|
160
|
-
const workspaceA = yield* Vfs.fromSnapshot(snapshot)
|
|
161
|
-
const workspaceB = yield* Vfs.fromSnapshot(snapshot)
|
|
162
|
-
const a = yield* workspaceA.caller()
|
|
163
|
-
const b = yield* workspaceB.caller()
|
|
164
|
-
|
|
165
|
-
yield* a.writeFile("/project/settings.json", new TextEncoder().encode("{\"theme\":\"light\"}"), {
|
|
166
|
-
access: "write",
|
|
167
|
-
truncate: true
|
|
168
|
-
})
|
|
169
|
-
|
|
170
|
-
return new TextDecoder().decode(yield* b.readFile("/project/settings.json"))
|
|
171
|
-
})
|
|
172
|
-
|
|
173
|
-
console.log(await Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer)))) // {"theme":"dark"}
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
Fixture paths must be absolute and unique, and parent directories must be listed explicitly. Fixtures can also contain
|
|
177
|
-
symbolic links, metadata, and forward hard links.
|
|
178
|
-
|
|
179
|
-
Snapshot decoding requires explicit work limits because encoded bytes may come from an untrusted source. A snapshot
|
|
180
|
-
contains the reachable namespace and metadata. It excludes callers, open handles, cursor positions, watch
|
|
181
|
-
subscriptions, and unlinked content. Each restored volume is independent.
|
|
182
|
-
|
|
183
|
-
## Overlays and snapshot deltas
|
|
184
|
-
|
|
185
|
-
An overlay creates a writable workspace from an immutable snapshot. Untouched file bytes remain shared until a
|
|
186
|
-
workspace edits them. See the [overlay guide](../../apps/docs/app/content/guides/overlay-filesystems.mdx) for the
|
|
187
|
-
workflow and conflict rules.
|
|
188
|
-
|
|
189
|
-
Snapshot deltas encode exact changes between compatible images. They have separate decode and application limits;
|
|
190
|
-
check those limits before accepting an untrusted delta. See the
|
|
191
|
-
[snapshot delta API](../../apps/docs/app/content/api/core/snapshot-delta.mdx) for operations and defaults.
|
|
192
|
-
|
|
193
|
-
## Use scoped handles for incremental I/O
|
|
194
|
-
|
|
195
|
-
Whole-file operations are convenient, but handles give each open file an independent `bigint` cursor and support
|
|
196
|
-
incremental reads, positional I/O, seeking, and truncation. `Effect.scoped` guarantees cleanup on success, failure, or
|
|
197
|
-
interruption.
|
|
198
|
-
|
|
199
|
-
```ts
|
|
200
|
-
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
201
|
-
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
202
|
-
import { Effect } from "effect"
|
|
203
|
-
|
|
204
|
-
const program = Effect.scoped(Effect.gen(function*() {
|
|
205
|
-
const fs = yield* (yield* Vfs.make()).caller()
|
|
206
|
-
yield* fs.writeFile("/events.log", new TextEncoder().encode("one\ntwo\n"), {
|
|
207
|
-
access: "write",
|
|
208
|
-
create: "exclusive"
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
const file = yield* fs.open("/events.log", { access: "read" })
|
|
212
|
-
const first = yield* file.read(4) // Advances this handle's cursor.
|
|
213
|
-
const second = yield* file.read(4)
|
|
214
|
-
const preview = yield* file.pread(3, 0n) // Does not move the cursor.
|
|
215
|
-
|
|
216
|
-
const decode = (bytes: Uint8Array) => new TextDecoder().decode(bytes)
|
|
217
|
-
|
|
218
|
-
// A positioned read also says whether it reached the end of the file.
|
|
219
|
-
return [decode(first), decode(second), decode(preview.bytes), preview.eof]
|
|
220
|
-
}))
|
|
221
|
-
|
|
222
|
-
console.log(await Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer)))) // ["one\n", "two\n", "one", false]
|
|
223
|
-
```
|
|
224
|
-
|
|
225
|
-
Handles also expose an explicit `close` effect when early release matters. Calling explicit close twice fails, while
|
|
226
|
-
scope cleanup remains safe after an explicit close.
|
|
227
|
-
|
|
228
|
-
## Handle expected failures as data
|
|
229
|
-
|
|
230
|
-
Every failure is a `VfsError` with a stable `code` and the `operation` that failed. A rejected option names its
|
|
231
|
-
`field`, a path-addressed failure names its `path` as bytes, and a classified failure keeps its `cause`. Codec and
|
|
232
|
-
store failures share the class with their own codes. Interruption and defects remain separate from these expected
|
|
233
|
-
failures.
|
|
234
|
-
|
|
235
|
-
```ts
|
|
236
|
-
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
237
|
-
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
238
|
-
import { Effect } from "effect"
|
|
239
|
-
|
|
240
|
-
const program = Effect.gen(function*() {
|
|
241
|
-
const fs = yield* (yield* Vfs.make()).caller()
|
|
242
|
-
|
|
243
|
-
return yield* fs.readFile("/optional.json").pipe(
|
|
244
|
-
Effect.catchTag("VfsError", (error) =>
|
|
245
|
-
error.code === "NotFound"
|
|
246
|
-
? Effect.succeed(new TextEncoder().encode("{}"))
|
|
247
|
-
: Effect.fail(error))
|
|
248
|
-
)
|
|
249
|
-
})
|
|
250
|
-
|
|
251
|
-
const bytes = await Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer)))
|
|
252
|
-
console.log(new TextDecoder().decode(bytes)) // {}
|
|
253
|
-
```
|
|
254
|
-
|
|
255
|
-
## Provide a volume and a caller as Effect services
|
|
256
|
-
|
|
257
|
-
`Volume` and `Caller` are services with static layers. `Volume.layer()` builds a fresh empty volume,
|
|
258
|
-
`Volume.layerFromSnapshot`, `Volume.layerFromFixture` and `Volume.layerOverlay` build one from existing state, and
|
|
259
|
-
`Volume.layerLive` opens a durable one through the `LiveImageStore` in context. `Caller.layer(options)` makes a root
|
|
260
|
-
caller on the volume in context. A caller supplied through the service keeps its original volume, identity, umask and
|
|
261
|
-
working directory.
|
|
262
|
-
|
|
263
|
-
```ts
|
|
264
|
-
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
265
|
-
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
266
|
-
import { Effect } from "effect"
|
|
267
|
-
|
|
268
|
-
const loadConfig = Effect.gen(function*() {
|
|
269
|
-
const fs = yield* Vfs.Caller
|
|
270
|
-
return yield* fs.readFile("/app/config.json")
|
|
271
|
-
})
|
|
272
|
-
|
|
273
|
-
const program = Effect.gen(function*() {
|
|
274
|
-
const fs = yield* Vfs.Caller
|
|
275
|
-
yield* fs.mkdir("/app")
|
|
276
|
-
yield* fs.writeFile("/app/config.json", new TextEncoder().encode("{}"), {
|
|
277
|
-
access: "write",
|
|
278
|
-
create: "exclusive"
|
|
279
|
-
})
|
|
280
|
-
return yield* loadConfig
|
|
281
|
-
})
|
|
282
|
-
|
|
283
|
-
const bytes = await Effect.runPromise(
|
|
284
|
-
program.pipe(Effect.provide(Vfs.Caller.layer()), Effect.provide(Vfs.Volume.layer()), Effect.provide(NodeCrypto.layer))
|
|
285
|
-
)
|
|
286
|
-
console.log(new TextDecoder().decode(bytes)) // {}
|
|
287
|
-
```
|
|
288
|
-
|
|
289
|
-
## When to use core or memory
|
|
290
|
-
|
|
291
|
-
Choose `@effect-vfs/core` when you need direct access to volumes, callers, credentials, byte paths, quotas, watches,
|
|
292
|
-
fixtures, or snapshots. Choose `@effect-vfs/memory` when existing code expects Effect's `FileSystem` service and you
|
|
293
|
-
want an in-memory implementation. The memory adapter is built on this core, so you can create a core volume and bind
|
|
294
|
-
the adapter to it when you need both interfaces.
|
|
80
|
+
## Guides and reference
|
|
295
81
|
|
|
296
|
-
|
|
82
|
+
- [Fixtures and snapshots](../../apps/docs/app/content/guides/fixtures-and-snapshots.mdx) covers seeded trees, codecs, and portable deltas.
|
|
83
|
+
- [Overlay workspaces](../../apps/docs/app/content/guides/overlay-filesystems.mdx) covers independent edits and `mergeSnapshotDeltas` conflicts.
|
|
84
|
+
- [Snapshot search](../../apps/docs/app/content/guides/snapshot-search.mdx) covers filename globs, content matches, and bounded results.
|
|
85
|
+
- [Filesystem model](../../apps/docs/app/content/concepts/filesystem-model.mdx) explains callers, handles, permissions, and byte paths.
|
|
86
|
+
- [API reference](../../apps/docs/app/content/api/core/virtual-file-system.mdx) lists constructors, services, and operations.
|
|
297
87
|
|
|
298
|
-
|
|
299
|
-
candidate.
|
|
88
|
+
## Limits
|
|
300
89
|
|
|
301
|
-
|
|
302
|
-
- Omitted logical quotas are unbounded by configuration, apart from fixed file and component bounds.
|
|
303
|
-
- Absolute paths ignore a supplied directory base. Relative paths can use a live, same-volume directory handle.
|
|
304
|
-
- Operations coordinate through one permit per volume. At most 65 operations are admitted at once by
|
|
305
|
-
default, including the active operation; set `maxPendingOperations` to change the waiting budget.
|
|
306
|
-
Excess work fails with retryable `VfsError` code `VolumeBusy`
|
|
307
|
-
before mutation or storage commit. Interruption while waiting makes no change; interruption after a commit
|
|
308
|
-
does not roll it back.
|
|
309
|
-
- Each watch subscriber retains at most 256 events by default; set `maxWatchEvents` to change this bound
|
|
310
|
-
(minimum 2). A subscriber that loses events receives `Rescan` with path `/` after its buffered events.
|
|
311
|
-
Rescan the volume, then continue consuming the watch; repeat the scan if another `Rescan` arrives.
|
|
312
|
-
Delivered events retain their order. The memory `FileSystem.watch` stream fails with a platform error
|
|
313
|
-
identified by `MemoryFileSystem.isWatchOverflow`. Open a new watch before rescanning its path so changes
|
|
314
|
-
during the scan remain observable.
|
|
315
|
-
- `sync` checks handle liveness. An in-memory volume provides no host or crash durability.
|
|
90
|
+
The package is experimental. Public APIs may change between minor releases.
|
|
316
91
|
|
|
317
|
-
|
|
318
|
-
|
|
92
|
+
Paths preserve arbitrary non-NUL bytes. Components are limited to 255 bytes, and symlink traversal to 40 links.
|
|
93
|
+
Volume options bound entries, file sizes, pending operations, and watch queues. In-memory volumes provide no host or
|
|
94
|
+
crash durability. See [compatibility and limits](../../apps/docs/app/content/reference/compatibility-and-limits.mdx)
|
|
95
|
+
for the complete contract.
|
package/dist/LiveVolume.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveVolume.d.ts","sourceRoot":"","sources":["../src/LiveVolume.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAA;AAC5C,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"LiveVolume.d.ts","sourceRoot":"","sources":["../src/LiveVolume.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAA;AACxC,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAA;AAC5C,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;AAI1C,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,YAAY,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AACjF,OAAO,KAAK,EAAE,MAAM,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAErF;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,CAAA;;IAW9D,4EAA4E;0BACtD,gBAAgB;2BACf,CAAC,OAAO,EAAE,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,YAAY,GAAG,eAAe,CAAC;qBACxF,CAAC,KAAK,EAAE,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC;;AAZtE;;;;;;;GAOG;AACH,qBAAa,cAAe,SAAQ,mBAKG;CAAG;AAE1C;;;;;GAKG;AACH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,aAAa,EAAE,QAAQ,CAAC,QAAQ,CAAA;IACzC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;CAC/B;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CACpD,MAAM,EACN,QAAQ,EACR,cAAc,GAAG,KAAK,CAAC,KAAK,GAAG,MAAM,CAAC,MAAM,CA4C5C,CAAA;AAEF;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;IACvB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;CACvC;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,iBAAiB,EAAE,CAAC,OAAO,CAAC,EAAE,aAAa,KAAK,MAAM,CAAC,MAAM,CACxE,UAAU,EACV,QAAQ,EACR,MAAM,CAAC,MAAM,CAKZ,CAAA;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,SAAS,EAAE,CACtB,KAAK,EAAE,UAAU,EACjB,aAAa,EAAE,QAAQ,CAAC,QAAQ,EAChC,MAAM,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,EAC3D,UAAU,CAAC,EAAE,gBAAgB,KAC1B,MAAM,CAAC,MAAM,CAChB,YAAY,EACZ,QAAQ,EACR,MAAM,CAAC,MAAM,CACU,CAAA"}
|
package/dist/LiveVolume.js
CHANGED
|
@@ -12,6 +12,7 @@ import { Context, Effect } from "effect";
|
|
|
12
12
|
import * as ByteSize from "effect/ByteSize";
|
|
13
13
|
import { argumentFailure, retargetFailure } from "./internal/errors.js";
|
|
14
14
|
import * as Model from "./internal/virtualFileSystem.js";
|
|
15
|
+
import * as Limits from "./internal/volumeLimits.js";
|
|
15
16
|
import { VfsError } from "./VfsError.js";
|
|
16
17
|
/**
|
|
17
18
|
* One exclusively owned store. Its Layer holds the storage resource until the
|
|
@@ -33,7 +34,11 @@ export class LiveImageStore extends Context.Service()("@effect-vfs/core/LiveImag
|
|
|
33
34
|
*/
|
|
34
35
|
export const open = Effect.fn("LiveVolume.open")(function* (options) {
|
|
35
36
|
const store = yield* LiveImageStore;
|
|
36
|
-
const initial = yield*
|
|
37
|
+
const { decoded, initial } = yield* Effect.gen(function* () {
|
|
38
|
+
const decoded = yield* Effect.fromResult(Limits.configuration(options.volume, "LiveVolume.open"));
|
|
39
|
+
const initial = yield* prepareEmptyImage(decoded.options);
|
|
40
|
+
return { decoded, initial };
|
|
41
|
+
}).pipe(
|
|
37
42
|
// Names the nested option that failed, such as `volume.maxEntries`.
|
|
38
43
|
Effect.mapError((cause) => {
|
|
39
44
|
const field = "field" in cause ? cause.field : undefined;
|
|
@@ -43,16 +48,11 @@ export const open = Effect.fn("LiveVolume.open")(function* (options) {
|
|
|
43
48
|
return yield* argumentFailure("LiveVolume.open", "maxImageBytes");
|
|
44
49
|
}
|
|
45
50
|
const image = yield* store.loadOrCreate(initial);
|
|
46
|
-
const
|
|
51
|
+
const requested = decoded.limits;
|
|
52
|
+
const session = yield* Model.openImageVolume(image, options.maxImageBytes, store.commit, store.durability ?? "memory-only", decoded.options).pipe(Effect.mapError((cause) => new VfsError({ code: "CorruptStore", operation: "LiveVolume.open", cause })));
|
|
47
53
|
yield* Effect.addFinalizer(() => session.shutdown);
|
|
48
54
|
const volume = session.volume;
|
|
49
|
-
if (volume.limits
|
|
50
|
-
volume.limits.maxPathBytes === undefined || options.volume.maxBytes === undefined ||
|
|
51
|
-
options.volume.maxFileBytes === undefined || options.volume.maxPathBytes === undefined ||
|
|
52
|
-
ByteSize.toBigInt(volume.limits.maxBytes) !== ByteSize.toBigInt(options.volume.maxBytes) ||
|
|
53
|
-
ByteSize.toBigInt(volume.limits.maxFileBytes) !== ByteSize.toBigInt(options.volume.maxFileBytes) ||
|
|
54
|
-
ByteSize.toBigInt(volume.limits.maxPathBytes) !== ByteSize.toBigInt(options.volume.maxPathBytes) ||
|
|
55
|
-
(options.volume.identity !== undefined && options.volume.identity !== volume.identity)) {
|
|
55
|
+
if (!Limits.compatible(volume.limits, requested, volume.identity, decoded.identity)) {
|
|
56
56
|
return yield* new VfsError({ code: "IncompatibleStore", operation: "LiveVolume.open" });
|
|
57
57
|
}
|
|
58
58
|
return volume;
|