@effect-vfs/core 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/dist/BytePath.d.ts +4 -1
  2. package/dist/BytePath.d.ts.map +1 -1
  3. package/dist/BytePath.js +6 -4
  4. package/dist/Caller.d.ts +105 -66
  5. package/dist/Caller.d.ts.map +1 -1
  6. package/dist/Caller.js +15 -87
  7. package/dist/LiveVolume.d.ts +6 -1
  8. package/dist/LiveVolume.d.ts.map +1 -1
  9. package/dist/LiveVolume.js +8 -1
  10. package/dist/Search.d.ts +32 -8
  11. package/dist/Search.d.ts.map +1 -1
  12. package/dist/Search.js +14 -13
  13. package/dist/SynchronousAdapter.d.ts +58 -0
  14. package/dist/SynchronousAdapter.d.ts.map +1 -0
  15. package/dist/SynchronousAdapter.js +116 -0
  16. package/dist/Target.d.ts +7 -91
  17. package/dist/Target.d.ts.map +1 -1
  18. package/dist/Target.js +7 -47
  19. package/dist/Testing.d.ts +5 -2
  20. package/dist/Testing.d.ts.map +1 -1
  21. package/dist/Testing.js +2 -1
  22. package/dist/VfsError.d.ts +1 -1
  23. package/dist/VfsError.d.ts.map +1 -1
  24. package/dist/VirtualFileSystem.d.ts +126 -43
  25. package/dist/VirtualFileSystem.d.ts.map +1 -1
  26. package/dist/VirtualFileSystem.js +25 -21
  27. package/dist/Volume.d.ts +6 -1
  28. package/dist/Volume.d.ts.map +1 -1
  29. package/dist/Volume.js +6 -2
  30. package/dist/internal/adapterBinding.d.ts +13 -0
  31. package/dist/internal/adapterBinding.d.ts.map +1 -0
  32. package/dist/internal/adapterBinding.js +13 -0
  33. package/dist/internal/bytePath.d.ts +6 -1
  34. package/dist/internal/bytePath.d.ts.map +1 -1
  35. package/dist/internal/callerWatch.d.ts +5 -1
  36. package/dist/internal/callerWatch.d.ts.map +1 -1
  37. package/dist/internal/callerWatch.js +4 -3
  38. package/dist/internal/engine.d.ts +1305 -0
  39. package/dist/internal/engine.d.ts.map +1 -0
  40. package/dist/internal/engine.js +3458 -0
  41. package/dist/internal/errors.d.ts +1 -33
  42. package/dist/internal/errors.d.ts.map +1 -1
  43. package/dist/internal/errors.js +1 -45
  44. package/dist/internal/fixture.d.ts +2 -1
  45. package/dist/internal/fixture.d.ts.map +1 -1
  46. package/dist/internal/fixture.js +7 -3
  47. package/dist/internal/image.d.ts +4 -1
  48. package/dist/internal/image.d.ts.map +1 -1
  49. package/dist/internal/image.js +6 -3
  50. package/dist/internal/lines.d.ts.map +1 -1
  51. package/dist/internal/lines.js +3 -2
  52. package/dist/internal/liveImage.d.ts +4 -2
  53. package/dist/internal/liveImage.d.ts.map +1 -1
  54. package/dist/internal/liveImage.js +3 -1
  55. package/dist/internal/merkle.d.ts +7 -4
  56. package/dist/internal/merkle.d.ts.map +1 -1
  57. package/dist/internal/merkle.js +6 -4
  58. package/dist/internal/search.d.ts +3 -1
  59. package/dist/internal/search.d.ts.map +1 -1
  60. package/dist/internal/search.js +4 -3
  61. package/dist/internal/snapshotDelta.d.ts +11 -7
  62. package/dist/internal/snapshotDelta.d.ts.map +1 -1
  63. package/dist/internal/snapshotDelta.js +15 -9
  64. package/dist/internal/snapshotMerge.d.ts +1 -1
  65. package/dist/internal/snapshotMerge.d.ts.map +1 -1
  66. package/dist/internal/snapshotMerge.js +5 -2
  67. package/dist/internal/tree.d.ts +4 -2
  68. package/dist/internal/tree.d.ts.map +1 -1
  69. package/dist/internal/tree.js +4 -2
  70. package/dist/internal/virtualFileSystem.d.ts +20 -49
  71. package/dist/internal/virtualFileSystem.d.ts.map +1 -1
  72. package/dist/internal/virtualFileSystem.js +75 -2082
  73. package/dist/internal/volumeLimits.d.ts +1 -0
  74. package/dist/internal/volumeLimits.d.ts.map +1 -1
  75. package/dist/internal/volumeLimits.js +3 -2
  76. package/dist/internal/volumeState.d.ts +10 -79
  77. package/dist/internal/volumeState.d.ts.map +1 -1
  78. package/dist/internal/volumeState.js +9 -79
  79. package/dist/internal/watchHub.d.ts +3 -3
  80. package/dist/internal/watchHub.d.ts.map +1 -1
  81. package/dist/internal/watchHub.js +4 -2
  82. package/package.json +14 -9
  83. package/dist/internal/confinement.d.ts +0 -16
  84. package/dist/internal/confinement.d.ts.map +0 -1
  85. package/dist/internal/confinement.js +0 -79
  86. package/dist/internal/directoryEntries.d.ts +0 -6
  87. package/dist/internal/directoryEntries.d.ts.map +0 -1
  88. package/dist/internal/directoryEntries.js +0 -79
  89. package/dist/internal/inodeTable.d.ts +0 -20
  90. package/dist/internal/inodeTable.d.ts.map +0 -1
  91. package/dist/internal/inodeTable.js +0 -48
  92. package/dist/internal/path.d.ts +0 -56
  93. package/dist/internal/path.d.ts.map +0 -1
  94. package/dist/internal/path.js +0 -141
  95. package/dist/internal/resolution.d.ts +0 -139
  96. package/dist/internal/resolution.d.ts.map +0 -1
  97. package/dist/internal/resolution.js +0 -332
  98. package/dist/internal/testSeams.d.ts +0 -17
  99. package/dist/internal/testSeams.d.ts.map +0 -1
  100. package/dist/internal/testSeams.js +0 -20
  101. package/dist/internal/tokenRegistry.d.ts +0 -83
  102. package/dist/internal/tokenRegistry.d.ts.map +0 -1
  103. package/dist/internal/tokenRegistry.js +0 -146
  104. package/dist/internal/turnstile.d.ts +0 -8
  105. package/dist/internal/turnstile.d.ts.map +0 -1
  106. package/dist/internal/turnstile.js +0 -43
