@effect-vfs/core 0.6.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.
@@ -1,6 +1,8 @@
1
1
  import * as ByteSize from "effect/ByteSize";
2
2
  import * as Effect from "effect/Effect";
3
3
  import * as Encoding from "effect/Encoding";
4
+ import * as Match from "effect/Match";
5
+ import * as Order from "effect/Order";
4
6
  import * as Result from "effect/Result";
5
7
  import * as Schema from "effect/Schema";
6
8
  import { SnapshotChange, SnapshotDifference, SnapshotNodeKind } from "../SnapshotDelta.js";
@@ -261,17 +263,9 @@ const deltaNode = (target, path, node, withPayload) => {
261
263
  return DeltaNode.cases.link.make({ to: CanonicalBase64.encode(first) }, UNCHECKED);
262
264
  }
263
265
  const metadata = storedMetadata(node.metadata);
264
- if (node.kind === "directory")
265
- return DeltaNode.cases.directory.make({ metadata }, UNCHECKED);
266
- if (!withPayload) {
267
- return node.kind === "file"
268
- ? DeltaNode.cases.file.make({ metadata }, UNCHECKED)
269
- : DeltaNode.cases.symlink.make({ metadata }, UNCHECKED);
270
- }
271
- const bytes = CanonicalBase64.encode(payloadOf(node));
272
- return node.kind === "file"
273
- ? DeltaNode.cases.file.make({ metadata, content: InlineContent.make({ bytes }, UNCHECKED) }, UNCHECKED)
274
- : DeltaNode.cases.symlink.make({ metadata, target: bytes }, UNCHECKED);
266
+ return Match.value(node).pipe(Match.discriminator("kind")("directory", () => DeltaNode.cases.directory.make({ metadata }, UNCHECKED)), Match.discriminator("kind")("file", (file) => DeltaNode.cases.file.make(withPayload
267
+ ? { metadata, content: InlineContent.make({ bytes: CanonicalBase64.encode(file.data) }, UNCHECKED) }
268
+ : { metadata }, UNCHECKED)), Match.discriminator("kind")("symlink", (symlink) => DeltaNode.cases.symlink.make(withPayload ? { metadata, target: CanonicalBase64.encode(symlink.target) } : { metadata }, UNCHECKED)), Match.exhaustive);
275
269
  };
276
270
  const lookup = (get, entriesOf, path) => {
277
271
  let parent = ROOT_INO;
@@ -365,7 +359,7 @@ const compare = (base, target) => {
365
359
  }
366
360
  }
367
361
  }
368
- return changes.sort((a, b) => bytesOrder(a.path, b.path)).map(({ change }) => change);
362
+ return changes.sort(Order.mapInput(bytesOrder, (entry) => entry.path)).map(({ change }) => change);
369
363
  };
