@effect-vfs/core 0.8.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 (105) hide show
  1. package/README.md +9 -0
  2. package/dist/BytePath.d.ts +4 -1
  3. package/dist/BytePath.d.ts.map +1 -1
  4. package/dist/BytePath.js +6 -4
  5. package/dist/Caller.d.ts +107 -66
  6. package/dist/Caller.d.ts.map +1 -1
  7. package/dist/Caller.js +16 -86
  8. package/dist/LiveVolume.d.ts +75 -2
  9. package/dist/LiveVolume.d.ts.map +1 -1
  10. package/dist/LiveVolume.js +87 -3
  11. package/dist/Search.d.ts +32 -8
  12. package/dist/Search.d.ts.map +1 -1
  13. package/dist/Search.js +19 -10
  14. package/dist/SynchronousAdapter.d.ts +58 -0
  15. package/dist/SynchronousAdapter.d.ts.map +1 -0
  16. package/dist/SynchronousAdapter.js +116 -0
  17. package/dist/Target.d.ts +7 -91
  18. package/dist/Target.d.ts.map +1 -1
  19. package/dist/Target.js +7 -47
  20. package/dist/Testing.d.ts +5 -2
  21. package/dist/Testing.d.ts.map +1 -1
  22. package/dist/Testing.js +2 -1
  23. package/dist/VfsError.d.ts +4 -2
  24. package/dist/VfsError.d.ts.map +1 -1
  25. package/dist/VfsError.js +3 -1
  26. package/dist/VirtualFileSystem.d.ts +151 -44
  27. package/dist/VirtualFileSystem.d.ts.map +1 -1
  28. package/dist/VirtualFileSystem.js +31 -21
  29. package/dist/Volume.d.ts +6 -1
  30. package/dist/Volume.d.ts.map +1 -1
  31. package/dist/Volume.js +6 -2
  32. package/dist/Watch.d.ts +18 -0
  33. package/dist/Watch.d.ts.map +1 -1
  34. package/dist/Watch.js +12 -0
  35. package/dist/internal/adapterBinding.d.ts +13 -0
  36. package/dist/internal/adapterBinding.d.ts.map +1 -0
  37. package/dist/internal/adapterBinding.js +13 -0
  38. package/dist/internal/bytePath.d.ts +6 -1
  39. package/dist/internal/bytePath.d.ts.map +1 -1
  40. package/dist/internal/callerWatch.d.ts +36 -0
  41. package/dist/internal/callerWatch.d.ts.map +1 -0
  42. package/dist/internal/callerWatch.js +185 -0
  43. package/dist/internal/engine.d.ts +1305 -0
  44. package/dist/internal/engine.d.ts.map +1 -0
  45. package/dist/internal/engine.js +3458 -0
  46. package/dist/internal/errors.d.ts +1 -33
  47. package/dist/internal/errors.d.ts.map +1 -1
  48. package/dist/internal/errors.js +1 -45
  49. package/dist/internal/fixture.d.ts +2 -1
  50. package/dist/internal/fixture.d.ts.map +1 -1
  51. package/dist/internal/fixture.js +8 -4
  52. package/dist/internal/image.d.ts +4 -1
  53. package/dist/internal/image.d.ts.map +1 -1
  54. package/dist/internal/image.js +6 -3
  55. package/dist/internal/lines.d.ts.map +1 -1
  56. package/dist/internal/lines.js +3 -2
  57. package/dist/internal/liveImage.d.ts +4 -2
  58. package/dist/internal/liveImage.d.ts.map +1 -1
  59. package/dist/internal/liveImage.js +3 -1
  60. package/dist/internal/merkle.d.ts +7 -4
  61. package/dist/internal/merkle.d.ts.map +1 -1
  62. package/dist/internal/merkle.js +6 -4
  63. package/dist/internal/search.d.ts +3 -1
  64. package/dist/internal/search.d.ts.map +1 -1
  65. package/dist/internal/search.js +4 -3
  66. package/dist/internal/snapshotDelta.d.ts +11 -7
  67. package/dist/internal/snapshotDelta.d.ts.map +1 -1
  68. package/dist/internal/snapshotDelta.js +15 -9
  69. package/dist/internal/snapshotMerge.d.ts +1 -1
  70. package/dist/internal/snapshotMerge.d.ts.map +1 -1
  71. package/dist/internal/snapshotMerge.js +5 -2
  72. package/dist/internal/tree.d.ts +4 -2
  73. package/dist/internal/tree.d.ts.map +1 -1
  74. package/dist/internal/tree.js +4 -2
  75. package/dist/internal/virtualFileSystem.d.ts +20 -49
  76. package/dist/internal/virtualFileSystem.d.ts.map +1 -1
  77. package/dist/internal/virtualFileSystem.js +85 -1927
  78. package/dist/internal/volumeLimits.d.ts +1 -0
  79. package/dist/internal/volumeLimits.d.ts.map +1 -1
  80. package/dist/internal/volumeLimits.js +3 -2
  81. package/dist/internal/volumeState.d.ts +10 -79
  82. package/dist/internal/volumeState.d.ts.map +1 -1
  83. package/dist/internal/volumeState.js +9 -79
  84. package/dist/internal/watchHub.d.ts +9 -5
  85. package/dist/internal/watchHub.d.ts.map +1 -1
  86. package/dist/internal/watchHub.js +35 -8
  87. package/package.json +16 -9
  88. package/dist/internal/inodeTable.d.ts +0 -20
  89. package/dist/internal/inodeTable.d.ts.map +0 -1
  90. package/dist/internal/inodeTable.js +0 -48
  91. package/dist/internal/path.d.ts +0 -56
  92. package/dist/internal/path.d.ts.map +0 -1
  93. package/dist/internal/path.js +0 -141
  94. package/dist/internal/resolution.d.ts +0 -135
  95. package/dist/internal/resolution.d.ts.map +0 -1
  96. package/dist/internal/resolution.js +0 -277
  97. package/dist/internal/testSeams.d.ts +0 -17
  98. package/dist/internal/testSeams.d.ts.map +0 -1
  99. package/dist/internal/testSeams.js +0 -20
  100. package/dist/internal/tokenRegistry.d.ts +0 -77
  101. package/dist/internal/tokenRegistry.d.ts.map +0 -1
  102. package/dist/internal/tokenRegistry.js +0 -128
  103. package/dist/internal/turnstile.d.ts +0 -8
  104. package/dist/internal/turnstile.d.ts.map +0 -1
  105. package/dist/internal/turnstile.js +0 -43