@@ -1,332 +0,0 @@
1
- import * as ByteSize from "effect/ByteSize";
2
- import * as Effect from "effect/Effect";
3
- import * as Hex from "effect/encoding/Hex";
4
- import * as Predicate from "effect/Predicate";
5
- import * as Result from "effect/Result";
6
- import { isEntry, isTarget } from "../Target.js";
7
- import { make as makeConfinement } from "./confinement.js";
8
- import { DOT_DOT_HEX, DOT_HEX, isAttachedBytes, isDotComponent, isWellFormed, MAX_NAME_BYTES, MAX_SYMLINK_TRAVERSALS, nameBytes, ownedPath, preparePath, SLASH_BYTE } from "./path.js";
9
- import * as TokenRegistry from "./tokenRegistry.js";
10
- import { ROOT_INO } from "./volumeState.js";
11
- const encoder = new TextEncoder();
12
- const EMPTY_AUTHORITIES = [];
13
- /** @internal */
14
- export const inGroup = (identity, gid) => identity.gid === gid || identity.groups.includes(gid);
15
- /** @internal */
16
- export const permitted = (node, identity) => {
17
- const shift = identity.uid === node.metadata.uid ? 6 : inGroup(identity, node.metadata.gid) ? 3 : 0;
18
- return (node.metadata.mode >> shift) & 0o7;
19
- };
20
- /** @internal */
21
- export const authorize = (node, identity, bits, op) => identity.privileged || (permitted(node, identity) & bits) === bits
22
- ? Effect.void
23
- : Effect.fail(op.fail("AccessDenied"));
24
- /** @internal */
25
- export const entryName = (input, op) => {
26
- if (Predicate.isString(input) && !isWellFormed(input))
27
- return Result.fail(op.fail("InvalidPathEncoding"));
28
- const bytes = Predicate.isString(input) ? encoder.encode(input) : input;
29
- if (!isAttachedBytes(bytes) || bytes.length === 0 || bytes.length > MAX_NAME_BYTES || bytes.includes(0) ||
30
- bytes.includes(SLASH_BYTE))
31
- return Result.fail(op.fail("InvalidArgument"));
32
- return Result.succeed(Hex.encode(new Uint8Array(bytes)));
33
- };
34
- /** @internal */
35
- export const make = ({ caller, get, identity, maxPathBytes, registry, confinement, additional, authorityView }) => {
36
- const visibleRoot = confinement?.root ?? ROOT_INO;
37
- const prepare = (input, op) => preparePath(input, op.operation, maxPathBytes);
38
- const prepareEntry = (input, op) => isEntry(input)
39
- ? Result.map(entryName(input.name, op), (name) => ({ kind: "entry", directory: input.directory, name }))
40
- : isTarget(input)
41
- ? Result.map(prepare(input.path, op), (path) => ({ kind: "path", path, base: input.relativeTo }))
42
- : Result.map(prepare(input, op), (path) => ({ kind: "path", path, base: undefined }));
43
- const nodeAt = Effect.fnUntraced(function* (lookup, ino) {
44
- const node = lookup(ino);
45
- return node === undefined ? yield* Effect.die("Inode left the table during resolution") : node;
46
- });
47
- const directoryAt = Effect.fnUntraced(function* (lookup, ino) {
48
- const node = yield* nodeAt(lookup, ino);
49
- return node.kind !== "directory" ? yield* Effect.die("Directory left the table during resolution") : node;
50
- });
51
- const callerDirectory = Effect.fnUntraced(function* (lookup, op) {
52
- const ino = TokenRegistry.inode(caller);
53
- const node = ino === undefined ? undefined : lookup(ino);
54
- return node?.kind !== "directory" ? yield* op.fail("ClosedCaller") : node;
55
- });
56
- const walk = Effect.fnUntraced(function* (path, base, op, mode, referencedBase) {
57
- const pathOp = op.at(path.input);
58
- const baseAuthorities = base === undefined
59
- ? EMPTY_AUTHORITIES
60
- : registry.handleBoundaries(base).map((roots) => makeConfinement(authorityView, roots));
61
- const authorizeDirectory = baseAuthorities.length === 0 ? authorize : Effect.fnUntraced(function* (node, callerIdentity, bits, context) {
62
- for (const authority of baseAuthorities)
63
- yield* authority.check(node, context, true);
64
- yield* authorize(node, callerIdentity, bits, context);
65
- });
66
- const parentOnly = mode.kind === "Parent";
67
- const allowMissing = mode.kind === "OrCreate" && mode.create !== "never";
68
- const followFinalSymlink = mode.kind === "Node"
69
- ? mode.followFinalSymlink !== false
70
- : mode.kind === "Parent" || mode.finalSymlink === "follow";
71
- const createMissing = mode.kind === "Node" ? mode.createMissing : undefined;
72
- if (!TokenRegistry.isOpen(caller)) {
73
- return yield* pathOp.fail("ClosedCaller");
74
- }
75
- if (additional !== undefined && additional.length > 0) {
76
- for (const authority of additional)
77
- yield* authority.alive(pathOp);
78
- }
79
- if (confinement !== undefined)
80
- yield* confinement.alive(pathOp);
81
- if (path.absolute && base !== undefined &&
82
- (confinement !== undefined || (additional?.length ?? 0) > 0 || baseAuthorities.length > 0)) {
83
- const node = yield* TokenRegistry.nodeOrFail(yield* registry.resolve(base, "directory"), "handle", pathOp);
84
- yield* registry.authorizeHandle(base, node, pathOp);
85
- if (confinement !== undefined)
86
- yield* confinement.check(node, pathOp);
87
- if (additional !== undefined && additional.length > 0) {
88
- for (const authority of additional)
89
- yield* authority.check(node, pathOp);
90
- }
91
- }
92
- // Reads remain live so recursive creation can revisit nodes added earlier in this walk.
93
- const lookup = yield* authorityView;
94
- let current = path.absolute ? (yield* nodeAt(lookup, visibleRoot)) : yield* callerDirectory(lookup, pathOp);
95
- if (!path.absolute && referencedBase !== undefined) {
96
- current = referencedBase;
97
- yield* authorize(current, identity, 0o1, op);
98
- }
99
- else if (!path.absolute && base !== undefined) {
100
- const directory = yield* TokenRegistry.nodeOrFail(yield* registry.resolve(base, "directory"), "handle", pathOp);
101
- if (directory.kind !== "directory")
102
- return yield* pathOp.fail("InvalidHandle");
103
- yield* registry.authorizeHandle(base, directory, pathOp);
104
- current = directory;
105
- yield* authorize(current, identity, 0o1, pathOp);
106
- }
107
- if (confinement !== undefined)
108
- yield* confinement.check(current, pathOp);
109
- if (additional !== undefined && additional.length > 0) {
110
- for (const authority of additional)
111
- yield* authority.check(current, pathOp);
112
- }
113
- if (current.metadata.nlink === 0) {
114
- return yield* pathOp.fail("NotFound");
115
- }
116
- let work = path;
117
- let parent;
118
- let name;
119
- let traversals = 0;
120
- // Leading components that came from a symbolic link's target. Only the components the caller wrote are
121
- // created, so a dangling link stays missing, as it does for mkdir -p.
122
- let linked = 0;
123
- for (let index = 0; index < work.components.length - (parentOnly ? 1 : 0); index++) {
124
- if (current.kind !== "directory") {
125
- return yield* pathOp.fail("NotDirectory");
126
- }
127
- yield* authorizeDirectory(current, identity, 0o1, pathOp);
128
- const component = work.components[index];
129
- if (component === undefined)
130
- break;
131
- if (component === DOT_HEX)
132
- continue;
133
- if (component === DOT_DOT_HEX) {
134
- current = yield* directoryAt(lookup, confinement !== undefined && current.ino === visibleRoot ? current.ino : current.parent);
135
- parent = undefined;
136
- name = undefined;
137
- continue;
138
- }
139
- parent = current;
140
- name = component;
141
- const childIno = current.entries.get(component);
142
- const child = childIno === undefined ? undefined : lookup(childIno);
143
- if (child === undefined) {
144
- if (allowMissing && index === work.components.length - 1) {
145
- return { kind: "missing", parent, name, op: pathOp };
146
- }
147
- if (createMissing !== undefined && index >= linked) {
148
- current = yield* createMissing(current, component, index === work.components.length - 1);
149
- continue;
150
- }
151
- return yield* pathOp.fail("NotFound");
152
- }
153
- if (child.kind === "symlink" && (followFinalSymlink || index < work.components.length - 1 || work.trailingSlash)) {
154
- if (child.target.length === 0) {
155
- return yield* pathOp.fail("NotFound");
156
- }
157
- if (++traversals > MAX_SYMLINK_TRAVERSALS) {
158
- return yield* pathOp.fail("SymlinkLoop");
159
- }
160
- const suffix = work.suffixes[index] ?? new Uint8Array(0);
161
- const remaining = work.components.length - index - 1;
162
- if (maxPathBytes !== undefined &&
163
- ByteSize.isGreaterThan(ByteSize.bytes(child.target.length + suffix.length), maxPathBytes)) {
164
- return yield* pathOp.fail("PathTooLong");
165
- }
166
- const expansion = new Uint8Array(child.target.length + suffix.length);
167
- expansion.set(child.target);
168
- expansion.set(suffix, child.target.length);
169
- const expanded = preparePath(ownedPath(expansion), op.operation, maxPathBytes);
170
- // The expansion is synthetic: its per-component limits are the caller's to hear about,
171
- // but the path in the error has to be the one the caller passed in, so the
172
- // expansion's own failure is not kept as the cause.
173
- if (Result.isFailure(expanded)) {
174
- return yield* pathOp.fail(expanded.failure.code);
175
- }
176
- work = expanded.success;
177
- linked = work.components.length - remaining + Math.max(0, linked - index - 1);
178
- if (work.absolute)
179
- current = yield* nodeAt(lookup, visibleRoot);
180
- index = -1;
181
- }
182
- else
183
- current = child;
184
- }
185
- if (baseAuthorities.length > 0) {
186
- for (const authority of baseAuthorities)
187
- yield* authority.check(current, pathOp, true);
188
- }
189
- if (!parentOnly && work.trailingSlash && current.kind !== "directory") {
190
- return yield* pathOp.fail("NotDirectory");
191
- }
192
- return parent === undefined || name === undefined
193
- ? { kind: "node", node: current, op: pathOp }
194
- : { kind: "entry", node: current, parent, name, op: pathOp };
195
- });
196
- const resolveNode = Effect.fnUntraced(function* (target, mode, op) {
197
- if (Predicate.isTagged(target, "Path")) {
198
- const path = yield* Effect.fromResult(prepare(target.path, op));
199
- const result = yield* walk(path, target.relativeTo, op, {
200
- ...mode,
201
- followFinalSymlink: mode.final === true
202
- ? mode.followFinalSymlink ?? true
203
- : target.followFinalSymlink ?? mode.followFinalSymlink ?? true
204
- });
205
- return result.kind === "missing" ? yield* result.op.fail("NotFound") : result;
206
- }
207
- if (!TokenRegistry.isOpen(caller))
208
- return yield* op.fail("ClosedCaller");
209
- const node = yield* TokenRegistry.nodeOrFail(yield* (Predicate.isTagged(target, "Reference")
210
- ? registry.resolve(target.reference, "reference")
211
- : registry.resolve(target.handle, "handle")), Predicate.isTagged(target, "Reference") ? "reference" : "handle", op);
212
- if (!Predicate.isTagged(target, "Reference"))
213
- yield* registry.authorizeHandle(target.handle, node, op);
214
- if (confinement !== undefined)
215
- yield* confinement.check(node, op);
216
- if (additional !== undefined && additional.length > 0) {
217
- for (const authority of additional)
218
- yield* authority.check(node, op);
219
- }
220
- return { kind: "node", node, op };
221
- });
222
- const directory = Effect.fnUntraced(function* (target, op) {
223
- const resolved = yield* resolveNode(target, { kind: "Node" }, op);
224
- if (resolved.node.kind !== "directory")
225
- return yield* resolved.op.fail("NotDirectory");
226
- return { node: resolved.node, op: resolved.op };
227
- });
228
- const entryDirectory = Effect.fnUntraced(function* (target, op) {
229
- const resolved = yield* directory(target, op);
230
- if (resolved.node.metadata.nlink === 0)
231
- return yield* resolved.op.fail("NotFound");
232
- return resolved;
233
- });
234
- const parent = Effect.fnUntraced(function* (prepared, op) {
235
- if (prepared.kind === "entry") {
236
- const resolved = yield* entryDirectory(prepared.directory, op);
237
- return {
238
- kind: "parent",
239
- parent: resolved.node.ino,
240
- name: prepared.name,
241
- trailingSlash: false,
242
- addressing: "entry",
243
- op
244
- };
245
- }
246
- const resolved = yield* walk(prepared.path, prepared.base, op, { kind: "Parent" });
247
- if (resolved.kind === "missing")
248
- return yield* resolved.op.fail("NotFound");
249
- if (resolved.node.kind !== "directory")
250
- return yield* resolved.op.fail("NotDirectory");
251
- return {
252
- kind: "parent",
253
- parent: resolved.node.ino,
254
- name: prepared.path.components.at(-1),
255
- trailingSlash: prepared.path.trailingSlash,
256
- addressing: "path",
257
- op: resolved.op
258
- };
259
- });
260
- const creationResult = (result, trailingSlash, addressing, op, origin) => {
261
- if (result.kind === "node")
262
- return Effect.fail(op.fail("IsDirectory"));
263
- const entry = {
264
- parent: result.parent,
265
- name: result.name,
266
- trailingSlash,
267
- addressing,
268
- op,
269
- origin: origin ?? { parent: result.parent, name: result.name }
270
- };
271
- return Effect.succeed(result.kind === "missing" ? { ...entry, kind: "missing" } : { ...entry, kind: "existing", node: result.node });
272
- };
273
- const orCreate = Effect.fnUntraced(function* (prepared, mode, op) {
274
- if (prepared.kind === "path") {
275
- const pathOp = op.at(prepared.path.input);
276
- if (mode.create === "exclusive") {
277
- const exists = yield* Effect.result(walk(prepared.path, prepared.base, op, { kind: "Node", followFinalSymlink: false }));
278
- if (Result.isSuccess(exists))
279
- return yield* pathOp.fail("AlreadyExists");
280
- if (exists.failure.code !== "NotFound")
281
- return yield* exists.failure;
282
- }
283
- return yield* creationResult(yield* walk(prepared.path, prepared.base, op, mode), prepared.path.trailingSlash, "path", pathOp);
284
- }
285
- const { node: parent } = yield* entryDirectory(prepared.directory, op);
286
- if (isDotComponent(prepared.name))
287
- return yield* op.fail("InvalidArgument");
288
- yield* authorize(parent, identity, 0o1, op);
289
- const directIno = parent.entries.get(prepared.name);
290
- const direct = directIno === undefined ? undefined : yield* get(directIno);
291
- if (mode.beforeFollow !== undefined)
292
- yield* mode.beforeFollow(parent, direct);
293
- if (mode.create === "exclusive" && mode.entryExclusive !== "afterSymlink" && direct !== undefined) {
294
- return yield* op.fail("AlreadyExists");
295
- }
296
- let result = direct === undefined
297
- ? { kind: "missing", parent, name: prepared.name, op }
298
- : { kind: "entry", node: direct, parent, name: prepared.name, op };
299
- if (direct?.kind === "symlink" && mode.finalSymlink === "follow") {
300
- const path = yield* Effect.fromResult(preparePath(ownedPath(nameBytes(prepared.name)), op.operation, undefined));
301
- // Following an entry link must retain the directory capability's boundaries.
302
- const base = Predicate.isTagged(prepared.directory, "Handle")
303
- ? prepared.directory.handle
304
- : Predicate.isTagged(prepared.directory, "Path")
305
- ? prepared.directory.relativeTo
306
- : undefined;
307
- result = yield* walk(path, base, op, mode, parent);
308
- // Entry writeFile rejects a followed directory before checking exclusive creation.
309
- if (mode.create === "exclusive" && mode.entryExclusive === "afterSymlink" &&
310
- (result.kind === "node" || (result.kind === "entry" && result.node.kind === "directory"))) {
311
- return yield* op.fail("IsDirectory");
312
- }
313
- }
314
- if (result.kind === "missing" && mode.create === "never")
315
- return yield* op.fail("NotFound");
316
- if (mode.create === "exclusive" && mode.entryExclusive === "afterSymlink" && result.kind !== "missing") {
317
- return yield* op.fail("AlreadyExists");
318
- }
319
- return yield* creationResult(result, false, "entry", op, { parent, name: prepared.name });
320
- });
321
- function resolve(input, mode, op) {
322
- if (isTarget(input)) {
323
- return mode.kind === "Node" ? resolveNode(input, mode, op) : Effect.die("A node target requires Node resolution");
324
- }
325
- return mode.kind === "Parent"
326
- ? parent(input, op)
327
- : mode.kind === "OrCreate"
328
- ? orCreate(input, mode, op)
329
- : Effect.die("An entry requires Parent or OrCreate resolution");
330
- }
331
- return { prepare, prepareEntry, resolve, directory, entryDirectory };
332
- };
@@ -1,17 +0,0 @@
1
- import * as Context from "effect/Context";
2
- import * as Effect from "effect/Effect";
3
- /** @internal */
4
- export interface VolumeTestSeams {
5
- readonly afterSubscribe: Effect.Effect<void>;
6
- readonly betweenSnapshotAndSummary: Effect.Effect<void>;
7
- readonly beforeTreeRemoval: Effect.Effect<void>;
8
- }
9
- /**
10
- * Read in the caller's fiber, so a test supplies it with `withVolumeTestSeams` on the call under test.
11
- *
12
- * @internal
13
- */
14
- export declare const VolumeTestSeams: Context.Reference<VolumeTestSeams>;
15
- /** @internal */
16
- export declare const withVolumeTestSeams: (seams: Partial<VolumeTestSeams>) => <XA, E, R>(self: Effect.Effect<XA, E, R>) => Effect.Effect<XA, E, R>;
17
- //# sourceMappingURL=testSeams.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"testSeams.d.ts","sourceRoot":"","sources":["../../src/internal/testSeams.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,OAAO,MAAM,gBAAgB,CAAA;AACzC,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAEvC,gBAAgB;AAChB,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAC5C,QAAQ,CAAC,yBAAyB,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;IAEvD,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAA;CAChD;AAED;;;;GAIG;AACH,eAAO,MAAM,eAAe,EAAE,OAAO,CAAC,SAAS,CAAC,eAAe,CAS9D,CAAA;AAED,gBAAgB;AAChB,eAAO,MAAM,mBAAmB,UAAW,OAAO,CAAC,eAAe,CAAC,yEAK9D,CAAA"}
@@ -1,20 +0,0 @@
1
- import * as Context from "effect/Context";
2
- import * as Effect from "effect/Effect";
3
- /**
4
- * Read in the caller's fiber, so a test supplies it with `withVolumeTestSeams` on the call under test.
5
- *
6
- * @internal
7
- */
8
- export const VolumeTestSeams = Context.Reference("@effect-vfs/core/VolumeTestSeams", {
9
- defaultValue: () => ({
10
- afterSubscribe: Effect.void,
11
- betweenSnapshotAndSummary: Effect.void,
12
- beforeTreeRemoval: Effect.void
13
- })
14
- });
15
- /** @internal */
16
- export const withVolumeTestSeams = (seams) => Effect.updateService(VolumeTestSeams, (current) => ({
17
- afterSubscribe: seams.afterSubscribe ?? current.afterSubscribe,
18
- betweenSnapshotAndSummary: seams.betweenSnapshotAndSummary ?? current.betweenSnapshotAndSummary,
19
- beforeTreeRemoval: seams.beforeTreeRemoval ?? current.beforeTreeRemoval
20
- }));
@@ -1,83 +0,0 @@
1
- import * as Effect from "effect/Effect";
2
- import * as Scope from "effect/Scope";
3
- import type { FsFailure } from "../VfsError.js";
4
- import type { DirectoryHandle, FileHandle, ObjectReference } from "../VirtualFileSystem.js";
5
- import type { OpContext } from "./errors.js";
6
- import type { Ino, Node } from "./volumeState.js";
7
- /** @internal */
8
- export type HandleLifecycle = {
9
- readonly kind: "acquiring";
10
- } | {
11
- readonly kind: "open";
12
- readonly ino: Ino;
13
- } | {
14
- readonly kind: "releasedPendingCommit";
15
- readonly ino: Ino | undefined;
16
- } | {
17
- readonly kind: "closed";
18
- };
19
- /** @internal */
20
- export interface HandleReference {
21
- readonly volume: symbol;
22
- lifecycle: HandleLifecycle;
23
- namespaceRoots?: ReadonlyArray<Ino> | undefined;
24
- boundaries?: ReadonlyArray<ReadonlyArray<Ino>> | undefined;
25
- authority?: ((node: Node, op: OpContext) => Effect.Effect<void, FsFailure>) | undefined;
26
- scope: Scope.Closeable | undefined;
27
- }
28
- /** @internal */
29
- export interface FileReference extends HandleReference {
30
- offset: bigint;
31
- readonly access: "read" | "write" | "readWrite";
32
- readonly append: boolean;
33
- }
34
- /** @internal */
35
- export type DirectoryReference = HandleReference;
36
- /** @internal */
37
- export type Token = ObjectReference | FileHandle | DirectoryHandle;
38
- /** @internal */
39
- export type Resolution = {
40
- readonly kind: "unknown";
41
- } | {
42
- readonly kind: "foreign";
43
- } | {
44
- readonly kind: "stale";
45
- } | {
46
- readonly kind: "live";
47
- readonly node: Node;
48
- };
49
- /** @internal */
50
- export declare const inode: (reference: HandleReference) => Ino | undefined;
51
- /** @internal */
52
- export declare const isOpen: (reference: HandleReference) => boolean;
53
- /** @internal */
54
- export declare const publish: (reference: HandleReference, ino: Ino) => void;
55
- /** @internal */
56
- export declare const released: (reference: HandleReference) => void;
57
- /** @internal */
58
- export declare const release: <E, R>(reference: HandleReference, releaseInode: (ino: Ino) => Effect.Effect<void, E, R>, afterRelease: (action: () => void) => Effect.Effect<void, never, R>) => Effect.Effect<void, E, R>;
59
- /** @internal */
60
- export declare const closeReleasedScope: (reference: HandleReference) => Effect.Effect<void, never, never>;
61
- /** @internal */
62
- export declare const finalize: (reference: HandleReference, close: Effect.Effect<unknown, FsFailure>, fallback: Effect.Effect<void>) => Effect.Effect<void, never, never>;
63
- /** @internal */
64
- export declare const acquire: <A, E, R, ReleaseR, CoordinateR>(reference: HandleReference, coordinate: (acquire: Effect.Effect<A, E, R | ReleaseR>) => Effect.Effect<A, E | FsFailure, CoordinateR>, acquisition: Effect.Effect<A, E, R>, releaseAcquired: () => Effect.Effect<void, never, ReleaseR>, finalizer: Effect.Effect<void, never, never>) => Effect.Effect<A, E | FsFailure, CoordinateR | Scope.Scope>;
65
- /** @internal */
66
- export declare const nodeOrFail: (result: Resolution, addressing: "reference" | "handle", op: OpContext) => Effect.Effect<never, FsFailure, never> | Effect.Effect<Node, never, never>;
67
- /** @internal */
68
- export declare const make: (volume: symbol, get: (ino: Ino) => Effect.Effect<Node | undefined>) => {
69
- referenceIdentity: (token: ObjectReference, op: OpContext) => Effect.Effect<Ino, FsFailure>;
70
- handleBoundaries: (token: Token) => readonly (readonly Ino[])[];
71
- authorizeHandle: (token: Token, node: Node, op: OpContext) => Effect.Effect<void, FsFailure, never>;
72
- resolve: (token: Token, addressing: "reference" | "handle" | "directory") => Effect.Effect<Resolution, never, never>;
73
- resolveInode: (ino: Ino) => Effect.Effect<Resolution>;
74
- referenceFor: (ino: Ino) => ObjectReference;
75
- forget: (ino: Ino) => boolean;
76
- file: (access: FileReference["access"], append: boolean) => FileReference;
77
- directory: (ino?: Ino) => DirectoryReference;
78
- registerFile: (handle: FileHandle, reference: FileReference) => WeakMap<object, FileReference>;
79
- registerDirectory: (handle: DirectoryHandle, reference: DirectoryReference) => WeakMap<object, HandleReference>;
80
- };
81
- /** @internal */
82
- export type TokenRegistry = ReturnType<typeof make>;
83
- //# sourceMappingURL=tokenRegistry.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"tokenRegistry.d.ts","sourceRoot":"","sources":["../../src/internal/tokenRegistry.ts"],"names":[],"mappings":"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"}
@@ -1,146 +0,0 @@
1
- import * as Effect from "effect/Effect";
2
- import * as Exit from "effect/Exit";
3
- import * as Predicate from "effect/Predicate";
4
- import * as Scope from "effect/Scope";
5
- import { ObjectReferenceId } from "../Caller.js";
6
- // Shared across volumes so a genuine token from another registry is distinguishable from a forgery.
7
- const references = new WeakMap();
8
- const files = new WeakMap();
9
- const directories = new WeakMap();
10
- /** @internal */
11
- export const inode = (reference) => reference.lifecycle.kind === "open" || reference.lifecycle.kind === "releasedPendingCommit"
12
- ? reference.lifecycle.ino
13
- : undefined;
14
- /** @internal */
15
- export const isOpen = (reference) => reference.lifecycle.kind === "open";
16
- /** @internal */
17
- export const publish = (reference, ino) => {
18
- reference.lifecycle = reference.lifecycle.kind === "releasedPendingCommit" || reference.lifecycle.kind === "closed"
19
- ? { kind: "releasedPendingCommit", ino }
20
- : { kind: "open", ino };
21
- };
22
- /** @internal */
23
- export const released = (reference) => {
24
- reference.lifecycle = reference.lifecycle.kind === "acquiring" ||
25
- (reference.lifecycle.kind === "releasedPendingCommit" && reference.lifecycle.ino === undefined)
26
- ? { kind: "releasedPendingCommit", ino: undefined }
27
- : { kind: "closed" };
28
- };
29
- // A release callback changes the engine's draft; publication may be immediate cleanup or after installation.
30
- /** @internal */
31
- export const release = Effect.fnUntraced(function* (reference, releaseInode, afterRelease) {
32
- const ino = inode(reference);
33
- yield* afterRelease(() => released(reference));
34
- if (ino !== undefined)
35
- yield* releaseInode(ino);
36
- });
37
- /** @internal */
38
- export const closeReleasedScope = (reference) => Effect.suspend(() => isOpen(reference) || reference.scope === undefined ? Effect.void : Scope.close(reference.scope, Exit.void));
39
- // The inode, rather than the open tag, decides whether a rerun must release a late publication.
40
- /** @internal */
41
- export const finalize = (reference, close, fallback) => Effect.uninterruptible(Effect.suspend(() => {
42
- if (inode(reference) === undefined) {
43
- // Check and mark together: acquisition can publish between separate Effect steps.
44
- released(reference);
45
- return Effect.void;
46
- }
47
- return Effect.ignore(close).pipe(Effect.andThen(Effect.suspend(() => inode(reference) === undefined ? Effect.void : fallback)));
48
- }));
49
- // Register before waiting: a closed scope runs its finalizer immediately, and engine permits are not reentrant.
50
- /** @internal */
51
- export const acquire = Effect.fnUntraced(function* (reference, coordinate, acquisition, releaseAcquired, finalizer) {
52
- const scope = yield* Scope.fork(yield* Effect.scope);
53
- reference.scope = scope;
54
- yield* Scope.addFinalizer(scope, finalizer);
55
- const closed = () => Predicate.isTagged(scope.state, "Closed");
56
- return yield* coordinate(Effect.suspend(() => closed() ? Effect.interrupt : acquisition).pipe(Effect.tap(() => Effect.suspend(() => closed() ? Effect.andThen(releaseAcquired(), Effect.interrupt) : Effect.void)))).pipe(Effect.tap(() => Effect.suspend(() => closed() ? Effect.interrupt : Effect.void)), Effect.onError(() => Effect.andThen(finalizer, Scope.close(scope, Exit.void))), Effect.onError(() => Effect.sync(() => {
57
- if (reference.lifecycle.kind === "releasedPendingCommit" && reference.lifecycle.ino === undefined) {
58
- reference.lifecycle = { kind: "closed" };
59
- }
60
- })));
61
- });
62
- /** @internal */
63
- export const nodeOrFail = (result, addressing, op) => {
64
- switch (result.kind) {
65
- case "live":
66
- return Effect.succeed(result.node);
67
- case "unknown":
68
- return Effect.fail(op.fail(addressing === "reference" ? "InvalidReference" : "InvalidHandle"));
69
- case "foreign":
70
- return Effect.fail(op.fail(addressing === "reference" ? "ForeignReference" : "ForeignHandle"));
71
- case "stale":
72
- return Effect.fail(op.fail(addressing === "reference" ? "StaleReference" : "InvalidHandle"));
73
- }
74
- };
75
- /** @internal */
76
- export const make = (volume, get) => {
77
- const tokens = new Map();
78
- const resolveInode = (ino) => Effect.map(get(ino), (node) => node === undefined || (node.kind === "directory" && node.metadata.nlink === 0)
79
- ? { kind: "stale" }
80
- : { kind: "live", node });
81
- const resolveReference = (token) => Effect.suspend(() => {
82
- const known = Predicate.isObject(token) ? references.get(token) : undefined;
83
- if (known === undefined)
84
- return Effect.succeed({ kind: "unknown" });
85
- if (known.volume !== volume)
86
- return Effect.succeed({ kind: "foreign" });
87
- return resolveInode(known.ino);
88
- });
89
- const resolveHandle = (token, directoryOnly = false) => Effect.suspend(() => {
90
- const known = Predicate.isObject(token)
91
- ? directories.get(token) ?? (directoryOnly ? undefined : files.get(token))
92
- : undefined;
93
- if (known === undefined)
94
- return Effect.succeed({ kind: "unknown" });
95
- if (known.volume !== volume)
96
- return Effect.succeed({ kind: "foreign" });
97
- if (!isOpen(known))
98
- return Effect.succeed({ kind: "stale" });
99
- const ino = inode(known);
100
- return ino === undefined
101
- ? Effect.succeed({ kind: "stale" })
102
- : Effect.map(get(ino), (node) => node === undefined ? { kind: "stale" } : { kind: "live", node });
103
- });
104
- return {
105
- referenceIdentity: (token, op) => {
106
- const known = Predicate.isObject(token) ? references.get(token) : undefined;
107
- if (known === undefined)
108
- return Effect.fail(op.fail("InvalidReference"));
109
- if (known.volume !== volume)
110
- return Effect.fail(op.fail("ForeignReference"));
111
- return Effect.succeed(known.ino);
112
- },
113
- handleBoundaries: (token) => (files.get(token) ?? directories.get(token))?.boundaries ?? [],
114
- authorizeHandle: (token, node, op) => {
115
- const reference = files.get(token) ?? directories.get(token);
116
- return reference?.authority?.(node, op) ?? Effect.void;
117
- },
118
- resolve: (token, addressing) => addressing === "reference" ? resolveReference(token) : resolveHandle(token, addressing === "directory"),
119
- resolveInode,
120
- referenceFor: (ino) => {
121
- const existing = tokens.get(ino);
122
- if (existing !== undefined)
123
- return existing;
124
- const reference = Object.freeze({ [ObjectReferenceId]: true });
125
- references.set(reference, { volume, ino });
126
- tokens.set(ino, reference);
127
- return reference;
128
- },
129
- forget: (ino) => tokens.delete(ino),
130
- file: (access, append) => ({
131
- volume,
132
- lifecycle: { kind: "acquiring" },
133
- scope: undefined,
134
- offset: 0n,
135
- access,
136
- append
137
- }),
138
- directory: (ino) => ({
139
- volume,
140
- lifecycle: ino === undefined ? { kind: "acquiring" } : { kind: "open", ino },
141
- scope: undefined
142
- }),
143
- registerFile: (handle, reference) => files.set(handle, reference),
144
- registerDirectory: (handle, reference) => directories.set(handle, reference)
145
- };
146
- };
@@ -1,8 +0,0 @@
1
- import * as Effect from "effect/Effect";
2
- /** @internal */
3
- export interface Turnstile {
4
- readonly withTurn: <A, E, R>(effect: Effect.Effect<A, E, R>) => Effect.Effect<A, E, R>;
5
- }
6
- /** @internal */
7
- export declare const makeTurnstile: () => Turnstile;
8
- //# sourceMappingURL=turnstile.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"turnstile.d.ts","sourceRoot":"","sources":["../../src/internal/turnstile.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,MAAM,eAAe,CAAA;AAIvC,gBAAgB;AAChB,MAAM,WAAW,SAAS;IACxB,QAAQ,CAAC,QAAQ,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,KAAK,MAAM,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAA;CACvF;AASD,gBAAgB;AAChB,eAAO,MAAM,aAAa,QAAO,SAkDhC,CAAA"}