370
364
  const getDocument = (delta) => Effect.suspend(() => {
371
365
  const value = SnapshotDeltaModel.value(delta);
@@ -622,7 +616,7 @@ const foldedIdentity = Effect.fnUntraced(function* (base, folded, identity, limi
622
616
  // Digest children before their directories.
623
617
  const ordered = [
624
618
  ...nodes.filter((node) => node.kind !== "directory"),
625
- ...directories.sort((a, b) => depth(b) - depth(a))
619
+ ...directories.sort(Order.flip(Order.mapInput(Order.Number, depth)))
626
620
  ];
627
621
  for (const node of ordered)
628
622
  digests.set(node.ino, yield* Merkle.nodeDigest(node, digestOf, measure));
@@ -1 +1 @@
1
- {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/internal/tree.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"tree.d.ts","sourceRoot":"","sources":["../../src/internal/tree.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  // Validate each tree line as it arrives; check cross-node graph rules after the final line.
2
2
  import * as Effect from "effect/Effect";
3
3
  import * as Encoding from "effect/Encoding";
4
+ import * as Match from "effect/Match";
4
5
  import * as Predicate from "effect/Predicate";
5
6
  import * as Result from "effect/Result";
6
7
  import * as Schema from "effect/Schema";
@@ -323,18 +324,17 @@ export const UNCHECKED = { disableChecks: true };
323
324
  /** @internal */
324
325
  export const treeNode = (node) => {
325
326
  const metadata = storedMetadata(node.metadata);
326
- if (node.kind === "directory") {
327
- return TreeNode.cases.directory.make({ ino: node.ino, parent: node.parent, name: encodeName(node.name), metadata }, UNCHECKED);
328
- }
329
- const named = node.links.map((link) => ({ parent: link.parent, name: encodeName(link.name) }));
330
- return node.kind === "file"
331
- ? TreeNode.cases.file.make({
332
- ino: node.ino,
333
- links: named,
334
- content: TreeContent.cases.Inline.make({ bytes: CanonicalBase64.encode(node.data) }, UNCHECKED),
335
- metadata
336
- }, UNCHECKED)
337
- : TreeNode.cases.symlink.make({ ino: node.ino, links: named, target: CanonicalBase64.encode(node.target), metadata }, UNCHECKED);
327
+ return Match.value(node).pipe(Match.discriminator("kind")("directory", (directory) => TreeNode.cases.directory.make({ ino: directory.ino, parent: directory.parent, name: encodeName(directory.name), metadata }, UNCHECKED)), Match.discriminator("kind")("file", (file) => TreeNode.cases.file.make({
328
+ ino: file.ino,
329
+ links: file.links.map((link) => ({ parent: link.parent, name: encodeName(link.name) })),
330
+ content: TreeContent.cases.Inline.make({ bytes: CanonicalBase64.encode(file.data) }, UNCHECKED),
331
+ metadata
332
+ }, UNCHECKED)), Match.discriminator("kind")("symlink", (symlink) => TreeNode.cases.symlink.make({
333
+ ino: symlink.ino,
334
+ links: symlink.links.map((link) => ({ parent: link.parent, name: encodeName(link.name) })),
335
+ target: CanonicalBase64.encode(symlink.target),
336
+ metadata
337
+ }, UNCHECKED)), Match.exhaustive);
338
338
  };
339
339
  /** @internal */
340
340
  export const treeNodes = Effect.fnUntraced(function* (value, extra = []) {
@@ -1,4 +1,5 @@
1
1
  import * as ByteSize from "effect/ByteSize";
2
+ import * as Crypto from "effect/Crypto";
2
3
  import * as Data from "effect/Data";
3
4
  import * as Effect from "effect/Effect";
4
5
  import type { Snapshot } from "../Snapshot.js";
@@ -6,8 +7,13 @@ import { type FsFailure, type ImageFailure } from "../VfsError.js";
6
7
  import type { OverlayVolume, Volume, VolumeLimits } from "../VirtualFileSystem.js";
7
8
  import * as LiveImage from "./liveImage.js";
8
9
  import { type ObservationEntry } from "./overlayDiff.js";
9
- import { type CommitProvider } from "./stagedState.js";
10
10
  import { type VolumeState } from "./volumeState.js";
11
+ type CommitOutcome = "committed" | "rejected" | "unknown";
12
+ interface CommitProvider<State> {
13
+ /** Preparation failures leave the volume available. */
14
+ readonly prepare?: (candidate: State) => Effect.Effect<void, FsFailure>;
15
+ readonly commit: (candidate: State) => Effect.Effect<CommitOutcome>;
16
+ }
11
17
  type VolumeSource = {
12
18
  readonly _tag: "Empty";
13
19
  } | {
@@ -74,7 +80,7 @@ export declare const makeVolume: (source: VolumeSource, options?: {
74
80
  observation: ObservationEntry[];
75
81
  }, FsFailure, never>;
76
82
  }>;
77
- }>, import("../VfsError.js").ArgumentFailure | ImageFailure, never>;
83
+ }>, import("../VfsError.js").ArgumentFailure | ImageFailure, Crypto.Crypto>;
78
84
  /** @internal */
79
85
  export declare const make: (options?: {
80
86
  readonly identity?: string & import("effect/Brand").Brand<"@effect-vfs/core/VolumeIdentity">;
@@ -84,7 +90,7 @@ export declare const make: (options?: {
84
90
  readonly maxBytes?: ByteSize.ByteSize;
85
91
  readonly maxFileBytes?: ByteSize.ByteSize;
86
92
  readonly maxPathBytes?: ByteSize.ByteSize;
87
- } | undefined) => Effect.Effect<Volume, import("../VfsError.js").ArgumentFailure, never>;
93
+ } | undefined) => Effect.Effect<Volume, import("../VfsError.js").ArgumentFailure, Crypto.Crypto>;
88
94
  /** @internal */
89
95
  export declare const prepareEmptyLiveImage: (options?: {
90
96
  readonly identity?: string & import("effect/Brand").Brand<"@effect-vfs/core/VolumeIdentity">;
@@ -94,12 +100,12 @@ export declare const prepareEmptyLiveImage: (options?: {
94
100
  readonly maxBytes?: ByteSize.ByteSize;
95
101
  readonly maxFileBytes?: ByteSize.ByteSize;
96
102
  readonly maxPathBytes?: ByteSize.ByteSize;
97
- } | undefined) => Effect.Effect<Uint8Array<ArrayBufferLike>, import("../VfsError.js").ArgumentFailure | ImageFailure, never>;
103
+ } | undefined) => Effect.Effect<Uint8Array<ArrayBufferLike>, import("../VfsError.js").ArgumentFailure | ImageFailure, Crypto.Crypto>;
98
104
  /** @internal */
99
105
  export declare const openImageVolume: (image: Uint8Array<ArrayBufferLike>, maxImageBytes: ByteSize.ByteSize, commit: (image: Uint8Array) => Effect.Effect<"committed" | "rejected" | "unknown">, durability?: "memory-only" | "survives-operating-system-crash" | "survives-power-loss" | "survives-process-crash" | undefined) => Effect.Effect<Readonly<{
100
106
  volume: Volume;
101
107
  shutdown: Effect.Effect<void, never, never>;
102
- }>, import("../VfsError.js").ArgumentFailure | ImageFailure, never>;
108
+ }>, import("../VfsError.js").ArgumentFailure | ImageFailure, Crypto.Crypto>;
103
109
  /** @internal */
104
110
  export declare const fromSnapshot: (snapshot: Snapshot, options?: {
105
111
  readonly identity?: string & import("effect/Brand").Brand<"@effect-vfs/core/VolumeIdentity">;
@@ -109,7 +115,7 @@ export declare const fromSnapshot: (snapshot: Snapshot, options?: {
109
115
  readonly maxBytes?: ByteSize.ByteSize;
110
116
  readonly maxFileBytes?: ByteSize.ByteSize;
111
117
  readonly maxPathBytes?: ByteSize.ByteSize;
112
- } | undefined) => Effect.Effect<Volume, import("../VfsError.js").ArgumentFailure | ImageFailure, never>;
118
+ } | undefined) => Effect.Effect<Volume, import("../VfsError.js").ArgumentFailure | ImageFailure, Crypto.Crypto>;
113
119
  /** @internal */
114
120
  export declare const makeOverlay: (base: Snapshot, options?: {
115
121
  readonly identity?: string & import("effect/Brand").Brand<"@effect-vfs/core/VolumeIdentity">;
@@ -119,6 +125,6 @@ export declare const makeOverlay: (base: Snapshot, options?: {
119
125
  readonly maxBytes?: ByteSize.ByteSize;
120
126
  readonly maxFileBytes?: ByteSize.ByteSize;
121
127
  readonly maxPathBytes?: ByteSize.ByteSize;
122
- } | undefined) => Effect.Effect<OverlayVolume, import("../VfsError.js").ArgumentFailure | ImageFailure, never>;
128
+ } | undefined) => Effect.Effect<OverlayVolume, import("../VfsError.js").ArgumentFailure | ImageFailure, Crypto.Crypto>;
123
129
  export {};
124
130
  //# sourceMappingURL=virtualFileSystem.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"virtualFileSystem.d.ts","sourceRoot":"","sources":["../../src/internal/virtualFileSystem.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAG3C,OAAO,KAAK,IAAI,MAAM,aAAa,CAAA;AACnC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AA4BvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAE9C,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAqB,MAAM,gBAAgB,CAAA;AACrF,OAAO,KAAK,EAMV,aAAa,EAEb,MAAM,EACN,YAAY,EAIb,MAAM,yBAAyB,CAAA;AA2BhC,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAE3C,OAAO,EAAkB,KAAK,gBAAgB,EAAyB,MAAM,kBAAkB,CAAA;AAmB/F,OAAO,EAAE,KAAK,cAAc,EAAe,MAAM,kBAAkB,CAAA;AAGnE,OAAO,EAeL,KAAK,WAAW,EAEjB,MAAM,kBAAkB,CAAA;AA+RzB,KAAK,YAAY,GACb;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAA;CAAE,GAChE;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAA;AAE9D,gBAAgB;AAChB,eAAO,MAAM,YAAY;;uBALJ,OAAO;;;;uBACP,MAAM;2BAAqB,SAAS,CAAC,QAAQ;;;;uBAC7C,UAAU;wBAAkB,WAAW;;;;;;;;;;;;;uBAFvC,OAAO;;;uBACP,MAAM;2BAAqB,SAAS,CAAC,QAAQ;;;uBAC7C,UAAU;wBAAkB,WAAW;;CAGD,CAAA;AA4E3D,gBAAgB;AAChB,eAAO,MAAM,UAAU;;;;;;;;mGAMV,WAAW,UACV,SAAS,CAAC,MAAM,UAChB,YAAY,KACjB,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,YAAY,CAAC;;;;;;;;;;;mEAkmG/C,CAAA;AAED,gBAAgB;AAChB,eAAO,MAAM,IAAI;;;;;;;;wFAIf,CAAA;AAEF,gBAAgB;AAChB,eAAO,MAAM,qBAAqB;;;;;;;;4HAShC,CAAA;AAEF,gBAAgB;AAChB,eAAO,MAAM,eAAe,yFAGV,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,WAAW,GAAG,UAAU,GAAG,SAAS,CAAC;;;mEA+ClF,CAAA;AAeF,gBAAgB;AAChB,eAAO,MAAM,YAAY;;;;;;;;uGAKxB,CAAA;AAED,gBAAgB;AAChB,eAAO,MAAM,WAAW;;;;;;;;8GAkCvB,CAAA"}
1
+ {"version":3,"file":"virtualFileSystem.d.ts","sourceRoot":"","sources":["../../src/internal/virtualFileSystem.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,QAAQ,MAAM,iBAAiB,CAAA;AAE3C,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AACvC,OAAO,KAAK,IAAI,MAAM,aAAa,CAAA;AACnC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AA4BvC,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAA;AAE9C,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,YAAY,EAAqB,MAAM,gBAAgB,CAAA;AACrF,OAAO,KAAK,EAMV,aAAa,EAEb,MAAM,EACN,YAAY,EAIb,MAAM,yBAAyB,CAAA;AA2BhC,OAAO,KAAK,SAAS,MAAM,gBAAgB,CAAA;AAE3C,OAAO,EAAkB,KAAK,gBAAgB,EAAyB,MAAM,kBAAkB,CAAA;AAqB/F,OAAO,EAgBL,KAAK,WAAW,EAEjB,MAAM,kBAAkB,CAAA;AAWzB,KAAK,aAAa,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,CAAA;AAEzD,UAAU,cAAc,CAAC,KAAK;IAC5B,uDAAuD;IACvD,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;IACvE,QAAQ,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;CACpE;AAwTD,KAAK,YAAY,GACb;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,QAAQ,CAAA;CAAE,GAChE;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAA;AAE9D,gBAAgB;AAChB,eAAO,MAAM,YAAY;;uBALJ,OAAO;;;;uBACP,MAAM;2BAAqB,SAAS,CAAC,QAAQ;;;;uBAC7C,UAAU;wBAAkB,WAAW;;;;;;;;;;;;;uBAFvC,OAAO;;;uBACP,MAAM;2BAAqB,SAAS,CAAC,QAAQ;;;uBAC7C,UAAU;wBAAkB,WAAW;;CAGD,CAAA;AAsD3D,gBAAgB;AAChB,eAAO,MAAM,UAAU;;;;;;;;mGAMV,WAAW,UACV,SAAS,CAAC,MAAM,UAChB,YAAY,KACjB,MAAM,CAAC,MAAM,CAAC,UAAU,EAAE,YAAY,CAAC;;;;;;;;;;;2EAmmG/C,CAAA;AAED,gBAAgB;AAChB,eAAO,MAAM,IAAI;;;;;;;;gGAIf,CAAA;AAEF,gBAAgB;AAChB,eAAO,MAAM,qBAAqB;;;;;;;;oIAShC,CAAA;AAEF,gBAAgB;AAChB,eAAO,MAAM,eAAe,yFAGV,UAAU,KAAK,MAAM,CAAC,MAAM,CAAC,WAAW,GAAG,UAAU,GAAG,SAAS,CAAC;;;2EA+ClF,CAAA;AAeF,gBAAgB;AAChB,eAAO,MAAM,YAAY;;;;;;;;+GAKxB,CAAA;AAED,gBAAgB;AAChB,eAAO,MAAM,WAAW;;;;;;;;sHAkCvB,CAAA"}
@@ -1,3 +1,4 @@
1
+ import * as Arr from "effect/Array";
1
2
  import * as ByteSize from "effect/ByteSize";
2
3
  import * as Clock from "effect/Clock";
3
4
  import * as Crypto from "effect/Crypto";
@@ -5,9 +6,9 @@ import * as Data from "effect/Data";
5
6
  import * as Effect from "effect/Effect";
6
7
  import * as Encoding from "effect/Encoding";
7
8
  import * as Exit from "effect/Exit";
9
+ import * as Match from "effect/Match";
8
10
  import * as Option from "effect/Option";
9
11
  import * as Predicate from "effect/Predicate";
10
- import * as Random from "effect/Random";
11
12
  import * as Result from "effect/Result";
12
13
  import * as Schema from "effect/Schema";
13
14
  import * as Scope from "effect/Scope";
@@ -31,10 +32,9 @@ import * as LiveImage from "./liveImage.js";
31
32
  import * as MetadataDomain from "./metadata.js";
32
33
  import { compareOverlay } from "./overlayDiff.js";
33
34
  import { DOT_DOT_HEX, DOT_HEX, inputBytes, isAttachedBytes, isDotComponent, isWellFormed, joinPath, MAX_NAME_BYTES, MAX_SYMLINK_TRAVERSALS, nameBytes, ownedPath, preparePath, ROOT_PATH, SLASH_BYTE, SLASH_HEX } from "./path.js";
34
- import { offerCommit } from "./stagedState.js";
35
35
  import { VolumeTestSeams } from "./testSeams.js";
36
36
  import { makeTurnstile } from "./turnstile.js";
37
- import { directoryMetadata, emptyRoot, getNode, Ino, MAX_FILE_BYTES, reachableValue, ROOT_INO, storedMetadata, WALK_YIELD_INTERVAL } from "./volumeState.js";
37
+ import { byEntryName, directoryMetadata, emptyRoot, getNode, Ino, MAX_FILE_BYTES, reachableValue, ROOT_INO, storedMetadata, WALK_YIELD_INTERVAL } from "./volumeState.js";
38
38
  import * as WatchHub from "./watchHub.js";
39
39
  const EXECUTE = 0o1;
40
40
  const WRITE = 0o2;
@@ -43,6 +43,20 @@ const ANY_EXECUTE = 0o111;
43
43
  const SET_ID_BITS = 0o6000;
44
44
  const STICKY_BIT = 0o1000;
45
45
  const RELATIME_INTERVAL_NS = 86400000000000n; // relatime, as Linux mounts by default: a read refreshes an access time at least this old, 24 hours.
46
+ // An unknown commit outcome stops the volume; definite rejection stops it only during cleanup.
47
+ const offerCommit = (provider, operation, candidate, cleanup) => Effect.map(Effect.exit(Effect.suspend(() => provider.commit(candidate))), (committed) => {
48
+ // TODO(#186): Preserve the cause of a provider defect or interruption.
49
+ if (Exit.isFailure(committed)) {
50
+ return { available: false, failure: fsFailure("OutcomeUnknown", operation) };
51
+ }
52
+ if (committed.value === "rejected") {
53
+ return { available: !cleanup, failure: fsFailure("StorageRejected", operation) };
54
+ }
55
+ if (committed.value === "unknown") {
56
+ return { available: false, failure: fsFailure("OutcomeUnknown", operation) };
57
+ }
58
+ return { available: true, failure: undefined };
59
+ });
46
60
  const WALK_CHUNK_ENTRIES = 128;
47
61
  const MAX_FILE_OFFSET = 0x7fffffffffffffffn;
48
62
  const isMode = Schema.is(Mode);
@@ -55,7 +69,7 @@ const decodeOwnerUpdate = Schema.decodeEffect(OwnerUpdate, { onExcessProperty: "
55
69
  const decodeTimes = Schema.decodeEffect(Times, { onExcessProperty: "error" });
56
70
  const decodeExpected = Schema.decodeEffect(SetattrOptions.fields.expected.schema, { onExcessProperty: "error" });
57
71
  const SETATTR_FIELDS = Object.keys(SetattrOptions.fields);
58
- const timeAt = (update, value, now) => update === undefined || update.kind === "omit" ? value : update.kind === "now" ? now : update.nanoseconds;
72
+ const timeAt = (update, value, now) => update === undefined ? value : Match.value(update).pipe(Match.discriminator("kind")("omit", () => value), Match.discriminator("kind")("now", () => now), Match.discriminator("kind")("value", ({ nanoseconds }) => nanoseconds), Match.exhaustive);
59
73
  const decodeWriteFileOptions = Schema.decodeEffect(WriteFileOptions, { onExcessProperty: "error" });
60
74
  const decodeOpenOptions = Schema.decodeEffect(OpenOptions, { onExcessProperty: "error" });
61
75
  const decodeMkdirOptions = Schema.decodeEffect(MkdirOptions, { onExcessProperty: "error" });
@@ -212,28 +226,14 @@ const observeChanges = Effect.fnUntraced(function* (captured) {
212
226
  return observation;
213
227
  });
214
228
  const randomHex128 = Effect.gen(function* () {
215
- const crypto = yield* Effect.serviceOption(Crypto.Crypto);
216
- if (Option.isSome(crypto))
217
- return Encoding.encodeHex(yield* Effect.orDie(crypto.value.randomBytes(16)));
218
- let hex = "";
219
- for (let draw = 0; draw < 4; draw++) {
220
- hex += (yield* Random.nextIntBetween(0, 0x100000000, { halfOpen: true })).toString(16).padStart(8, "0");
221
- }
222
- return hex;
223
- });
224
- // The 128-bit secret behind every key's tag, always from the platform's cryptographically secure generator. Unlike an
225
- // identity or an epoch it must not follow a seedable Random: every key hands out the identity and epoch, so a
226
- // reproducible secret would let one key forge the tag of any other inode. Web Crypto is global in every supported
227
- // runtime, so reading it adds no service requirement; a runtime without it is a defect, not a recoverable failure.
228
- const randomKeySecret = Effect.sync(() => {
229
- const webCrypto = globalThis.crypto;
230
- if (webCrypto === undefined) {
231
- throw new Error("globalThis.crypto is unavailable, so a volume cannot draw its reference-key secret");
232
- }
233
- return Encoding.encodeHex(webCrypto.getRandomValues(new Uint8Array(16)));
234
- });
229
+ const crypto = yield* Crypto.Crypto;
230
+ return Encoding.encodeHex(yield* crypto.randomBytes(16));
231
+ }).pipe(Effect.orDie);
235
232
  /** @internal */
236
233
  export const makeVolume = Effect.fnUntraced(function* (source, options, commitProvider, captureInitial, durability = "memory-only") {
234
+ const services = yield* Effect.context();
235
+ // These accessors run inside synchronous transition callbacks. The runner is reached only when an invariant fails.
236
+ const dieInvariant = (message) => Effect.runSyncWith(services)(Effect.die(message));
237
237
  const live = Predicate.isTagged("Live")(source) ? source.restored : undefined;
238
238
  let restoredOptions = options;
239
239
  if (live !== undefined) {
@@ -255,7 +255,7 @@ export const makeVolume = Effect.fnUntraced(function* (source, options, commitPr
255
255
  : VolumeIdentity.make(settings.identity);
256
256
  const incarnation = VolumeIncarnation.make(yield* randomHex128);
257
257
  const epoch = live === undefined ? VolumeEpoch.make(yield* randomHex128) : live.epoch;
258
- const keySecret = live === undefined ? KeySecret.make(yield* randomKeySecret) : live.keySecret;
258
+ const keySecret = live === undefined ? KeySecret.make(yield* randomHex128) : live.keySecret;
259
259
  const identityBytes = Result.getOrThrow(Encoding.decodeHex(identity));
260
260
  const epochBytes = Result.getOrThrow(Encoding.decodeHex(epoch));
261
261
  const keySecretBytes = Result.getOrThrow(Encoding.decodeHex(keySecret));
@@ -421,7 +421,7 @@ export const makeVolume = Effect.fnUntraced(function* (source, options, commitPr
421
421
  }));
422
422
  const current = () => {
423
423
  if (draft === undefined)
424
- throw new Error("Volume mutation outside a transition");
424
+ return dieInvariant("Volume mutation outside a transition");
425
425
  return draft;
426
426
  };
427
427
  const pathOf = (get, ino) => {
@@ -913,14 +913,15 @@ export const makeVolume = Effect.fnUntraced(function* (source, options, commitPr
913
913
  };
914
914
  const directoryNow = (ino) => {
915
915
  const node = view(ino);
916
- if (node?.kind !== "directory")
917
- throw new Error("Directory left the inode table during a transition");
916
+ if (node?.kind !== "directory") {
917
+ return dieInvariant("Directory left the inode table during a transition");
918
+ }
918
919
  return node;
919
920
  };
920
921
  const nodeNow = (ino) => {
921
922
  const node = view(ino);
922
923
  if (node === undefined)
923
- throw new Error("Inode left the table during a transition");
924
+ return dieInvariant("Inode left the table during a transition");
924
925
  return node;
925
926
  };
926
927
  const creationTimes = (times, now) => ({
@@ -1740,29 +1741,26 @@ export const makeVolume = Effect.fnUntraced(function* (source, options, commitPr
1740
1741
  });
1741
1742
  const walkChildren = (directory, path, depth, up) => {
1742
1743
  const listed = referenceFor(directory.ino);
1743
- const frames = [];
1744
- for (const name of [...directory.entries.keys()].sort()) {
1745
- const childIno = directory.entries.get(name);
1746
- const child = childIno === undefined ? undefined : view(childIno);
1744
+ return Arr.flatMap(Arr.sort(directory.entries, byEntryName), ([name, childIno]) => {
1745
+ const child = view(childIno);
1747
1746
  if (child === undefined)
1748
- continue;
1747
+ return [];
1749
1748
  const bytes = nameBytes(name);
1750
- frames.push({
1751
- ino: child.ino,
1752
- kind: child.kind,
1753
- name: bytes,
1754
- key: name,
1755
- path: joinPath(path, bytes),
1756
- parent: directory.ino,
1757
- up,
1758
- depth: depth + 1,
1759
- bytes: child.kind === "directory" ? 0n : child.metadata.size,
1760
- reference: referenceFor(child.ino),
1761
- directory: listed,
1762
- listed: false
1763
- });
1764
- }
1765
- return frames;
1749
+ return [{
1750
+ ino: child.ino,
1751
+ kind: child.kind,
1752
+ name: bytes,
1753
+ key: name,
1754
+ path: joinPath(path, bytes),
1755
+ parent: directory.ino,
1756
+ up,
1757
+ depth: depth + 1,
1758
+ bytes: child.kind === "directory" ? 0n : child.metadata.size,
1759
+ reference: referenceFor(child.ino),
1760
+ directory: listed,
1761
+ listed: false
1762
+ }];
1763
+ });
1766
1764
  };
1767
1765
  const anchorFrame = (parent, key, root) => ({
1768
1766
  ino: root.ino,
@@ -1,5 +1,6 @@
1
1
  import * as Brand from "effect/Brand";
2
2
  import * as Effect from "effect/Effect";
3
+ import * as Order from "effect/Order";
3
4
  import type { Metadata } from "../Metadata.js";
4
5
  import * as InodeTable from "./inodeTable.js";
5
6
  import type { StoredMetadata } from "./metadata.js";
@@ -64,9 +65,9 @@ export interface VolumeState {
64
65
  /** @internal */
65
66
  export declare const getNode: (state: VolumeState, ino: Ino) => Node | undefined;
66
67
  /** @internal */
67
- export declare const byIno: (a: Node, b: Node) => number;
68
+ export declare const byIno: Order.Order<Node>;
68
69
  /** @internal */
69
- export declare const byEntryName: ([a]: readonly [string, Ino], [b]: readonly [string, Ino]) => -1 | 0 | 1;
70
+ export declare const byEntryName: Order.Order<readonly [string, Ino]>;
70
71
  /** @internal */
71
72
  export declare const payloadOf: (node: Node) => Uint8Array | undefined;
72
73
  /** @internal */
@@ -1 +1 @@
1
- {"version":3,"file":"volumeState.d.ts","sourceRoot":"","sources":["../../src/internal/volumeState.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"volumeState.d.ts","sourceRoot":"","sources":["../../src/internal/volumeState.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,7 @@
1
1
  import * as Brand from "effect/Brand";
2
2
  import * as Effect from "effect/Effect";
3
+ import * as Match from "effect/Match";
4
+ import * as Order from "effect/Order";
3
5
  import * as InodeTable from "./inodeTable.js";
4
6
  /** @internal */
5
7
  export const Ino = Brand.nominal();
@@ -19,9 +21,9 @@ export const WALK_YIELD_INTERVAL = 128;
19
21
  /** @internal */
20
22
  export const getNode = (state, ino) => InodeTable.get(state.inodes, ino);
21
23
  /** @internal */
22
- export const byIno = (a, b) => a.ino - b.ino;
24
+ export const byIno = Order.mapInput(Order.Number, (node) => node.ino);
23
25
  /** @internal */
24
- export const byEntryName = ([a], [b]) => (a < b ? -1 : a > b ? 1 : 0);
26
+ export const byEntryName = Order.mapInput(Order.String, ([name]) => name);
25
27
  const inNameOrder = (entries) => {
26
28
  let previous;
27
29
  for (const name of entries.keys()) {
@@ -170,34 +172,30 @@ export const assemble = (specs) => {
170
172
  const owner = Symbol();
171
173
  let inodes = InodeTable.empty();
172
174
  for (const spec of specs) {
173
- const base = { ...spec.metadata, kind: spec.kind, ino: BigInt(spec.ino) };
174
- const node = spec.kind === "directory"
175
- ? {
176
- kind: "directory",
177
- ino: spec.ino,
178
- parent: spec.parent,
179
- name: spec.name,
180
- entries: new Map((edges.get(spec.ino) ?? []).sort(byEntryName)),
181
- metadata: { ...base, nlink: 2 + (subdirectories.get(spec.ino) ?? 0), size: 0n },
182
- revision: spec.revision
183
- }
184
- : spec.kind === "file"
185
- ? {
186
- kind: "file",
187
- ino: spec.ino,
188
- data: spec.data,
189
- links: spec.links,
190
- metadata: { ...base, nlink: spec.links.length, size: BigInt(spec.data.length) },
191
- revision: spec.revision
192
- }
193
- : {
194
- kind: "symlink",
195
- ino: spec.ino,
196
- target: spec.target,
197
- links: spec.links,
198
- metadata: { ...base, nlink: spec.links.length, size: BigInt(spec.target.length) },
199
- revision: spec.revision
200
- };
175
+ const base = { ...spec.metadata, ino: BigInt(spec.ino) };
176
+ const node = Match.value(spec).pipe(Match.discriminator("kind")("directory", (directory) => ({
177
+ kind: "directory",
178
+ ino: directory.ino,
179
+ parent: directory.parent,
180
+ name: directory.name,
181
+ entries: new Map((edges.get(directory.ino) ?? []).sort(byEntryName)),
182
+ metadata: { ...base, kind: "directory", nlink: 2 + (subdirectories.get(directory.ino) ?? 0), size: 0n },
183
+ revision: directory.revision
184
+ })), Match.discriminator("kind")("file", (file) => ({
185
+ kind: "file",
186
+ ino: file.ino,
187
+ data: file.data,
188
+ links: file.links,
189
+ metadata: { ...base, kind: "file", nlink: file.links.length, size: BigInt(file.data.length) },
190
+ revision: file.revision
191
+ })), Match.discriminator("kind")("symlink", (symlink) => ({
192
+ kind: "symlink",
193
+ ino: symlink.ino,
194
+ target: symlink.target,
195
+ links: symlink.links,
196
+ metadata: { ...base, kind: "symlink", nlink: symlink.links.length, size: BigInt(symlink.target.length) },
197
+ revision: symlink.revision
198
+ })), Match.exhaustive);
201
199
  inodes = InodeTable.set(inodes, spec.ino, node, owner);
202
200
  }
203
201
  return { inodes, open: new Map(), nextInode, revision, entries, usedBytes };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@effect-vfs/core",
3
- "version": "0.6.0",
3
+ "version": "0.7.0",
4
4
  "description": "A runtime-neutral virtual filesystem core for Effect.",
5
5
  "homepage": "https://github.com/lloydrichards/effect-virtual-fs#readme",
6
6
  "bugs": {
@@ -21,18 +21,18 @@
21
21
  "scripts": {
22
22
  "build": "tsc --project tsconfig.build.json && tsc --project compatibility/node-next/tsconfig.json",
23
23
  "clean": "git clean -xdf .cache .turbo dist docs node_modules tsconfig.tsbuildinfo",
24
- "test": "vitest run",
24
+ "test": "bun --bun run vitest run --config ../../vitest.config.ts --project core",
25
25
  "type-check": "tsc --noEmit && tsc --project contracts/tsconfig.json"
26
26
  },
27
27
  "peerDependencies": {
28
- "effect": "4.0.0-rc.115"
28
+ "effect": "4.0.0-rc.117"
29
29
  },
30
30
  "devDependencies": {
31
- "@effect/platform-bun": "4.0.0-rc.115",
32
- "@effect/platform-node-shared": "4.0.0-rc.115",
33
- "@effect/vitest": "4.0.0-rc.115",
31
+ "@effect/platform-bun": "4.0.0-rc.117",
32
+ "@effect/platform-node-shared": "4.0.0-rc.117",
33
+ "@effect/vitest": "4.0.0-rc.117",
34
34
  "@repo/config-typescript": "workspace:*",
35
- "effect": "4.0.0-rc.115"
35
+ "effect": "4.0.0-rc.117"
36
36
  },
37
37
  "exports": {
38
38
  ".": {
@@ -1,18 +0,0 @@
1
- import * as Effect from "effect/Effect";
2
- import type { FsFailure } from "../VfsError.js";
3
- type CommitOutcome = "committed" | "rejected" | "unknown";
4
- /** @internal */
5
- export interface CommitProvider<State> {
6
- /** Preparation failures leave the volume available. */
7
- readonly prepare?: (candidate: State) => Effect.Effect<void, FsFailure>;
8
- readonly commit: (candidate: State) => Effect.Effect<CommitOutcome>;
9
- }
10
- /** @internal */
11
- export interface Classified {
12
- readonly available: boolean;
13
- readonly failure: FsFailure | undefined;
14
- }
15
- /** @internal */
16
- export declare const offerCommit: <State>(provider: CommitProvider<State>, operation: string, candidate: State, cleanup: boolean) => Effect.Effect<Classified>;
17
- export {};
18
- //# sourceMappingURL=stagedState.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"stagedState.d.ts","sourceRoot":"","sources":["../../src/internal/stagedState.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAA;AAG/C,KAAK,aAAa,GAAG,WAAW,GAAG,UAAU,GAAG,SAAS,CAAA;AAEzD,gBAAgB;AAChB,MAAM,WAAW,cAAc,CAAC,KAAK;IACnC,uDAAuD;IACvD,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,SAAS,CAAC,CAAA;IACvE,QAAQ,CAAC,MAAM,EAAE,CAAC,SAAS,EAAE,KAAK,KAAK,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,CAAA;CACpE;AAED,gBAAgB;AAChB,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAA;IAC3B,QAAQ,CAAC,OAAO,EAAE,SAAS,GAAG,SAAS,CAAA;CACxC;AAGD,gBAAgB;AAChB,eAAO,MAAM,WAAW,GAAI,KAAK,YACrB,cAAc,CAAC,KAAK,CAAC,aACpB,MAAM,aACN,KAAK,WACP,OAAO,KACf,MAAM,CAAC,MAAM,CAAC,UAAU,CAgBvB,CAAA"}
@@ -1,18 +0,0 @@
1
- import * as Effect from "effect/Effect";
2
- import * as Exit from "effect/Exit";
3
- import { fsFailure } from "./errors.js";
4
- // An unknown commit outcome stops the volume; definite rejection stops it only during cleanup.
5
- /** @internal */
6
- export const offerCommit = (provider, operation, candidate, cleanup) => Effect.map(Effect.exit(Effect.suspend(() => provider.commit(candidate))), (committed) => {
7
- // TODO(#186): Preserve the cause of a provider defect or interruption.
8
- if (Exit.isFailure(committed)) {
9
- return { available: false, failure: fsFailure("OutcomeUnknown", operation) };
10
- }
11
- if (committed.value === "rejected") {
12
- return { available: !cleanup, failure: fsFailure("StorageRejected", operation) };
13
- }
14
- if (committed.value === "unknown") {
15
- return { available: false, failure: fsFailure("OutcomeUnknown", operation) };
16
- }
17
- return { available: true, failure: undefined };
18
- });