@@ -1,141 +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 { getBytes as getBytePathBytes, make as makeBytePath } from "./bytePath.js";
7
- import { fsFailure } from "./errors.js";
8
- /** @internal */
9
- export const SLASH_HEX = "2f";
10
- /** @internal */
11
- export const DOT_HEX = "2e";
12
- /** @internal */
13
- export const DOT_DOT_HEX = "2e2e";
14
- /** @internal */
15
- export const SLASH_BYTE = 47;
16
- const DOT_BYTE = 46;
17
- /** @internal */
18
- export const NUL_BYTE = 0;
19
- /** @internal */
20
- export const MAX_SYMLINK_TRAVERSALS = 40;
21
- /** @internal */
22
- export const MAX_NAME_BYTES = 255;
23
- /** @internal */
24
- export const isNameBytes = (name) => name.length >= 1 && name.length <= MAX_NAME_BYTES && !name.includes(NUL_BYTE) && !name.includes(SLASH_BYTE) &&
25
- !(name.length === 1 && name[0] === DOT_BYTE) && !(name.length === 2 && name[0] === DOT_BYTE && name[1] === DOT_BYTE);
26
- /** @internal */
27
- export const ROOT_PATH = new Uint8Array([SLASH_BYTE]);
28
- /** @internal */
29
- export const joinPath = (prefix, name) => {
30
- const separator = prefix.length === 0 || prefix[prefix.length - 1] === SLASH_BYTE ? 0 : 1;
31
- const joined = new Uint8Array(prefix.length + separator + name.length);
32
- joined.set(prefix);
33
- if (separator === 1)
34
- joined[prefix.length] = SLASH_BYTE;
35
- joined.set(name, prefix.length + separator);
36
- return joined;
37
- };
38
- // A missing final component follows the same rejection path as "." and ".."; callers choose the POSIX error code.
39
- /** @internal */
40
- export const isDotComponent = (name) => name === undefined || name === DOT_HEX || name === DOT_DOT_HEX;
41
- /** @internal */
42
- export const ownedPath = (bytes) => makeBytePath(bytes);
43
- /** @internal */
44
- export const strictString = (bytes, operation) => Effect.try({
45
- try: () => new TextDecoder("utf-8", { fatal: true, ignoreBOM: true }).decode(bytes),
46
- catch: () => fsFailure("UnrepresentableName", operation)
47
- });
48
- /** @internal */
49
- export const nameBytes = (name) => Result.getOrThrow(Hex.decode(name));
50
- // Constructing a view detects detached buffers, including empty ones.
51
- const attachedBuffer = (bytes) => {
52
- try {
53
- const probe = new Uint8Array(bytes.buffer, bytes.byteOffset, 0);
54
- return probe.byteLength === 0;
55
- }
56
- catch (error) {
57
- if (error instanceof TypeError)
58
- return false;
59
- throw error;
60
- }
61
- };
62
- // Reject shared or detached buffers before copying; Buffer subclasses remain valid.
63
- /** @internal */
64
- export const isAttachedBytes = (bytes) => Predicate.isUint8Array(bytes) && bytes.buffer instanceof ArrayBuffer && attachedBuffer(bytes);
65
- /** @internal */
66
- export const pathFromBytes = Effect.fn("VirtualFileSystem.pathFromBytes")(function* (bytes) {
67
- if (!isAttachedBytes(bytes))
68
- return yield* fsFailure("InvalidArgument", "pathFromBytes");
69
- const owned = new Uint8Array(bytes);
70
- if (owned.length === 0 || owned.includes(0)) {
71
- return yield* fsFailure("InvalidArgument", "pathFromBytes");
72
- }
73
- return makeBytePath(owned);
74
- });
75
- /** @internal */
76
- export const pathToBytes = Effect.fn("VirtualFileSystem.pathToBytes")(function* (path) {
77
- const bytes = getBytePathBytes(path);
78
- if (bytes === undefined)
79
- return yield* fsFailure("InvalidArgument", "pathToBytes");
80
- return new Uint8Array(bytes);
81
- });
82
- // String.prototype.isWellFormed requires ES2024; this package targets ES2023.
83
- /** @internal */
84
- export const isWellFormed = (value) => {
85
- for (let index = 0; index < value.length; index++) {
86
- const code = value.charCodeAt(index);
87
- if (code >= 0xd800 && code <= 0xdbff) {
88
- const next = value.charCodeAt(++index);
89
- if (!(next >= 0xdc00 && next <= 0xdfff))
90
- return false;
91
- }
92
- else if (code >= 0xdc00 && code <= 0xdfff)
93
- return false;
94
- }
95
- return true;
96
- };
97
- const UTF8_ENCODER = new TextEncoder();
98
- /** @internal */
99
- export const inputBytes = (input) => Predicate.isString(input)
100
- ? isWellFormed(input) ? Result.succeed(UTF8_ENCODER.encode(input)) : Result.fail("InvalidPathEncoding")
101
- : Result.fromNullishOr(getBytePathBytes(input), () => "InvalidArgument");
102
- /** @internal */
103
- export const preparePath = (input, operation, maxPathBytes) => {
104
- const encoded = inputBytes(input);
105
- if (Result.isFailure(encoded)) {
106
- return Result.fail(encoded.failure === "InvalidPathEncoding"
107
- ? fsFailure("InvalidPathEncoding", operation, { path: input })
108
- : fsFailure("InvalidArgument", operation));
109
- }
110
- const bytes = encoded.success;
111
- if (bytes.length === 0)
112
- return Result.fail(fsFailure("NotFound", operation, { path: input }));
113
- if (bytes.includes(0))
114
- return Result.fail(fsFailure("InvalidArgument", operation, { path: input }));
115
- if (maxPathBytes !== undefined && ByteSize.isGreaterThan(ByteSize.bytes(bytes.length), maxPathBytes)) {
116
- return Result.fail(fsFailure("PathTooLong", operation, { path: input }));
117
- }
118
- const components = [];
119
- const suffixes = [];
120
- let start = 0;
121
- for (let index = 0; index <= bytes.length; index++) {
122
- if (index !== bytes.length && bytes[index] !== SLASH_BYTE)
123
- continue;
124
- if (index > start) {
125
- if (index - start > MAX_NAME_BYTES) {
126
- return Result.fail(fsFailure("PathTooLong", operation, { path: input }));
127
- }
128
- components.push(Hex.encode(bytes.subarray(start, index)));
129
- suffixes.push(bytes.subarray(index));
130
- }
131
- start = index + 1;
132
- }
133
- return Result.succeed({
134
- input,
135
- absolute: bytes[0] === SLASH_BYTE,
136
- trailingSlash: bytes.at(-1) === SLASH_BYTE,
137
- bytes,
138
- suffixes,
139
- components
140
- });
141
- };
@@ -1,135 +0,0 @@
1
- import * as ByteSize from "effect/ByteSize";
2
- import * as Effect from "effect/Effect";
3
- import * as Result from "effect/Result";
4
- import type { Identity } from "../Caller.js";
5
- import { type EntryInput, type NameInput, type Target } from "../Target.js";
6
- import type { FsFailure } from "../VfsError.js";
7
- import type { DirectoryHandle, PathInput } from "../VirtualFileSystem.js";
8
- import type { OpContext } from "./errors.js";
9
- import { type PreparedPath } from "./path.js";
10
- import * as TokenRegistry from "./tokenRegistry.js";
11
- import { type Directory, type Ino, type Node } from "./volumeState.js";
12
- /** @internal */
13
- export type PreparedEntry = {
14
- readonly kind: "path";
15
- readonly path: PreparedPath;
16
- readonly base: DirectoryHandle | undefined;
17
- } | {
18
- readonly kind: "entry";
19
- readonly directory: Target;
20
- readonly name: string;
21
- };
22
- /** @internal */
23
- export interface NodeMode<R = never> {
24
- readonly kind: "Node";
25
- readonly followFinalSymlink?: boolean;
26
- readonly final?: boolean;
27
- readonly createMissing?: (parent: Directory, name: string, final: boolean) => Effect.Effect<Directory, FsFailure, R>;
28
- }
29
- /** @internal */
30
- export interface ParentMode {
31
- readonly kind: "Parent";
32
- }
33
- /** @internal */
34
- export interface OrCreateMode<R = never> {
35
- readonly kind: "OrCreate";
36
- readonly create: "never" | "ifMissing" | "exclusive";
37
- readonly finalSymlink: "follow" | "preserve";
38
- readonly entryExclusive?: "afterSymlink";
39
- readonly beforeFollow?: (parent: Directory, child: Node | undefined) => Effect.Effect<void, FsFailure, R>;
40
- }
41
- /** @internal */
42
- export type NodeResult = {
43
- readonly kind: "node";
44
- readonly node: Node;
45
- readonly op: OpContext;
46
- } | {
47
- readonly kind: "entry";
48
- readonly node: Node;
49
- readonly parent: Directory;
50
- readonly name: string;
51
- readonly op: OpContext;
52
- };
53
- /** @internal */
54
- export interface ParentResult {
55
- readonly kind: "parent";
56
- readonly parent: Ino;
57
- readonly name: string | undefined;
58
- readonly trailingSlash: boolean;
59
- readonly addressing: "path" | "entry";
60
- readonly op: OpContext;
61
- }
62
- interface CreateEntry {
63
- readonly parent: Directory;
64
- readonly name: string;
65
- readonly trailingSlash: boolean;
66
- readonly addressing: "path" | "entry";
67
- readonly op: OpContext;
68
- readonly origin: {
69
- readonly parent: Directory;
70
- readonly name: string;
71
- };
72
- }
73
- /** @internal */
74
- export type CreateResult = (CreateEntry & {
75
- readonly kind: "existing";
76
- readonly node: Node;
77
- }) | (CreateEntry & {
78
- readonly kind: "missing";
79
- });
80
- /** @internal */
81
- export declare const inGroup: (identity: Identity, gid: number) => boolean;
82
- /** @internal */
83
- export declare const permitted: (node: Node, identity: Identity) => number;
84
- /** @internal */
85
- export declare const authorize: (node: Node, identity: Identity, bits: number, op: OpContext) => Effect.Effect<void, never, never> | Effect.Effect<never, FsFailure, never>;
86
- /** @internal */
87
- export declare const entryName: (input: NameInput, op: OpContext) => Result.Result<string, FsFailure>;
88
- /** @internal */
89
- export declare const make: ({ caller, get, identity, maxPathBytes, registry }: {
90
- readonly caller: TokenRegistry.DirectoryReference;
91
- readonly get: (ino: Ino) => Effect.Effect<Node | undefined>;
92
- readonly identity: Identity;
93
- readonly maxPathBytes: ByteSize.ByteSize | undefined;
94
- readonly registry: TokenRegistry.TokenRegistry;
95
- }) => {
96
- prepare: (input: PathInput, op: OpContext) => Result.Result<PreparedPath, FsFailure>;
97
- prepareEntry: (input: EntryInput, op: OpContext) => Result.Result<PreparedEntry, FsFailure>;
98
- resolve: {
99
- <R = never>(target: Target, mode: NodeMode<R>, op: OpContext): Effect.Effect<NodeResult, FsFailure, R>;
100
- (prepared: PreparedEntry, mode: ParentMode, op: OpContext): Effect.Effect<ParentResult, FsFailure>;
101
- <R = never>(prepared: PreparedEntry, mode: OrCreateMode<R>, op: OpContext): Effect.Effect<CreateResult, FsFailure, R>;
102
- };
103
- directory: (target: {
104
- readonly _tag: "Handle";
105
- readonly handle: import("../VirtualFileSystem.js").FileHandle | DirectoryHandle;
106
- } | {
107
- readonly _tag: "Path";
108
- readonly path: PathInput;
109
- readonly relativeTo?: DirectoryHandle;
110
- readonly followFinalSymlink?: boolean;
111
- } | {
112
- readonly _tag: "Reference";
113
- readonly reference: import("../VirtualFileSystem.js").ObjectReference;
114
- }, op: OpContext) => Effect.Effect<{
115
- node: Directory;
116
- op: OpContext;
117
- }, FsFailure, never>;
118
- entryDirectory: (target: {
119
- readonly _tag: "Handle";
120
- readonly handle: import("../VirtualFileSystem.js").FileHandle | DirectoryHandle;
121
- } | {
122
- readonly _tag: "Path";
123
- readonly path: PathInput;
124
- readonly relativeTo?: DirectoryHandle;
125
- readonly followFinalSymlink?: boolean;
126
- } | {
127
- readonly _tag: "Reference";
128
- readonly reference: import("../VirtualFileSystem.js").ObjectReference;
129
- }, op: OpContext) => Effect.Effect<{
130
- node: Directory;
131
- op: OpContext;
132
- }, FsFailure, never>;
133
- };
134
- export {};
135
- //# sourceMappingURL=resolution.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"resolution.d.ts","sourceRoot":"","sources":["../../src/internal/resolution.ts"],"names":[],"mappings":"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"}
@@ -1,277 +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 { DOT_DOT_HEX, DOT_HEX, isAttachedBytes, isDotComponent, isWellFormed, MAX_NAME_BYTES, MAX_SYMLINK_TRAVERSALS, nameBytes, ownedPath, preparePath, SLASH_BYTE } from "./path.js";
8
- import * as TokenRegistry from "./tokenRegistry.js";
9
- import { ROOT_INO } from "./volumeState.js";
10
- const encoder = new TextEncoder();
11
- /** @internal */
12
- export const inGroup = (identity, gid) => identity.gid === gid || identity.groups.includes(gid);
13
- /** @internal */
14
- export const permitted = (node, identity) => {
15
- const shift = identity.uid === node.metadata.uid ? 6 : inGroup(identity, node.metadata.gid) ? 3 : 0;
16
- return (node.metadata.mode >> shift) & 0o7;
17
- };
18
- /** @internal */
19
- export const authorize = (node, identity, bits, op) => identity.privileged || (permitted(node, identity) & bits) === bits
20
- ? Effect.void
21
- : Effect.fail(op.fail("AccessDenied"));
22
- /** @internal */
23
- export const entryName = (input, op) => {
24
- if (Predicate.isString(input) && !isWellFormed(input))
25
- return Result.fail(op.fail("InvalidPathEncoding"));
26
- const bytes = Predicate.isString(input) ? encoder.encode(input) : input;
27
- if (!isAttachedBytes(bytes) || bytes.length === 0 || bytes.length > MAX_NAME_BYTES || bytes.includes(0) ||
28
- bytes.includes(SLASH_BYTE))
29
- return Result.fail(op.fail("InvalidArgument"));
30
- return Result.succeed(Hex.encode(new Uint8Array(bytes)));
31
- };
32
- /** @internal */
33
- export const make = ({ caller, get, identity, maxPathBytes, registry }) => {
34
- const prepare = (input, op) => preparePath(input, op.operation, maxPathBytes);
35
- const prepareEntry = (input, op) => isEntry(input)
36
- ? Result.map(entryName(input.name, op), (name) => ({ kind: "entry", directory: input.directory, name }))
37
- : isTarget(input)
38
- ? Result.map(prepare(input.path, op), (path) => ({ kind: "path", path, base: input.relativeTo }))
39
- : Result.map(prepare(input, op), (path) => ({ kind: "path", path, base: undefined }));
40
- const nodeAt = Effect.fnUntraced(function* (ino) {
41
- const node = yield* get(ino);
42
- return node === undefined ? yield* Effect.die("Inode left the table during resolution") : node;
43
- });
44
- const directoryAt = Effect.fnUntraced(function* (ino) {
45
- const node = yield* nodeAt(ino);
46
- return node.kind !== "directory" ? yield* Effect.die("Directory left the table during resolution") : node;
47
- });
48
- const callerDirectory = Effect.fnUntraced(function* (op) {
49
- const ino = TokenRegistry.inode(caller);
50
- const node = ino === undefined ? undefined : yield* get(ino);
51
- return node?.kind !== "directory" ? yield* op.fail("ClosedCaller") : node;
52
- });
53
- const walk = Effect.fnUntraced(function* (path, base, op, mode, referencedBase) {
54
- const pathOp = op.at(path.input);
55
- const parentOnly = mode.kind === "Parent";
56
- const allowMissing = mode.kind === "OrCreate" && mode.create !== "never";
57
- const followFinalSymlink = mode.kind === "Node"
58
- ? mode.followFinalSymlink !== false
59
- : mode.kind === "Parent" || mode.finalSymlink === "follow";
60
- const createMissing = mode.kind === "Node" ? mode.createMissing : undefined;
61
- if (!TokenRegistry.isOpen(caller)) {
62
- return yield* pathOp.fail("ClosedCaller");
63
- }
64
- let current = path.absolute ? (yield* nodeAt(ROOT_INO)) : yield* callerDirectory(pathOp);
65
- if (!path.absolute && referencedBase !== undefined) {
66
- current = referencedBase;
67
- yield* authorize(current, identity, 0o1, op);
68
- }
69
- else if (!path.absolute && base !== undefined) {
70
- const directory = yield* TokenRegistry.nodeOrFail(yield* registry.resolve(base, "directory"), "handle", pathOp);
71
- if (directory.kind !== "directory")
72
- return yield* pathOp.fail("InvalidHandle");
73
- current = directory;
74
- yield* authorize(current, identity, 0o1, pathOp);
75
- }
76
- if (current.metadata.nlink === 0) {
77
- return yield* pathOp.fail("NotFound");
78
- }
79
- let work = path;
80
- let parent;
81
- let name;
82
- let traversals = 0;
83
- // Leading components that came from a symbolic link's target. Only the components the caller wrote are
84
- // created, so a dangling link stays missing, as it does for mkdir -p.
85
- let linked = 0;
86
- for (let index = 0; index < work.components.length - (parentOnly ? 1 : 0); index++) {
87
- if (current.kind !== "directory") {
88
- return yield* pathOp.fail("NotDirectory");
89
- }
90
- yield* authorize(current, identity, 0o1, pathOp);
91
- const component = work.components[index];
92
- if (component === undefined)
93
- break;
94
- if (component === DOT_HEX)
95
- continue;
96
- if (component === DOT_DOT_HEX) {
97
- current = yield* directoryAt(current.parent);
98
- parent = undefined;
99
- name = undefined;
100
- continue;
101
- }
102
- parent = current;
103
- name = component;
104
- const childIno = current.entries.get(component);
105
- const child = childIno === undefined ? undefined : (yield* get(childIno));
106
- if (child === undefined) {
107
- if (allowMissing && index === work.components.length - 1) {
108
- return { kind: "missing", parent, name, op: pathOp };
109
- }
110
- if (createMissing !== undefined && index >= linked) {
111
- current = yield* createMissing(current, component, index === work.components.length - 1);
112
- continue;
113
- }
114
- return yield* pathOp.fail("NotFound");
115
- }
116
- if (child.kind === "symlink" && (followFinalSymlink || index < work.components.length - 1 || work.trailingSlash)) {
117
- if (child.target.length === 0) {
118
- return yield* pathOp.fail("NotFound");
119
- }
120
- if (++traversals > MAX_SYMLINK_TRAVERSALS) {
121
- return yield* pathOp.fail("SymlinkLoop");
122
- }
123
- const suffix = work.suffixes[index] ?? new Uint8Array(0);
124
- const remaining = work.components.length - index - 1;
125
- if (maxPathBytes !== undefined &&
126
- ByteSize.isGreaterThan(ByteSize.bytes(child.target.length + suffix.length), maxPathBytes)) {
127
- return yield* pathOp.fail("PathTooLong");
128
- }
129
- const expansion = new Uint8Array(child.target.length + suffix.length);
130
- expansion.set(child.target);
131
- expansion.set(suffix, child.target.length);
132
- const expanded = preparePath(ownedPath(expansion), op.operation, maxPathBytes);
133
- // The expansion is synthetic: its per-component limits are the caller's to hear about,
134
- // but the path in the error has to be the one the caller passed in, so the
135
- // expansion's own failure is not kept as the cause.
136
- if (Result.isFailure(expanded)) {
137
- return yield* pathOp.fail(expanded.failure.code);
138
- }
139
- work = expanded.success;
140
- linked = work.components.length - remaining + Math.max(0, linked - index - 1);
141
- if (work.absolute)
142
- current = yield* nodeAt(ROOT_INO);
143
- index = -1;
144
- }
145
- else
146
- current = child;
147
- }
148
- if (!parentOnly && work.trailingSlash && current.kind !== "directory") {
149
- return yield* pathOp.fail("NotDirectory");
150
- }
151
- return parent === undefined || name === undefined
152
- ? { kind: "node", node: current, op: pathOp }
153
- : { kind: "entry", node: current, parent, name, op: pathOp };
154
- });
155
- const resolveNode = Effect.fnUntraced(function* (target, mode, op) {
156
- if (Predicate.isTagged(target, "Path")) {
157
- const path = yield* Effect.fromResult(prepare(target.path, op));
158
- const result = yield* walk(path, target.relativeTo, op, {
159
- ...mode,
160
- followFinalSymlink: mode.final === true
161
- ? mode.followFinalSymlink ?? true
162
- : target.followFinalSymlink ?? mode.followFinalSymlink ?? true
163
- });
164
- return result.kind === "missing" ? yield* result.op.fail("NotFound") : result;
165
- }
166
- if (!TokenRegistry.isOpen(caller))
167
- return yield* op.fail("ClosedCaller");
168
- const node = yield* TokenRegistry.nodeOrFail(yield* (Predicate.isTagged(target, "Reference")
169
- ? registry.resolve(target.reference, "reference")
170
- : registry.resolve(target.handle, "handle")), Predicate.isTagged(target, "Reference") ? "reference" : "handle", op);
171
- return { kind: "node", node, op };
172
- });
173
- const directory = Effect.fnUntraced(function* (target, op) {
174
- const resolved = yield* resolveNode(target, { kind: "Node" }, op);
175
- if (resolved.node.kind !== "directory")
176
- return yield* resolved.op.fail("NotDirectory");
177
- return { node: resolved.node, op: resolved.op };
178
- });
179
- const entryDirectory = Effect.fnUntraced(function* (target, op) {
180
- const resolved = yield* directory(target, op);
181
- if (resolved.node.metadata.nlink === 0)
182
- return yield* resolved.op.fail("NotFound");
183
- return resolved;
184
- });
185
- const parent = Effect.fnUntraced(function* (prepared, op) {
186
- if (prepared.kind === "entry") {
187
- const resolved = yield* entryDirectory(prepared.directory, op);
188
- return {
189
- kind: "parent",
190
- parent: resolved.node.ino,
191
- name: prepared.name,
192
- trailingSlash: false,
193
- addressing: "entry",
194
- op
195
- };
196
- }
197
- const resolved = yield* walk(prepared.path, prepared.base, op, { kind: "Parent" });
198
- if (resolved.kind === "missing")
199
- return yield* resolved.op.fail("NotFound");
200
- if (resolved.node.kind !== "directory")
201
- return yield* resolved.op.fail("NotDirectory");
202
- return {
203
- kind: "parent",
204
- parent: resolved.node.ino,
205
- name: prepared.path.components.at(-1),
206
- trailingSlash: prepared.path.trailingSlash,
207
- addressing: "path",
208
- op: resolved.op
209
- };
210
- });
211
- const creationResult = (result, trailingSlash, addressing, op, origin) => {
212
- if (result.kind === "node")
213
- return Effect.fail(op.fail("IsDirectory"));
214
- const entry = {
215
- parent: result.parent,
216
- name: result.name,
217
- trailingSlash,
218
- addressing,
219
- op,
220
- origin: origin ?? { parent: result.parent, name: result.name }
221
- };
222
- return Effect.succeed(result.kind === "missing" ? { ...entry, kind: "missing" } : { ...entry, kind: "existing", node: result.node });
223
- };
224
- const orCreate = Effect.fnUntraced(function* (prepared, mode, op) {
225
- if (prepared.kind === "path") {
226
- const pathOp = op.at(prepared.path.input);
227
- if (mode.create === "exclusive") {
228
- const exists = yield* Effect.result(walk(prepared.path, prepared.base, op, { kind: "Node", followFinalSymlink: false }));
229
- if (Result.isSuccess(exists))
230
- return yield* pathOp.fail("AlreadyExists");
231
- if (exists.failure.code !== "NotFound")
232
- return yield* exists.failure;
233
- }
234
- return yield* creationResult(yield* walk(prepared.path, prepared.base, op, mode), prepared.path.trailingSlash, "path", pathOp);
235
- }
236
- const { node: parent } = yield* entryDirectory(prepared.directory, op);
237
- if (isDotComponent(prepared.name))
238
- return yield* op.fail("InvalidArgument");
239
- yield* authorize(parent, identity, 0o1, op);
240
- const directIno = parent.entries.get(prepared.name);
241
- const direct = directIno === undefined ? undefined : yield* get(directIno);
242
- if (mode.beforeFollow !== undefined)
243
- yield* mode.beforeFollow(parent, direct);
244
- if (mode.create === "exclusive" && mode.entryExclusive !== "afterSymlink" && direct !== undefined) {
245
- return yield* op.fail("AlreadyExists");
246
- }
247
- let result = direct === undefined
248
- ? { kind: "missing", parent, name: prepared.name, op }
249
- : { kind: "entry", node: direct, parent, name: prepared.name, op };
250
- if (direct?.kind === "symlink" && mode.finalSymlink === "follow") {
251
- const path = yield* Effect.fromResult(preparePath(ownedPath(nameBytes(prepared.name)), op.operation, undefined));
252
- result = yield* walk(path, undefined, op, mode, parent);
253
- // Entry writeFile rejects a followed directory before checking exclusive creation.
254
- if (mode.create === "exclusive" && mode.entryExclusive === "afterSymlink" &&
255
- (result.kind === "node" || (result.kind === "entry" && result.node.kind === "directory"))) {
256
- return yield* op.fail("IsDirectory");
257
- }
258
- }
259
- if (result.kind === "missing" && mode.create === "never")
260
- return yield* op.fail("NotFound");
261
- if (mode.create === "exclusive" && mode.entryExclusive === "afterSymlink" && result.kind !== "missing") {
262
- return yield* op.fail("AlreadyExists");
263
- }
264
- return yield* creationResult(result, false, "entry", op, { parent, name: prepared.name });
265
- });
266
- function resolve(input, mode, op) {
267
- if (isTarget(input)) {
268
- return mode.kind === "Node" ? resolveNode(input, mode, op) : Effect.die("A node target requires Node resolution");
269
- }
270
- return mode.kind === "Parent"
271
- ? parent(input, op)
272
- : mode.kind === "OrCreate"
273
- ? orCreate(input, mode, op)
274
- : Effect.die("An entry requires Parent or OrCreate resolution");
275
- }
276
- return { prepare, prepareEntry, resolve, directory, entryDirectory };
277
- };
@@ -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
- }));