mirror of
https://github.com/morluto/rea
synced 2026-10-26 22:28:27 -04:00
0ecf3f2438
* fix(build): emit no runtime imports for type-only bindings
Under verbatimModuleSyntax, an import whose specifiers all carry inline
`type` modifiers compiles to `import {} from "..."`, a side-effect import
that still resolves and evaluates the module. 78 compiled modules carried
one, including `import {} from "playwright-core"` in the browser scenario
session, which loaded Playwright's module graph at MCP startup.
Use top-level `import type` for these bindings and enable oxlint's
typescript/no-import-type-side-effects rule so the pattern cannot return.
Refs #1685
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* perf(mcp): load Playwright when a scenario launches, not at startup
The browser scenario and Electron active providers are constructed before
the stdio server serves, and both bound Playwright's launchers at module
load. Resolve `chromium` and `_electron` from playwright-core when a
capture opens instead, as the web module resolver already does.
MCP startup now resolves 1,312 modules (4,902 resolutions) instead of
1,598 (6,076) before reading the first request, and no longer loads
playwright-core. Provider construction, the tool catalog, and capability
inventory are unchanged.
A boundary test spawns the stdio server, initializes, lists tools, and
asserts playwright-core and isomorphic-git were not resolved; the CLI
probe now exercises the real launch path to prove detection.
Closes #1685
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
378 lines
11 KiB
TypeScript
378 lines
11 KiB
TypeScript
import type { BigIntStats, Dir, Dirent } from "node:fs";
|
|
import { opendir, readlink, realpath } from "node:fs/promises";
|
|
import { dirname, join, resolve } from "node:path";
|
|
import { ArtifactReaderFailure } from "../artifacts/ArtifactReader.js";
|
|
import type { ArtifactCleanupAttempt } from "../artifacts/ArtifactResourceScope.js";
|
|
import { OwnedDirectoryHandle } from "../filesystem/OwnedDirectoryHandle.js";
|
|
import { err, ok } from "../domain/result.js";
|
|
|
|
import {
|
|
cancelled,
|
|
entryFailure,
|
|
failure,
|
|
filesystemFailureDetail,
|
|
safeSize,
|
|
} from "./ReferenceSourceReaderErrors.js";
|
|
import { isPathWithinRoot } from "../domain/localPath.js";
|
|
import { pathFromRoot } from "./ReferenceSourceReaderPaths.js";
|
|
import { readStableFile } from "./ReferenceSourceReaderFile.js";
|
|
import {
|
|
isAborted,
|
|
sameFile,
|
|
validateDirectory,
|
|
bigLstat,
|
|
} from "./ReferenceSourceReaderValidate.js";
|
|
import type {
|
|
PendingDirectory,
|
|
ReferenceSourceEntry,
|
|
ReferenceSourceEntryKind,
|
|
ReferenceSourceResult,
|
|
TraversalState,
|
|
} from "./ReferenceSourceReaderTypes.js";
|
|
|
|
export const traverseDirectory = async (
|
|
state: TraversalState,
|
|
current: PendingDirectory,
|
|
): Promise<ReferenceSourceResult<undefined>> => {
|
|
const before = await validateDirectory(
|
|
state.root,
|
|
state.rootIdentity,
|
|
current.path,
|
|
state.signal,
|
|
);
|
|
if (isAborted(state.signal)) return { ok: false, error: cancelled() };
|
|
if (!before.ok) {
|
|
state.entries.push(
|
|
entryFailure(
|
|
pathFromRoot(state.root, current.path),
|
|
"directory",
|
|
before.code,
|
|
before.message,
|
|
),
|
|
);
|
|
return { ok: true, value: undefined };
|
|
}
|
|
const directories: PendingDirectory[] = [];
|
|
let readFailure: string | undefined;
|
|
let handle: Dir | undefined;
|
|
try {
|
|
handle = await opendir(current.path);
|
|
} catch (cause: unknown) {
|
|
readFailure = directoryReadFailure(cause);
|
|
}
|
|
if (handle !== undefined) {
|
|
const owner = new OwnedDirectoryHandle(handle);
|
|
let result: ReferenceSourceResult<undefined> = ok(undefined);
|
|
let cleanup: ArtifactCleanupAttempt;
|
|
try {
|
|
for (;;) {
|
|
let child: Dirent | null;
|
|
try {
|
|
child = await handle.read();
|
|
} catch (cause: unknown) {
|
|
readFailure = directoryReadFailure(cause);
|
|
break;
|
|
}
|
|
if (child === null) break;
|
|
result = await processEntry(state, current, child.name, directories);
|
|
if (!result.ok) break;
|
|
}
|
|
} finally {
|
|
cleanup = await state.resources.release({
|
|
kind: "directory-handle",
|
|
handle: owner,
|
|
resource: current.path,
|
|
});
|
|
}
|
|
if (cleanup.kind === "failed") {
|
|
if (result.ok && !isAborted(state.signal) && readFailure !== undefined)
|
|
state.entries.push(
|
|
entryFailure(
|
|
pathFromRoot(state.root, current.path),
|
|
"directory",
|
|
"io",
|
|
readFailure,
|
|
),
|
|
);
|
|
const observation = ArtifactReaderFailure.cleanupObservation(
|
|
cleanup.cause,
|
|
current.path,
|
|
);
|
|
const previous = result.ok ? undefined : result.error.cleanup;
|
|
const primary = result.ok
|
|
? isAborted(state.signal)
|
|
? cancelled()
|
|
: failure(
|
|
"io",
|
|
readFailure ??
|
|
`Reference directory cleanup failed: ${current.path}`,
|
|
)
|
|
: result.error;
|
|
return err({
|
|
...primary,
|
|
cleanup:
|
|
previous === undefined
|
|
? observation
|
|
: {
|
|
reason: `${previous.reason}; ${observation.reason}`,
|
|
resources: [
|
|
...new Set([...previous.resources, ...observation.resources]),
|
|
],
|
|
},
|
|
cause: primary.cause ?? cleanup.cause,
|
|
});
|
|
}
|
|
if (!result.ok) return result;
|
|
}
|
|
if (isAborted(state.signal)) return { ok: false, error: cancelled() };
|
|
if (readFailure !== undefined) {
|
|
state.entries.push(
|
|
entryFailure(
|
|
pathFromRoot(state.root, current.path),
|
|
"directory",
|
|
"io",
|
|
readFailure,
|
|
),
|
|
);
|
|
return { ok: true, value: undefined };
|
|
}
|
|
const after = await validateDirectory(
|
|
state.root,
|
|
state.rootIdentity,
|
|
current.path,
|
|
state.signal,
|
|
);
|
|
if (isAborted(state.signal)) return { ok: false, error: cancelled() };
|
|
if (!after.ok || !sameFile(before.stats, after.stats)) {
|
|
state.entries.push(
|
|
entryFailure(
|
|
pathFromRoot(state.root, current.path),
|
|
"directory",
|
|
"changed",
|
|
"Directory changed while it was read",
|
|
),
|
|
);
|
|
return { ok: true, value: undefined };
|
|
}
|
|
if (current.path !== state.root)
|
|
state.entries.push({
|
|
status: "read",
|
|
kind: "directory",
|
|
path: pathFromRoot(state.root, current.path),
|
|
});
|
|
directories.reverse();
|
|
state.pending.push(...directories);
|
|
return { ok: true, value: undefined };
|
|
};
|
|
|
|
const directoryReadFailure = (cause: unknown): string => {
|
|
const message = filesystemFailureDetail(cause, "Directory could not be read");
|
|
if (message === undefined) throw cause;
|
|
return message;
|
|
};
|
|
|
|
const readMetadata = async (
|
|
path: string,
|
|
): Promise<
|
|
| { readonly ok: true; readonly value: BigIntStats }
|
|
| { readonly ok: false; readonly message: string }
|
|
> => {
|
|
try {
|
|
return { ok: true, value: await bigLstat(path) };
|
|
} catch (cause: unknown) {
|
|
const message = filesystemFailureDetail(
|
|
cause,
|
|
"Entry metadata could not be read",
|
|
);
|
|
if (message === undefined) throw cause;
|
|
return { ok: false, message };
|
|
}
|
|
};
|
|
|
|
const processEntry = async (
|
|
state: TraversalState,
|
|
current: PendingDirectory,
|
|
name: string,
|
|
directories: PendingDirectory[],
|
|
): Promise<ReferenceSourceResult<undefined>> => {
|
|
if (isAborted(state.signal)) return { ok: false, error: cancelled() };
|
|
const absolute = join(current.path, name);
|
|
const path = pathFromRoot(state.root, absolute);
|
|
const metadata = await readMetadata(absolute);
|
|
if (isAborted(state.signal)) return { ok: false, error: cancelled() };
|
|
if (!metadata.ok) {
|
|
state.entries.push(entryFailure(path, "unknown", "io", metadata.message));
|
|
return { ok: true, value: undefined };
|
|
}
|
|
const kind = metadata.value.isSymbolicLink()
|
|
? "symlink"
|
|
: metadata.value.isDirectory()
|
|
? "directory"
|
|
: metadata.value.isFile()
|
|
? "file"
|
|
: "other";
|
|
const excluded = applyExclusion(state.shouldExclude, path, kind);
|
|
if (!excluded.ok)
|
|
return {
|
|
ok: false,
|
|
error: {
|
|
tag: "reference-source-reader",
|
|
code: "io",
|
|
message: "Reference source exclusion check failed",
|
|
},
|
|
};
|
|
if (excluded.value) return { ok: true, value: undefined };
|
|
if (isAborted(state.signal)) return { ok: false, error: cancelled() };
|
|
if (kind === "symlink")
|
|
state.entries.push(
|
|
await describeSymlink(state.root, absolute, path, state.signal),
|
|
);
|
|
else if (kind === "directory") {
|
|
directories.push({ path: absolute });
|
|
} else if (kind !== "file")
|
|
state.entries.push(
|
|
entryFailure(
|
|
path,
|
|
"other",
|
|
"unsupported",
|
|
"Entry is not a regular file",
|
|
safeSize(metadata.value.size),
|
|
),
|
|
);
|
|
else return processFileEntry(state, absolute, path, metadata.value);
|
|
return { ok: true, value: undefined };
|
|
};
|
|
|
|
const processFileEntry = async (
|
|
state: TraversalState,
|
|
absolute: string,
|
|
path: string,
|
|
metadata: BigIntStats,
|
|
): Promise<ReferenceSourceResult<undefined>> => {
|
|
const result = await readStableFile({
|
|
resources: state.resources,
|
|
root: state.root,
|
|
rootIdentity: state.rootIdentity,
|
|
absolute,
|
|
path,
|
|
expected: metadata,
|
|
...(state.signal === undefined ? {} : { signal: state.signal }),
|
|
});
|
|
const { entry } = result;
|
|
if (entry.status === "read" && entry.kind === "file")
|
|
state.bytesRead += entry.bytes.byteLength;
|
|
state.entries.push(entry);
|
|
if (result.cleanup !== undefined)
|
|
return err({
|
|
tag: "reference-source-reader",
|
|
code:
|
|
entry.status === "failed" && entry.code === "cancelled"
|
|
? "cancelled"
|
|
: "io",
|
|
message:
|
|
entry.status === "failed"
|
|
? entry.message
|
|
: `Reference file cleanup failed: ${path}`,
|
|
cleanup: result.cleanup,
|
|
});
|
|
return ok(undefined);
|
|
};
|
|
|
|
const describeSymlink = async (
|
|
root: string,
|
|
absolute: string,
|
|
path: string,
|
|
signal?: AbortSignal,
|
|
): Promise<ReferenceSourceEntry> => {
|
|
try {
|
|
const rawTarget = await readlink(absolute);
|
|
const lexicalTarget = resolve(dirname(absolute), rawTarget);
|
|
if (!isPathWithinRoot(root, lexicalTarget))
|
|
return {
|
|
status: "read",
|
|
kind: "symlink",
|
|
path,
|
|
target: lexicalTarget,
|
|
targetState: "external",
|
|
};
|
|
try {
|
|
const canonicalTarget = await realpath(lexicalTarget);
|
|
return isPathWithinRoot(root, canonicalTarget)
|
|
? {
|
|
status: "read",
|
|
kind: "symlink",
|
|
path,
|
|
target: pathFromRoot(root, canonicalTarget),
|
|
targetState: "internal",
|
|
}
|
|
: {
|
|
status: "read",
|
|
kind: "symlink",
|
|
path,
|
|
target: canonicalTarget,
|
|
targetState: "external",
|
|
};
|
|
} catch (cause: unknown) {
|
|
if (isAborted(signal))
|
|
return entryFailure(
|
|
path,
|
|
"symlink",
|
|
"cancelled",
|
|
"Symlink inspection cancelled",
|
|
);
|
|
const code =
|
|
cause instanceof Error ? Reflect.get(cause, "code") : undefined;
|
|
if (code !== "ENOENT") {
|
|
const message = filesystemFailureDetail(
|
|
cause,
|
|
"Symbolic link target could not be resolved",
|
|
);
|
|
if (message === undefined) throw cause;
|
|
return entryFailure(path, "symlink", "io", message);
|
|
}
|
|
const missingOutsideRoot = !isPathWithinRoot(root, lexicalTarget);
|
|
return {
|
|
status: "read",
|
|
kind: "symlink",
|
|
path,
|
|
target: missingOutsideRoot
|
|
? lexicalTarget
|
|
: pathFromRoot(root, lexicalTarget),
|
|
targetState: "missing",
|
|
};
|
|
}
|
|
} catch (cause: unknown) {
|
|
if (isAborted(signal))
|
|
return entryFailure(
|
|
path,
|
|
"symlink",
|
|
"cancelled",
|
|
"Symlink inspection cancelled",
|
|
);
|
|
const message = filesystemFailureDetail(
|
|
cause,
|
|
"Symbolic link target could not be read",
|
|
);
|
|
if (message === undefined) throw cause;
|
|
return entryFailure(path, "symlink", "io", message);
|
|
}
|
|
};
|
|
|
|
const applyExclusion = (
|
|
shouldExclude:
|
|
| ((path: string, kind: ReferenceSourceEntryKind) => boolean)
|
|
| undefined,
|
|
path: string,
|
|
kind: ReferenceSourceEntryKind,
|
|
): { readonly ok: true; readonly value: boolean } | { readonly ok: false } => {
|
|
try {
|
|
return { ok: true, value: shouldExclude?.(path, kind) === true };
|
|
} catch (cause: unknown) {
|
|
// Exclusion predicates are caller-supplied; a throwing predicate fails
|
|
// closed and the caller-visible `{ ok: false }` preserves the rejection
|
|
// without propagating an arbitrary predicate cause.
|
|
void cause;
|
|
return { ok: false };
|
|
}
|
|
};
|