@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.
- package/README.md +9 -0
- package/dist/BytePath.d.ts +4 -1
- package/dist/BytePath.d.ts.map +1 -1
- package/dist/BytePath.js +6 -4
- package/dist/Caller.d.ts +107 -66
- package/dist/Caller.d.ts.map +1 -1
- package/dist/Caller.js +16 -86
- package/dist/LiveVolume.d.ts +75 -2
- package/dist/LiveVolume.d.ts.map +1 -1
- package/dist/LiveVolume.js +87 -3
- package/dist/Search.d.ts +32 -8
- package/dist/Search.d.ts.map +1 -1
- package/dist/Search.js +19 -10
- package/dist/SynchronousAdapter.d.ts +58 -0
- package/dist/SynchronousAdapter.d.ts.map +1 -0
- package/dist/SynchronousAdapter.js +116 -0
- package/dist/Target.d.ts +7 -91
- package/dist/Target.d.ts.map +1 -1
- package/dist/Target.js +7 -47
- package/dist/Testing.d.ts +5 -2
- package/dist/Testing.d.ts.map +1 -1
- package/dist/Testing.js +2 -1
- package/dist/VfsError.d.ts +4 -2
- package/dist/VfsError.d.ts.map +1 -1
- package/dist/VfsError.js +3 -1
- package/dist/VirtualFileSystem.d.ts +151 -44
- package/dist/VirtualFileSystem.d.ts.map +1 -1
- package/dist/VirtualFileSystem.js +31 -21
- package/dist/Volume.d.ts +6 -1
- package/dist/Volume.d.ts.map +1 -1
- package/dist/Volume.js +6 -2
- package/dist/Watch.d.ts +18 -0
- package/dist/Watch.d.ts.map +1 -1
- package/dist/Watch.js +12 -0
- package/dist/internal/adapterBinding.d.ts +13 -0
- package/dist/internal/adapterBinding.d.ts.map +1 -0
- package/dist/internal/adapterBinding.js +13 -0
- package/dist/internal/bytePath.d.ts +6 -1
- package/dist/internal/bytePath.d.ts.map +1 -1
- package/dist/internal/callerWatch.d.ts +36 -0
- package/dist/internal/callerWatch.d.ts.map +1 -0
- package/dist/internal/callerWatch.js +185 -0
- package/dist/internal/engine.d.ts +1305 -0
- package/dist/internal/engine.d.ts.map +1 -0
- package/dist/internal/engine.js +3458 -0
- package/dist/internal/errors.d.ts +1 -33
- package/dist/internal/errors.d.ts.map +1 -1
- package/dist/internal/errors.js +1 -45
- package/dist/internal/fixture.d.ts +2 -1
- package/dist/internal/fixture.d.ts.map +1 -1
- package/dist/internal/fixture.js +8 -4
- package/dist/internal/image.d.ts +4 -1
- package/dist/internal/image.d.ts.map +1 -1
- package/dist/internal/image.js +6 -3
- package/dist/internal/lines.d.ts.map +1 -1
- package/dist/internal/lines.js +3 -2
- package/dist/internal/liveImage.d.ts +4 -2
- package/dist/internal/liveImage.d.ts.map +1 -1
- package/dist/internal/liveImage.js +3 -1
- package/dist/internal/merkle.d.ts +7 -4
- package/dist/internal/merkle.d.ts.map +1 -1
- package/dist/internal/merkle.js +6 -4
- package/dist/internal/search.d.ts +3 -1
- package/dist/internal/search.d.ts.map +1 -1
- package/dist/internal/search.js +4 -3
- package/dist/internal/snapshotDelta.d.ts +11 -7
- package/dist/internal/snapshotDelta.d.ts.map +1 -1
- package/dist/internal/snapshotDelta.js +15 -9
- package/dist/internal/snapshotMerge.d.ts +1 -1
- package/dist/internal/snapshotMerge.d.ts.map +1 -1
- package/dist/internal/snapshotMerge.js +5 -2
- package/dist/internal/tree.d.ts +4 -2
- package/dist/internal/tree.d.ts.map +1 -1
- package/dist/internal/tree.js +4 -2
- package/dist/internal/virtualFileSystem.d.ts +20 -49
- package/dist/internal/virtualFileSystem.d.ts.map +1 -1
- package/dist/internal/virtualFileSystem.js +85 -1927
- package/dist/internal/volumeLimits.d.ts +1 -0
- package/dist/internal/volumeLimits.d.ts.map +1 -1
- package/dist/internal/volumeLimits.js +3 -2
- package/dist/internal/volumeState.d.ts +10 -79
- package/dist/internal/volumeState.d.ts.map +1 -1
- package/dist/internal/volumeState.js +9 -79
- package/dist/internal/watchHub.d.ts +9 -5
- package/dist/internal/watchHub.d.ts.map +1 -1
- package/dist/internal/watchHub.js +35 -8
- package/package.json +16 -9
- package/dist/internal/inodeTable.d.ts +0 -20
- package/dist/internal/inodeTable.d.ts.map +0 -1
- package/dist/internal/inodeTable.js +0 -48
- package/dist/internal/path.d.ts +0 -56
- package/dist/internal/path.d.ts.map +0 -1
- package/dist/internal/path.js +0 -141
- package/dist/internal/resolution.d.ts +0 -135
- package/dist/internal/resolution.d.ts.map +0 -1
- package/dist/internal/resolution.js +0 -277
- package/dist/internal/testSeams.d.ts +0 -17
- package/dist/internal/testSeams.d.ts.map +0 -1
- package/dist/internal/testSeams.js +0 -20
- package/dist/internal/tokenRegistry.d.ts +0 -77
- package/dist/internal/tokenRegistry.d.ts.map +0 -1
- package/dist/internal/tokenRegistry.js +0 -128
- package/dist/internal/turnstile.d.ts +0 -8
- package/dist/internal/turnstile.d.ts.map +0 -1
- package/dist/internal/turnstile.js +0 -43
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import * as Predicate from "effect/Predicate";
|
|
2
|
+
import { Change } from "../Watch.js";
|
|
3
|
+
import { getBytes } from "./bytePath.js";
|
|
4
|
+
import { Errors, Paths, State } from "./engine.js";
|
|
5
|
+
const fsFailure = Errors.fsFailure;
|
|
6
|
+
const { nameBytes, ownedPath, SLASH_HEX } = Paths;
|
|
7
|
+
const { getNode, ROOT_INO } = State;
|
|
8
|
+
const directoryPath = (state, ino) => {
|
|
9
|
+
const names = [];
|
|
10
|
+
const visited = new Set();
|
|
11
|
+
let at = ino;
|
|
12
|
+
for (;;) {
|
|
13
|
+
if (visited.has(at))
|
|
14
|
+
return undefined;
|
|
15
|
+
visited.add(at);
|
|
16
|
+
const directory = getNode(state, at);
|
|
17
|
+
if (directory?.kind !== "directory" || directory.metadata.nlink === 0)
|
|
18
|
+
return undefined;
|
|
19
|
+
if (at === ROOT_INO)
|
|
20
|
+
return SLASH_HEX + names.reverse().join(SLASH_HEX);
|
|
21
|
+
names.push(directory.name);
|
|
22
|
+
at = directory.parent;
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const below = (path, root) => root === SLASH_HEX || path === root || path.startsWith(root + SLASH_HEX);
|
|
26
|
+
const rebase = (path, root) => root === SLASH_HEX ? path : path === root ? SLASH_HEX : path.slice(root.length);
|
|
27
|
+
const entryPath = (state, link) => {
|
|
28
|
+
const parent = directoryPath(state, link.parent);
|
|
29
|
+
return parent === undefined ? undefined : parent + (parent === SLASH_HEX ? "" : SLASH_HEX) + link.name;
|
|
30
|
+
};
|
|
31
|
+
const paths = (state, target) => {
|
|
32
|
+
const node = getNode(state, target);
|
|
33
|
+
if (node === undefined || node.metadata.nlink === 0)
|
|
34
|
+
return [];
|
|
35
|
+
if (node.kind === "directory") {
|
|
36
|
+
const path = directoryPath(state, target);
|
|
37
|
+
return path === undefined ? [] : [path];
|
|
38
|
+
}
|
|
39
|
+
return node.links.flatMap((link) => {
|
|
40
|
+
const directory = getNode(state, link.parent);
|
|
41
|
+
const path = directory?.kind === "directory" && directory.entries.get(link.name) === target
|
|
42
|
+
? entryPath(state, link)
|
|
43
|
+
: undefined;
|
|
44
|
+
return path === undefined ? [] : [path];
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
const rootPath = (state, roots) => {
|
|
48
|
+
let previous = SLASH_HEX;
|
|
49
|
+
for (const root of roots) {
|
|
50
|
+
const path = directoryPath(state, root);
|
|
51
|
+
if (path === undefined)
|
|
52
|
+
return fsFailure("ClosedCaller", "watch");
|
|
53
|
+
if (!below(path, previous))
|
|
54
|
+
return fsFailure("AccessDenied", "watch");
|
|
55
|
+
previous = path;
|
|
56
|
+
}
|
|
57
|
+
return previous;
|
|
58
|
+
};
|
|
59
|
+
const eventName = (event) => {
|
|
60
|
+
if (event.name !== undefined)
|
|
61
|
+
return event.name;
|
|
62
|
+
const bytes = getBytes(event.change.path);
|
|
63
|
+
const last = bytes.lastIndexOf(47);
|
|
64
|
+
return Array.from(bytes.subarray(last + 1), (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
65
|
+
};
|
|
66
|
+
/** Select and rebase names while publication holds both installed namespace states. @internal */
|
|
67
|
+
export const make = (options) => {
|
|
68
|
+
let selected = options.selected;
|
|
69
|
+
let oldSelected = selected;
|
|
70
|
+
let failure;
|
|
71
|
+
let previousRoot = SLASH_HEX;
|
|
72
|
+
let currentRoot = SLASH_HEX;
|
|
73
|
+
let terminal;
|
|
74
|
+
let rescanPath = SLASH_HEX;
|
|
75
|
+
const visible = (state, root, selection) => {
|
|
76
|
+
const aliases = paths(state, options.target);
|
|
77
|
+
for (const chain of options.additionalRoots ?? []) {
|
|
78
|
+
const boundary = rootPath(state, chain);
|
|
79
|
+
if (!Predicate.isString(boundary) || !aliases.some((path) => below(path, boundary)))
|
|
80
|
+
return [];
|
|
81
|
+
}
|
|
82
|
+
return aliases.filter((path) => below(path, root) && (selection === undefined || path === entryPath(state, selection)));
|
|
83
|
+
};
|
|
84
|
+
return {
|
|
85
|
+
boundedTerminal: true,
|
|
86
|
+
prepare: ({ before, after, renames }) => {
|
|
87
|
+
terminal = undefined;
|
|
88
|
+
oldSelected = selected;
|
|
89
|
+
const beforeRoot = rootPath(before, options.roots);
|
|
90
|
+
const afterRoot = rootPath(after, options.roots);
|
|
91
|
+
for (const chain of options.additionalRoots ?? []) {
|
|
92
|
+
const boundary = rootPath(after, chain);
|
|
93
|
+
if (!Predicate.isString(boundary)) {
|
|
94
|
+
failure = boundary;
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (!Predicate.isString(afterRoot)) {
|
|
99
|
+
failure = afterRoot;
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (!Predicate.isString(beforeRoot))
|
|
103
|
+
return;
|
|
104
|
+
previousRoot = beforeRoot;
|
|
105
|
+
currentRoot = afterRoot;
|
|
106
|
+
for (const rename of renames ?? []) {
|
|
107
|
+
if (rename.ino === options.target && selected !== undefined && selected.parent === rename.from.parent &&
|
|
108
|
+
selected.name === rename.from.name)
|
|
109
|
+
selected = rename.to;
|
|
110
|
+
}
|
|
111
|
+
const oldPaths = visible(before, previousRoot, oldSelected);
|
|
112
|
+
const nextPaths = visible(after, currentRoot, selected);
|
|
113
|
+
rescanPath = rebase(nextPaths[0] ?? oldPaths[0] ?? currentRoot, nextPaths.length > 0 ? currentRoot : previousRoot);
|
|
114
|
+
if (nextPaths.length === 0)
|
|
115
|
+
terminal = oldPaths.map((path) => rebase(path, previousRoot));
|
|
116
|
+
},
|
|
117
|
+
includes: () => failure === undefined,
|
|
118
|
+
project: (event, { before, after }) => {
|
|
119
|
+
if (failure !== undefined)
|
|
120
|
+
return undefined;
|
|
121
|
+
if (terminal === undefined && event.ino !== options.target && Predicate.isTagged(event.change, "Create")) {
|
|
122
|
+
const name = eventName(event);
|
|
123
|
+
const parent = getNode(before, event.parent);
|
|
124
|
+
const replacedPath = entryPath(before, { parent: event.parent, name });
|
|
125
|
+
// Rename publishes the incoming object; its replaced alias needs the old target's removal.
|
|
126
|
+
if (parent?.kind === "directory" && parent.entries.get(name) === options.target &&
|
|
127
|
+
replacedPath !== undefined && visible(before, previousRoot, oldSelected).includes(replacedPath)) {
|
|
128
|
+
return {
|
|
129
|
+
...event,
|
|
130
|
+
ino: options.target,
|
|
131
|
+
change: Change.cases.Remove.make({ path: ownedPath(nameBytes(rebase(replacedPath, previousRoot))) })
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
const removed = Predicate.isTagged(event.change, "Remove");
|
|
136
|
+
const installed = removed ? before : after;
|
|
137
|
+
const root = removed ? previousRoot : currentRoot;
|
|
138
|
+
const selection = removed ? oldSelected : selected;
|
|
139
|
+
let path;
|
|
140
|
+
const node = getNode(installed, event.ino);
|
|
141
|
+
if (event.ino === options.target) {
|
|
142
|
+
// Settlement emits the terminal removals once, behind already queued changes.
|
|
143
|
+
if (removed && terminal !== undefined)
|
|
144
|
+
return undefined;
|
|
145
|
+
path = node?.kind === "directory"
|
|
146
|
+
? directoryPath(installed, event.ino)
|
|
147
|
+
: entryPath(installed, { parent: event.parent, name: eventName(event) });
|
|
148
|
+
if (selection !== undefined && path !== entryPath(installed, selection))
|
|
149
|
+
return undefined;
|
|
150
|
+
}
|
|
151
|
+
else {
|
|
152
|
+
const scopes = visible(installed, root, selection);
|
|
153
|
+
path = entryPath(installed, { parent: event.parent, name: eventName(event) });
|
|
154
|
+
if (path === undefined || !scopes.some((scope) => {
|
|
155
|
+
if (!below(path, scope))
|
|
156
|
+
return false;
|
|
157
|
+
if (options.recursive)
|
|
158
|
+
return true;
|
|
159
|
+
return directoryPath(installed, event.parent) === scope;
|
|
160
|
+
}))
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
if (path === undefined || !below(path, root))
|
|
164
|
+
return undefined;
|
|
165
|
+
// A confinement root's outside parent is never reported as a rename.
|
|
166
|
+
if (event.ino === options.roots.at(-1) && !Predicate.isTagged(event.change, "Update"))
|
|
167
|
+
return undefined;
|
|
168
|
+
return {
|
|
169
|
+
...event,
|
|
170
|
+
change: Change.cases[event.change._tag].make({ path: ownedPath(nameBytes(rebase(path, root))) })
|
|
171
|
+
};
|
|
172
|
+
},
|
|
173
|
+
rescan: () => ({
|
|
174
|
+
change: Change.cases.Rescan.make({ path: ownedPath(nameBytes(rescanPath)) }),
|
|
175
|
+
parent: options.target,
|
|
176
|
+
ino: options.target
|
|
177
|
+
}),
|
|
178
|
+
failure: () => failure,
|
|
179
|
+
settle: () => terminal?.map((path) => ({
|
|
180
|
+
change: Change.cases.Remove.make({ path: ownedPath(nameBytes(path)) }),
|
|
181
|
+
parent: options.target,
|
|
182
|
+
ino: options.target
|
|
183
|
+
}))
|
|
184
|
+
};
|
|
185
|
+
};
|