@effect-vfs/core 0.3.1 → 0.5.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 +41 -150
- package/dist/BytePath.d.ts +2 -1
- package/dist/BytePath.d.ts.map +1 -1
- package/dist/LiveVolume.d.ts +104 -0
- package/dist/LiveVolume.d.ts.map +1 -0
- package/dist/LiveVolume.js +73 -0
- package/dist/Snapshot.d.ts +4 -2
- package/dist/Snapshot.d.ts.map +1 -1
- package/dist/Snapshot.js +4 -2
- package/dist/VirtualFileSystem.d.ts +333 -108
- package/dist/VirtualFileSystem.d.ts.map +1 -1
- package/dist/VirtualFileSystem.js +122 -32
- package/dist/VirtualFileSystemError.d.ts +54 -0
- package/dist/VirtualFileSystemError.d.ts.map +1 -0
- package/dist/VirtualFileSystemError.js +49 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +14 -0
- package/dist/internal/errors.d.ts +12 -0
- package/dist/internal/errors.d.ts.map +1 -0
- package/dist/internal/{virtualFileSystem/errors.js → errors.js} +11 -15
- package/dist/internal/{virtualFileSystem/fixture.d.ts → fixture.d.ts} +11 -8
- package/dist/internal/fixture.d.ts.map +1 -0
- package/dist/internal/{virtualFileSystem/fixture.js → fixture.js} +11 -5
- package/dist/internal/liveImage.d.ts +286 -0
- package/dist/internal/liveImage.d.ts.map +1 -0
- package/dist/internal/liveImage.js +176 -0
- package/dist/internal/{virtualFileSystem/overlayContent.d.ts → overlayContent.d.ts} +2 -2
- package/dist/internal/overlayContent.d.ts.map +1 -0
- package/dist/internal/{virtualFileSystem/overlayContent.js → overlayContent.js} +2 -2
- package/dist/internal/{virtualFileSystem/overlayDiff.d.ts → overlayDiff.d.ts} +1 -1
- package/dist/internal/overlayDiff.d.ts.map +1 -0
- package/dist/internal/{virtualFileSystem/overlayDiff.js → overlayDiff.js} +1 -1
- package/dist/internal/{virtualFileSystem/path.d.ts → path.d.ts} +2 -2
- package/dist/internal/path.d.ts.map +1 -0
- package/dist/internal/{virtualFileSystem/path.js → path.js} +1 -1
- package/dist/internal/stagedState.d.ts +22 -0
- package/dist/internal/stagedState.d.ts.map +1 -0
- package/dist/internal/stagedState.js +62 -0
- package/dist/internal/{virtualFileSystem/testHooks.d.ts → testHooks.d.ts} +2 -2
- package/dist/internal/testHooks.d.ts.map +1 -0
- package/dist/internal/virtualFileSystem.d.ts +224 -16
- package/dist/internal/virtualFileSystem.d.ts.map +1 -1
- package/dist/internal/virtualFileSystem.js +1294 -159
- package/dist/internal/{virtualFileSystem/watchHub.d.ts → watchHub.d.ts} +3 -3
- package/dist/internal/watchHub.d.ts.map +1 -0
- package/dist/internal/watchHub.js +54 -0
- package/package.json +14 -6
- package/dist/internal/virtualFileSystem/errors.d.ts +0 -31
- package/dist/internal/virtualFileSystem/errors.d.ts.map +0 -1
- package/dist/internal/virtualFileSystem/fixture.d.ts.map +0 -1
- package/dist/internal/virtualFileSystem/overlayContent.d.ts.map +0 -1
- package/dist/internal/virtualFileSystem/overlayDiff.d.ts.map +0 -1
- package/dist/internal/virtualFileSystem/path.d.ts.map +0 -1
- package/dist/internal/virtualFileSystem/testHooks.d.ts.map +0 -1
- package/dist/internal/virtualFileSystem/watchHub.d.ts.map +0 -1
- package/dist/internal/virtualFileSystem/watchHub.js +0 -33
- /package/dist/internal/{virtualFileSystem/testHooks.js → testHooks.js} +0 -0
package/README.md
CHANGED
|
@@ -1,13 +1,10 @@
|
|
|
1
1
|
# @effect-vfs/core
|
|
2
2
|
|
|
3
|
-
`@effect-vfs/core`
|
|
4
|
-
|
|
5
|
-
encoded snapshots without reading from or writing to the host filesystem.
|
|
3
|
+
`@effect-vfs/core` manages in-memory volumes with byte-preserving paths, callers with explicit permissions, quotas,
|
|
4
|
+
watches, fixtures, and portable snapshots. It does not access the host filesystem.
|
|
6
5
|
|
|
7
|
-
Use
|
|
8
|
-
|
|
9
|
-
Effect's standard `FileSystem` service, use [`@effect-vfs/memory`](https://www.npmjs.com/package/@effect-vfs/memory),
|
|
10
|
-
which adapts this package to that interface.
|
|
6
|
+
Use core when you need direct control over a shared volume or byte-oriented paths. For Effect's standard
|
|
7
|
+
`FileSystem` service, use [`@effect-vfs/memory`](../memory/README.md).
|
|
11
8
|
|
|
12
9
|
The package implements a documented subset of POSIX behavior. It does not claim full POSIX conformance. See the
|
|
13
10
|
[implemented profile](https://github.com/lloydrichards/effect-virtual-fs/blob/main/.okf/profiles/implemented-filesystem.md)
|
|
@@ -16,10 +13,15 @@ for the exact permission, path, timestamp, quota, and atomicity rules.
|
|
|
16
13
|
## Install
|
|
17
14
|
|
|
18
15
|
```sh
|
|
19
|
-
npm install @effect-vfs/core
|
|
16
|
+
npm install @effect-vfs/core@latest
|
|
20
17
|
```
|
|
21
18
|
|
|
22
|
-
|
|
19
|
+
The package declares its exact Effect version as a peer dependency. The examples also need a `Crypto` service. On Node
|
|
20
|
+
or Bun, install the matching provider and supply `NodeCrypto.layer`:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
npm install "@effect/platform-node-shared@$(npm view @effect-vfs/core peerDependencies.effect)"
|
|
24
|
+
```
|
|
23
25
|
|
|
24
26
|
## The mental model
|
|
25
27
|
|
|
@@ -40,6 +42,7 @@ lifetime are explicit values that can be composed in one Effect program.
|
|
|
40
42
|
|
|
41
43
|
```ts
|
|
42
44
|
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
45
|
+
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
43
46
|
import { ByteSize, Effect } from "effect"
|
|
44
47
|
|
|
45
48
|
const utf8 = new TextEncoder()
|
|
@@ -76,7 +79,7 @@ const program = Effect.scoped(Effect.gen(function*() {
|
|
|
76
79
|
}
|
|
77
80
|
}))
|
|
78
81
|
|
|
79
|
-
const result = await Effect.runPromise(program)
|
|
82
|
+
const result = await Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer)))
|
|
80
83
|
console.log(result) // { config: { feature: "preview" }, mode: "640" }
|
|
81
84
|
```
|
|
82
85
|
|
|
@@ -94,6 +97,7 @@ contain names which are not valid UTF-8.
|
|
|
94
97
|
|
|
95
98
|
```ts
|
|
96
99
|
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
100
|
+
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
97
101
|
import { Effect } from "effect"
|
|
98
102
|
|
|
99
103
|
const program = Effect.gen(function*() {
|
|
@@ -112,7 +116,7 @@ const program = Effect.gen(function*() {
|
|
|
112
116
|
return { names: names.map((name) => Array.from(name)), roundTrip: Array.from(roundTrip) }
|
|
113
117
|
})
|
|
114
118
|
|
|
115
|
-
console.log(await Effect.runPromise(program))
|
|
119
|
+
console.log(await Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))))
|
|
116
120
|
// { names: [[255]], roundTrip: [47, 255] }
|
|
117
121
|
```
|
|
118
122
|
|
|
@@ -128,6 +132,7 @@ resettable sandboxes.
|
|
|
128
132
|
|
|
129
133
|
```ts
|
|
130
134
|
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
135
|
+
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
131
136
|
import { Effect } from "effect"
|
|
132
137
|
import * as ByteSize from "effect/ByteSize"
|
|
133
138
|
|
|
@@ -165,7 +170,7 @@ const program = Effect.gen(function*() {
|
|
|
165
170
|
return new TextDecoder().decode(yield* b.readFile("/project/settings.json"))
|
|
166
171
|
})
|
|
167
172
|
|
|
168
|
-
console.log(await Effect.runPromise(program)) // {"theme":"dark"}
|
|
173
|
+
console.log(await Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer)))) // {"theme":"dark"}
|
|
169
174
|
```
|
|
170
175
|
|
|
171
176
|
Fixture paths must be absolute and unique, and parent directories must be listed explicitly. Fixtures can also contain
|
|
@@ -175,140 +180,15 @@ Snapshot decoding requires explicit work limits because encoded bytes may come f
|
|
|
175
180
|
contains the reachable namespace and metadata. It excludes callers, open handles, cursor positions, watch
|
|
176
181
|
subscriptions, and unlinked content. Each restored volume is independent.
|
|
177
182
|
|
|
178
|
-
##
|
|
179
|
-
|
|
180
|
-
Use `makeOverlay` when several writable workspaces start from the same snapshot. Untouched regular-file contents are
|
|
181
|
-
shared internally. A content change copies that whole file for the editing workspace; namespace and metadata state are
|
|
182
|
-
always private. Logical quotas still count the complete visible volume, including shared base files and unlinked-open
|
|
183
|
-
contents.
|
|
184
|
-
|
|
185
|
-
```ts
|
|
186
|
-
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
187
|
-
import { Effect } from "effect"
|
|
188
|
-
import * as ByteSize from "effect/ByteSize"
|
|
189
|
-
|
|
190
|
-
const branch = Effect.gen(function*() {
|
|
191
|
-
const template = yield* Vfs.fromFixture({
|
|
192
|
-
entries: [
|
|
193
|
-
{ kind: "directory", path: "/project" },
|
|
194
|
-
{ kind: "file", path: "/project/settings.json", bytes: new Uint8Array() }
|
|
195
|
-
]
|
|
196
|
-
})
|
|
197
|
-
const base = yield* template.snapshot
|
|
198
|
-
const workspace = yield* Vfs.makeOverlay(base, { maxBytes: ByteSize.megabytes(1) })
|
|
199
|
-
const fs = yield* workspace.caller()
|
|
200
|
-
|
|
201
|
-
yield* fs.rename("/project/settings.json", "/project/preferences.json")
|
|
202
|
-
return yield* workspace.capture()
|
|
203
|
-
})
|
|
204
|
-
```
|
|
205
|
-
|
|
206
|
-
`makeOverlay(base, options)` returns a reusable Effect. Each execution creates a fresh workspace from `base`; it does
|
|
207
|
-
not memoize or reset an earlier workspace. Invalid volume options fail with `ConfigurationError`, while failure to
|
|
208
|
-
inspect the base snapshot fails with `ImageError`.
|
|
209
|
-
|
|
210
|
-
`changes()` reports final differences from the base. It hides timestamp-only differences unless called with
|
|
211
|
-
`{ includeTimestamps: true }`. Renames are paired only from retained object identity; equal contents are never treated
|
|
212
|
-
as rename evidence. Paths in summaries are `BytePath` values, so inspect them with `pathToBytes` when names may not be
|
|
213
|
-
UTF-8. Although `OverlayChange` and its options have schemas, a summary is an in-process value containing opaque
|
|
214
|
-
`BytePath` objects. The schema is not a portable summary encoding.
|
|
215
|
-
|
|
216
|
-
`capture()` returns a complete version 1 snapshot and matching summary from one committed state. Later writes change
|
|
217
|
-
neither result. The snapshot works with `encodeSnapshot`, `fromSnapshot`, and `@effect-vfs/persistence` checkpoints.
|
|
218
|
-
Restoring it recovers the complete filesystem state, not the earlier overlay base relationship or summary. To reset,
|
|
219
|
-
create a fresh overlay from the base and replace your application reference; existing callers, handles, and watches
|
|
220
|
-
remain attached to the old workspace until their own lifetimes end.
|
|
221
|
-
|
|
222
|
-
The Effects returned by `changes(options)` and `capture(options)` are also reusable. Each execution observes the
|
|
223
|
-
workspace again. Invalid summary options fail with `ConfigurationError`; failure to construct the complete observed
|
|
224
|
-
snapshot fails with `ImageError`. Reusing a previously completed `capture()` result does not observe later writes.
|
|
225
|
-
|
|
226
|
-
## Store and apply exact snapshot deltas
|
|
227
|
-
|
|
228
|
-
Use `diffSnapshots` when you need a portable description of the exact difference between two immutable snapshots.
|
|
229
|
-
The result is an opaque `SnapshotDelta`: inspect it for a path-oriented summary, encode it with Effect Schema, or apply
|
|
230
|
-
it to a semantically identical base to reconstruct the target snapshot.
|
|
231
|
-
|
|
232
|
-
```ts
|
|
233
|
-
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
234
|
-
import * as NodeCrypto from "@effect/platform-node/NodeCrypto"
|
|
235
|
-
import { Effect, Schema } from "effect"
|
|
236
|
-
import * as ByteSize from "effect/ByteSize"
|
|
237
|
-
|
|
238
|
-
const Delta = Vfs.SnapshotDeltaFromBytes()
|
|
239
|
-
|
|
240
|
-
const program = Effect.gen(function*() {
|
|
241
|
-
const baseVolume = yield* Vfs.fromFixture({
|
|
242
|
-
entries: [{ kind: "file", path: "/old.txt", bytes: new TextEncoder().encode("before") }]
|
|
243
|
-
})
|
|
244
|
-
const targetVolume = yield* Vfs.fromFixture({
|
|
245
|
-
entries: [{ kind: "file", path: "/new.txt", bytes: new TextEncoder().encode("after") }]
|
|
246
|
-
})
|
|
247
|
-
const base = yield* baseVolume.snapshot
|
|
248
|
-
const target = yield* targetVolume.snapshot
|
|
249
|
-
|
|
250
|
-
const delta = yield* Vfs.diffSnapshots(base, target)
|
|
251
|
-
const changes = yield* Vfs.inspectSnapshotDelta(base, delta)
|
|
252
|
-
const bytes = yield* Schema.encodeEffect(Delta)(delta)
|
|
253
|
-
const decoded = yield* Schema.decodeEffect(Delta)(bytes)
|
|
254
|
-
const restored = yield* Vfs.applySnapshotDelta(base, decoded)
|
|
255
|
-
|
|
256
|
-
return { changes, restored }
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
await Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer)))
|
|
260
|
-
```
|
|
183
|
+
## Overlays and snapshot deltas
|
|
261
184
|
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
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.
|
|
265
188
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
you need a writable volume.
|
|
270
|
-
|
|
271
|
-
Creation, inspection, encoding, decoding, and application use finite `SnapshotDeltaLimits.default` limits when none are supplied.
|
|
272
|
-
Delta creation, inspection, and application require Effect's platform-neutral `Crypto.Crypto` service for SHA-256 base identity.
|
|
273
|
-
Provide the official Node, Bun, browser, or Deno crypto layer at the application edge; the core never imports a host
|
|
274
|
-
crypto implementation. Memory-sensitive applications can use one policy throughout the workflow:
|
|
275
|
-
|
|
276
|
-
```ts
|
|
277
|
-
const limits = Vfs.SnapshotDeltaLimits.constrained
|
|
278
|
-
const Delta = Vfs.SnapshotDeltaFromBytes(limits)
|
|
279
|
-
|
|
280
|
-
const constrained = Effect.gen(function*() {
|
|
281
|
-
const delta = yield* Vfs.diffSnapshots(base, target, limits)
|
|
282
|
-
const bytes = yield* Schema.encodeEffect(Delta)(delta)
|
|
283
|
-
const decoded = yield* Schema.decodeEffect(Delta)(bytes)
|
|
284
|
-
return yield* Vfs.applySnapshotDelta(base, decoded, limits)
|
|
285
|
-
})
|
|
286
|
-
```
|
|
287
|
-
|
|
288
|
-
Both presets are frozen complete policies. The shipped values are:
|
|
289
|
-
|
|
290
|
-
| limit | `constrained` | `default` |
|
|
291
|
-
| ----------------------------------- | ------------: | --------: |
|
|
292
|
-
| encoded bytes | 2 MiB | 16 MiB |
|
|
293
|
-
| canonical identity bytes | 4 MiB | 32 MiB |
|
|
294
|
-
| stored records plus summary changes | 6,500 | 50,000 |
|
|
295
|
-
| decoded delta bytes | 512 KiB | 8 MiB |
|
|
296
|
-
| base records | 6,500 | 50,000 |
|
|
297
|
-
| target records | 6,500 | 50,000 |
|
|
298
|
-
| namespace entries | 6,500 | 100,000 |
|
|
299
|
-
| output records | 6,500 | 50,000 |
|
|
300
|
-
| output payload bytes | 256 KiB | 4 MiB |
|
|
301
|
-
| inherited records | 6,500 | 50,000 |
|
|
302
|
-
|
|
303
|
-
These are conservative finite defaults, not universal workspace-size or memory guarantees. To tune one dimension,
|
|
304
|
-
spread a preset into a complete `SnapshotDeltaLimits` value and replace that field.
|
|
305
|
-
|
|
306
|
-
```ts
|
|
307
|
-
const limits = {
|
|
308
|
-
...Vfs.SnapshotDeltaLimits.default,
|
|
309
|
-
maxOutputBytes: ByteSize.mebibytes(8)
|
|
310
|
-
}
|
|
311
|
-
```
|
|
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.
|
|
312
192
|
|
|
313
193
|
## Use scoped handles for incremental I/O
|
|
314
194
|
|
|
@@ -318,6 +198,7 @@ interruption.
|
|
|
318
198
|
|
|
319
199
|
```ts
|
|
320
200
|
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
201
|
+
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
321
202
|
import { Effect } from "effect"
|
|
322
203
|
|
|
323
204
|
const program = Effect.scoped(Effect.gen(function*() {
|
|
@@ -335,7 +216,7 @@ const program = Effect.scoped(Effect.gen(function*() {
|
|
|
335
216
|
return [first, second, preview].map((bytes) => new TextDecoder().decode(bytes))
|
|
336
217
|
}))
|
|
337
218
|
|
|
338
|
-
console.log(await Effect.runPromise(program)) // ["one\n", "two\n", "one"]
|
|
219
|
+
console.log(await Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer)))) // ["one\n", "two\n", "one"]
|
|
339
220
|
```
|
|
340
221
|
|
|
341
222
|
Handles also expose an explicit `close` effect when early release matters. Calling explicit close twice fails, while
|
|
@@ -349,6 +230,7 @@ expected failures.
|
|
|
349
230
|
|
|
350
231
|
```ts
|
|
351
232
|
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
233
|
+
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
352
234
|
import { Effect } from "effect"
|
|
353
235
|
|
|
354
236
|
const program = Effect.gen(function*() {
|
|
@@ -362,7 +244,7 @@ const program = Effect.gen(function*() {
|
|
|
362
244
|
)
|
|
363
245
|
})
|
|
364
246
|
|
|
365
|
-
const bytes = await Effect.runPromise(program)
|
|
247
|
+
const bytes = await Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer)))
|
|
366
248
|
console.log(new TextDecoder().decode(bytes)) // {}
|
|
367
249
|
```
|
|
368
250
|
|
|
@@ -374,6 +256,7 @@ current directory.
|
|
|
374
256
|
|
|
375
257
|
```ts
|
|
376
258
|
import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
259
|
+
import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
377
260
|
import { Effect } from "effect"
|
|
378
261
|
|
|
379
262
|
const loadConfig = Effect.gen(function*() {
|
|
@@ -391,7 +274,7 @@ const program = Effect.gen(function*() {
|
|
|
391
274
|
return yield* loadConfig.pipe(Effect.provideService(Vfs.CurrentFileSystem, fs))
|
|
392
275
|
})
|
|
393
276
|
|
|
394
|
-
const bytes = await Effect.runPromise(program)
|
|
277
|
+
const bytes = await Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer)))
|
|
395
278
|
console.log(new TextDecoder().decode(bytes)) // {}
|
|
396
279
|
```
|
|
397
280
|
|
|
@@ -410,9 +293,17 @@ candidate.
|
|
|
410
293
|
- Components are limited to 255 bytes. Symbolic-link traversal is limited to 40 links.
|
|
411
294
|
- Omitted logical quotas are unbounded by configuration, apart from fixed file and component bounds.
|
|
412
295
|
- Absolute paths ignore a supplied directory base. Relative paths can use a live, same-volume directory handle.
|
|
413
|
-
- Operations coordinate through one permit per volume.
|
|
414
|
-
|
|
415
|
-
|
|
296
|
+
- Operations coordinate through one permit per volume. At most 65 operations are admitted at once by
|
|
297
|
+
default, including the active operation; set `maxPendingOperations` to change the waiting budget.
|
|
298
|
+
Excess work fails with retryable `FsError` code `VolumeBusy`
|
|
299
|
+
before mutation or storage commit. Interruption while waiting makes no change; interruption after a commit
|
|
300
|
+
does not roll it back.
|
|
301
|
+
- Each watch subscriber retains at most 256 events by default; set `maxWatchEvents` to change this bound
|
|
302
|
+
(minimum 2). A subscriber that loses events receives `Rescan` with path `/` after its buffered events.
|
|
303
|
+
Rescan the volume, then continue consuming the watch; repeat the scan if another `Rescan` arrives.
|
|
304
|
+
Delivered events retain their order. The memory `FileSystem.watch` stream fails with a platform error
|
|
305
|
+
identified by `MemoryFileSystem.isWatchOverflow`. Open a new watch before rescanning its path so changes
|
|
306
|
+
during the scan remain observable.
|
|
416
307
|
- `sync` checks handle liveness. An in-memory volume provides no host or crash durability.
|
|
417
308
|
|
|
418
309
|
For the complete contract, read the
|
package/dist/BytePath.d.ts
CHANGED
|
@@ -29,6 +29,7 @@ export type BytePathId = typeof BytePathId;
|
|
|
29
29
|
* @example
|
|
30
30
|
* ```ts
|
|
31
31
|
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
32
|
+
* import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
32
33
|
* import { Effect } from "effect"
|
|
33
34
|
*
|
|
34
35
|
* // A name that is not valid UTF-8 still round-trips exactly.
|
|
@@ -45,7 +46,7 @@ export type BytePathId = typeof BytePathId;
|
|
|
45
46
|
* return yield* caller.readDirectoryBytes("/")
|
|
46
47
|
* })
|
|
47
48
|
*
|
|
48
|
-
* Effect.runPromise(program).then(console.log)
|
|
49
|
+
* Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
|
|
49
50
|
* // [ Uint8Array(2) [ 255, 254 ] ]
|
|
50
51
|
* ```
|
|
51
52
|
*
|
package/dist/BytePath.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BytePath.d.ts","sourceRoot":"","sources":["../src/BytePath.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,2BAAiD,CAAA;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAA;AAE1C
|
|
1
|
+
{"version":3,"file":"BytePath.d.ts","sourceRoot":"","sources":["../src/BytePath.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;AAC1C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAGvC;;;;;GAKG;AACH,eAAO,MAAM,UAAU,EAAE,2BAAiD,CAAA;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,UAAU,GAAG,OAAO,UAAU,CAAA;AAE1C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,MAAM,WAAW,QAAS,SAAQ,KAAK,CAAC,KAAK,EAAE,QAAQ;IACrD,QAAQ,CAAC,CAAC,UAAU,CAAC,EAAE,UAAU,CAAA;CAClC;AAED;;;;;GAKG;AACH,eAAO,MAAM,QAAQ,oCAAgD,CAAA"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image boundary for a storage adapter that commits each live volume mutation.
|
|
3
|
+
*
|
|
4
|
+
* An adapter must preserve the supplied bytes atomically and classify each
|
|
5
|
+
* commit as confirmed, definitely rejected, or uncertain. The opened volume
|
|
6
|
+
* stays at `memory-only` unless the adapter explicitly supplies a qualified
|
|
7
|
+
* durability tier.
|
|
8
|
+
*
|
|
9
|
+
* @since 0.4.0
|
|
10
|
+
*/
|
|
11
|
+
import { Context, Effect } from "effect";
|
|
12
|
+
import * as ByteSize from "effect/ByteSize";
|
|
13
|
+
import type * as Crypto from "effect/Crypto";
|
|
14
|
+
import type * as PlatformError from "effect/PlatformError";
|
|
15
|
+
import type * as Scope from "effect/Scope";
|
|
16
|
+
import type { ConfigurationError, ImageError, Volume, VolumeDurability, VolumeOptions } from "./VirtualFileSystem.js";
|
|
17
|
+
/**
|
|
18
|
+
* A storage commit's observed outcome.
|
|
19
|
+
*
|
|
20
|
+
* @since 0.4.0
|
|
21
|
+
*/
|
|
22
|
+
export type CommitOutcome = "committed" | "rejected" | "unknown";
|
|
23
|
+
declare const LiveVolumeError_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 & {
|
|
24
|
+
readonly _tag: "LiveVolumeError";
|
|
25
|
+
} & Readonly<A>;
|
|
26
|
+
/**
|
|
27
|
+
* A failed live-store startup or recovery.
|
|
28
|
+
*
|
|
29
|
+
* @category errors
|
|
30
|
+
* @since 0.4.0
|
|
31
|
+
*/
|
|
32
|
+
export declare class LiveVolumeError extends LiveVolumeError_base<{
|
|
33
|
+
readonly code: "InvalidConfiguration" | "Storage" | "Ownership" | "IncompatibleStore" | "CorruptStore";
|
|
34
|
+
readonly cause?: unknown;
|
|
35
|
+
}> {
|
|
36
|
+
}
|
|
37
|
+
declare const LiveImageStore_base: Context.ServiceClass<LiveImageStore, "@effect-vfs/core/LiveImageStore", {
|
|
38
|
+
/** Storage guarantee for successful commits; omission means memory-only. */
|
|
39
|
+
readonly durability?: VolumeDurability;
|
|
40
|
+
readonly loadOrCreate: (initial: Uint8Array) => Effect.Effect<Uint8Array, LiveVolumeError>;
|
|
41
|
+
readonly commit: (image: Uint8Array) => Effect.Effect<CommitOutcome>;
|
|
42
|
+
}>;
|
|
43
|
+
/**
|
|
44
|
+
* One exclusively owned store. Its Layer holds the storage resource until the
|
|
45
|
+
* volume has shut down. Providers must atomically replace each image and
|
|
46
|
+
* classify ambiguous commits as `unknown`.
|
|
47
|
+
*
|
|
48
|
+
* @since 0.4.0
|
|
49
|
+
*/
|
|
50
|
+
export declare class LiveImageStore extends LiveImageStore_base {
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Configuration shared by every live-image provider.
|
|
54
|
+
*
|
|
55
|
+
* @category models
|
|
56
|
+
* @since 0.4.0
|
|
57
|
+
*/
|
|
58
|
+
export interface Options {
|
|
59
|
+
readonly maxImageBytes: ByteSize.ByteSize;
|
|
60
|
+
readonly volume: VolumeOptions;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Open a live volume using the supplied store Layer. The caller owns a Scope;
|
|
64
|
+
* the volume shuts down before that Layer releases its storage resource.
|
|
65
|
+
*
|
|
66
|
+
* @since 0.4.0
|
|
67
|
+
*/
|
|
68
|
+
export declare const open: (options: Options) => Effect.Effect<Volume, LiveVolumeError, LiveImageStore | Crypto.Crypto | Scope.Scope>;
|
|
69
|
+
/**
|
|
70
|
+
* A live volume and the shutdown action its storage adapter must run before
|
|
71
|
+
* closing the underlying store.
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* ```ts
|
|
75
|
+
* import type { ImageSession } from "@effect-vfs/core/LiveVolume"
|
|
76
|
+
*
|
|
77
|
+
* const shutdown = (session: ImageSession) => session.shutdown
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* @category models
|
|
81
|
+
* @since 0.4.0
|
|
82
|
+
*/
|
|
83
|
+
export interface ImageSession {
|
|
84
|
+
readonly volume: Volume;
|
|
85
|
+
readonly shutdown: Effect.Effect<void>;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Construct a versioned image for a new, empty volume.
|
|
89
|
+
* Store this image before opening it for mutation.
|
|
90
|
+
*
|
|
91
|
+
* @since 0.4.0
|
|
92
|
+
*/
|
|
93
|
+
export declare const prepareEmptyImage: (options?: VolumeOptions) => Effect.Effect<Uint8Array, ConfigurationError | ImageError | PlatformError.PlatformError, Crypto.Crypto>;
|
|
94
|
+
/**
|
|
95
|
+
* Open a validated image and stage every mutation before calling `commit`.
|
|
96
|
+
* The returned session contains the volume and a coordinated shutdown effect.
|
|
97
|
+
* Run shutdown before releasing the storage connection. An uncertain commit
|
|
98
|
+
* makes the volume unavailable until it is reopened.
|
|
99
|
+
*
|
|
100
|
+
* @since 0.4.0
|
|
101
|
+
*/
|
|
102
|
+
export declare const openImage: (image: Uint8Array, maxImageBytes: ByteSize.ByteSize, commit: (image: Uint8Array) => Effect.Effect<CommitOutcome>, durability?: VolumeDurability) => Effect.Effect<ImageSession, ConfigurationError | ImageError | PlatformError.PlatformError, Crypto.Crypto>;
|
|
103
|
+
export {};
|
|
104
|
+
//# sourceMappingURL=LiveVolume.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LiveVolume.d.ts","sourceRoot":"","sources":["../src/LiveVolume.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,OAAO,EAAE,OAAO,EAAQ,MAAM,EAAE,MAAM,QAAQ,CAAA;AAC9C,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAC3C,OAAO,KAAK,KAAK,MAAM,MAAM,eAAe,CAAA;AAC5C,OAAO,KAAK,KAAK,aAAa,MAAM,sBAAsB,CAAA;AAC1D,OAAO,KAAK,KAAK,KAAK,MAAM,cAAc,CAAA;AAE1C,OAAO,KAAK,EAAE,kBAAkB,EAAE,UAAU,EAAE,MAAM,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,wBAAwB,CAAA;AAErH;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,CAAA;;;;AAEhE;;;;;GAKG;AACH,qBAAa,eAAgB,SAAQ,qBAAoC;IACvE,QAAQ,CAAC,IAAI,EAAE,sBAAsB,GAAG,SAAS,GAAG,WAAW,GAAG,mBAAmB,GAAG,cAAc,CAAA;IACtG,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CACzB,CAAC;CAAG;;IAUH,4EAA4E;0BACtD,gBAAgB;2BACf,CAAC,OAAO,EAAE,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,eAAe,CAAC;qBACzE,CAAC,KAAK,EAAE,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC;;AAXtE;;;;;;GAMG;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;;;;;GAKG;AACH,eAAO,MAAM,IAAI,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,MAAM,CAAC,MAAM,CACpD,MAAM,EACN,eAAe,EACf,cAAc,GAAG,MAAM,CAAC,MAAM,GAAG,KAAK,CAAC,KAAK,CAkC5C,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;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,CAAC,OAAO,CAAC,EAAE,aAAa,KAAK,MAAM,CAAC,MAAM,CACxE,UAAU,EACV,kBAAkB,GAAG,UAAU,GAAG,aAAa,CAAC,aAAa,EAC7D,MAAM,CAAC,MAAM,CACgB,CAAA;AAE/B;;;;;;;GAOG;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,kBAAkB,GAAG,UAAU,GAAG,aAAa,CAAC,aAAa,EAC7D,MAAM,CAAC,MAAM,CACU,CAAA"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Image boundary for a storage adapter that commits each live volume mutation.
|
|
3
|
+
*
|
|
4
|
+
* An adapter must preserve the supplied bytes atomically and classify each
|
|
5
|
+
* commit as confirmed, definitely rejected, or uncertain. The opened volume
|
|
6
|
+
* stays at `memory-only` unless the adapter explicitly supplies a qualified
|
|
7
|
+
* durability tier.
|
|
8
|
+
*
|
|
9
|
+
* @since 0.4.0
|
|
10
|
+
*/
|
|
11
|
+
import { Context, Data, Effect } from "effect";
|
|
12
|
+
import * as ByteSize from "effect/ByteSize";
|
|
13
|
+
import * as Model from "./internal/virtualFileSystem.js";
|
|
14
|
+
/**
|
|
15
|
+
* A failed live-store startup or recovery.
|
|
16
|
+
*
|
|
17
|
+
* @category errors
|
|
18
|
+
* @since 0.4.0
|
|
19
|
+
*/
|
|
20
|
+
export class LiveVolumeError extends Data.TaggedError("LiveVolumeError") {
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* One exclusively owned store. Its Layer holds the storage resource until the
|
|
24
|
+
* volume has shut down. Providers must atomically replace each image and
|
|
25
|
+
* classify ambiguous commits as `unknown`.
|
|
26
|
+
*
|
|
27
|
+
* @since 0.4.0
|
|
28
|
+
*/
|
|
29
|
+
export class LiveImageStore extends Context.Service()("@effect-vfs/core/LiveImageStore") {
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Open a live volume using the supplied store Layer. The caller owns a Scope;
|
|
33
|
+
* the volume shuts down before that Layer releases its storage resource.
|
|
34
|
+
*
|
|
35
|
+
* @since 0.4.0
|
|
36
|
+
*/
|
|
37
|
+
export const open = Effect.fn("LiveVolume.open")(function* (options) {
|
|
38
|
+
const store = yield* LiveImageStore;
|
|
39
|
+
const initial = yield* prepareEmptyImage(options.volume).pipe(Effect.mapError((cause) => new LiveVolumeError({ code: "InvalidConfiguration", cause })));
|
|
40
|
+
if (BigInt(initial.length) > ByteSize.toBigInt(options.maxImageBytes)) {
|
|
41
|
+
return yield* new LiveVolumeError({ code: "InvalidConfiguration" });
|
|
42
|
+
}
|
|
43
|
+
const image = yield* store.loadOrCreate(initial);
|
|
44
|
+
const session = yield* openImage(image, options.maxImageBytes, store.commit, store.durability ?? "memory-only").pipe(Effect.mapError((cause) => new LiveVolumeError({ code: "CorruptStore", cause })));
|
|
45
|
+
yield* Effect.addFinalizer(() => session.shutdown);
|
|
46
|
+
const volume = session.volume;
|
|
47
|
+
if (volume.limits.maxEntries !== options.volume.maxEntries || volume.limits.maxBytes === undefined ||
|
|
48
|
+
volume.limits.maxPathBytes === undefined || options.volume.maxBytes === undefined ||
|
|
49
|
+
options.volume.maxFileBytes === undefined || options.volume.maxPathBytes === undefined ||
|
|
50
|
+
ByteSize.toBigInt(volume.limits.maxBytes) !== ByteSize.toBigInt(options.volume.maxBytes) ||
|
|
51
|
+
ByteSize.toBigInt(volume.limits.maxFileBytes) !== ByteSize.toBigInt(options.volume.maxFileBytes) ||
|
|
52
|
+
ByteSize.toBigInt(volume.limits.maxPathBytes) !== ByteSize.toBigInt(options.volume.maxPathBytes) ||
|
|
53
|
+
(options.volume.identity !== undefined && options.volume.identity !== volume.identity)) {
|
|
54
|
+
return yield* new LiveVolumeError({ code: "IncompatibleStore" });
|
|
55
|
+
}
|
|
56
|
+
return volume;
|
|
57
|
+
});
|
|
58
|
+
/**
|
|
59
|
+
* Construct a versioned image for a new, empty volume.
|
|
60
|
+
* Store this image before opening it for mutation.
|
|
61
|
+
*
|
|
62
|
+
* @since 0.4.0
|
|
63
|
+
*/
|
|
64
|
+
export const prepareEmptyImage = Model.prepareEmptyLiveImage;
|
|
65
|
+
/**
|
|
66
|
+
* Open a validated image and stage every mutation before calling `commit`.
|
|
67
|
+
* The returned session contains the volume and a coordinated shutdown effect.
|
|
68
|
+
* Run shutdown before releasing the storage connection. An uncertain commit
|
|
69
|
+
* makes the volume unavailable until it is reopened.
|
|
70
|
+
*
|
|
71
|
+
* @since 0.4.0
|
|
72
|
+
*/
|
|
73
|
+
export const openImage = Model.openImageVolume;
|
package/dist/Snapshot.d.ts
CHANGED
|
@@ -31,6 +31,7 @@ declare const ImageError_base: new <A extends Record<string, any> = {}>(args: im
|
|
|
31
31
|
* @example
|
|
32
32
|
* ```ts
|
|
33
33
|
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
34
|
+
* import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
34
35
|
* import { ByteSize, Effect } from "effect"
|
|
35
36
|
*
|
|
36
37
|
* // Decoding is a trust boundary. `code` says what went wrong and `field` says
|
|
@@ -53,7 +54,7 @@ declare const ImageError_base: new <A extends Record<string, any> = {}>(args: im
|
|
|
53
54
|
* )
|
|
54
55
|
* })
|
|
55
56
|
*
|
|
56
|
-
* Effect.runPromise(program).then(console.log)
|
|
57
|
+
* Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
|
|
57
58
|
* // declined at encodedBytes
|
|
58
59
|
* ```
|
|
59
60
|
*
|
|
@@ -73,6 +74,7 @@ export declare class ImageError extends ImageError_base<{
|
|
|
73
74
|
* @example
|
|
74
75
|
* ```ts
|
|
75
76
|
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
77
|
+
* import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
76
78
|
* import { ByteSize, Effect } from "effect"
|
|
77
79
|
*
|
|
78
80
|
* // All four are required: each bounds a different resource. `maxEncodedBytes`
|
|
@@ -93,7 +95,7 @@ export declare class ImageError extends ImageError_base<{
|
|
|
93
95
|
* return "accepted"
|
|
94
96
|
* })
|
|
95
97
|
*
|
|
96
|
-
* Effect.runPromise(program).then(console.log)
|
|
98
|
+
* Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
|
|
97
99
|
* // accepted
|
|
98
100
|
* ```
|
|
99
101
|
*
|
package/dist/Snapshot.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Snapshot.d.ts","sourceRoot":"","sources":["../src/Snapshot.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAG,2BAAoC,CAAA;AAElE;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAA;AAElD;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,CAAC,cAAc,CAAC,EAAE,cAAc,CAAA;CAC1C;;;;AAED
|
|
1
|
+
{"version":3,"file":"Snapshot.d.ts","sourceRoot":"","sources":["../src/Snapshot.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC;;;;;GAKG;AACH,eAAO,MAAM,cAAc,EAAG,2BAAoC,CAAA;AAElE;;;;;GAKG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,cAAc,CAAA;AAElD;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,CAAC,cAAc,CAAC,EAAE,cAAc,CAAA;CAC1C;;;;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,qBAAa,UAAW,SAAQ,gBAA+B;IAC7D,uEAAuE;IACvE,QAAQ,CAAC,IAAI,EAAE,iBAAiB,GAAG,oBAAoB,GAAG,kBAAkB,GAAG,eAAe,CAAA;IAC9F,kFAAkF;IAClF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAA;CACxB,CAAC;CAAG;AAEL;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,eAAO,MAAM,YAAY;IACvB,sDAAsD;;IAEtD,6DAA6D;;IAE7D,2CAA2C;;IAE3C,6CAA6C;;EAE7C,CAAA;AAEF;;;;;GAKG;AACH,MAAM,MAAM,YAAY,GAAG,OAAO,YAAY,CAAC,IAAI,CAAA"}
|
package/dist/Snapshot.js
CHANGED
|
@@ -22,6 +22,7 @@ export const SnapshotTypeId = "@effect-vfs/core/Snapshot";
|
|
|
22
22
|
* @example
|
|
23
23
|
* ```ts
|
|
24
24
|
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
25
|
+
* import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
25
26
|
* import { ByteSize, Effect } from "effect"
|
|
26
27
|
*
|
|
27
28
|
* // Decoding is a trust boundary. `code` says what went wrong and `field` says
|
|
@@ -44,7 +45,7 @@ export const SnapshotTypeId = "@effect-vfs/core/Snapshot";
|
|
|
44
45
|
* )
|
|
45
46
|
* })
|
|
46
47
|
*
|
|
47
|
-
* Effect.runPromise(program).then(console.log)
|
|
48
|
+
* Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
|
|
48
49
|
* // declined at encodedBytes
|
|
49
50
|
* ```
|
|
50
51
|
*
|
|
@@ -59,6 +60,7 @@ export class ImageError extends Data.TaggedError("ImageError") {
|
|
|
59
60
|
* @example
|
|
60
61
|
* ```ts
|
|
61
62
|
* import { VirtualFileSystem as Vfs } from "@effect-vfs/core"
|
|
63
|
+
* import * as NodeCrypto from "@effect/platform-node-shared/NodeCrypto"
|
|
62
64
|
* import { ByteSize, Effect } from "effect"
|
|
63
65
|
*
|
|
64
66
|
* // All four are required: each bounds a different resource. `maxEncodedBytes`
|
|
@@ -79,7 +81,7 @@ export class ImageError extends Data.TaggedError("ImageError") {
|
|
|
79
81
|
* return "accepted"
|
|
80
82
|
* })
|
|
81
83
|
*
|
|
82
|
-
* Effect.runPromise(program).then(console.log)
|
|
84
|
+
* Effect.runPromise(program.pipe(Effect.provide(NodeCrypto.layer))).then(console.log)
|
|
83
85
|
* // accepted
|
|
84
86
|
* ```
|
|
85
87
|
*
|