mirror of
https://github.com/morluto/rea
synced 2026-10-27 06:33:32 -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>
317 lines
11 KiB
TypeScript
317 lines
11 KiB
TypeScript
import { compareUnicodeCodePoints } from "../domain/unicodeCodePointOrder.js";
|
|
import {
|
|
createHistoricalSourceGraph,
|
|
historicalSourceLanguages,
|
|
historicalSourceManifests,
|
|
historicalSourceParseFailureKey,
|
|
type HistoricalSourceGraph,
|
|
type HistoricalSourceGraphInput,
|
|
} from "../domain/referenceSourceGraph.js";
|
|
import { err, ok, type Result } from "../domain/result.js";
|
|
import type { ArtifactResourceScope } from "../artifacts/ArtifactResourceScope.js";
|
|
import type { AnalysisCleanupObservation } from "../domain/analysisErrorBase.js";
|
|
import { readReferenceSource } from "../reference/ReferenceSourceReader.js";
|
|
import type {
|
|
ReferenceSourceEntryKind,
|
|
ReferenceSourceRead,
|
|
ReferenceSourceReaderError,
|
|
} from "../reference/ReferenceSourceReaderTypes.js";
|
|
import { parseReferenceSourceEntries } from "./ReferenceSourceImportEntries.js";
|
|
import { readReferenceSourceVcs } from "./ReferenceSourceVcsAdapter.js";
|
|
import type {
|
|
ReferenceSourceImportError,
|
|
ReferenceSourceImportOptions,
|
|
} from "./ReferenceSourceImportTypes.js";
|
|
import {
|
|
prepareReferenceSourceImport,
|
|
type PreparedReferenceSourceImport,
|
|
} from "./ReferenceSourceImportPolicy.js";
|
|
|
|
const failure = (
|
|
code: ReferenceSourceImportError["code"],
|
|
message: string,
|
|
): ReferenceSourceImportError => ({
|
|
tag: "reference-source-import",
|
|
code,
|
|
message,
|
|
});
|
|
|
|
const cancelled = (
|
|
partial?: ReferenceSourceRead,
|
|
): ReferenceSourceImportError => ({
|
|
...failure("cancelled", "Reference source import cancelled"),
|
|
...(partial === undefined ? {} : { partial }),
|
|
});
|
|
|
|
const isAborted = (signal?: AbortSignal): boolean => signal?.aborted === true;
|
|
|
|
const relationshipKey = (
|
|
relationship: HistoricalSourceGraphInput["relationships"][number],
|
|
): string =>
|
|
`${relationship.from_path}\u0000${relationship.to}\u0000${relationship.kind}\u0000${relationship.resolution}\u0000${relationship.parse_state}`;
|
|
|
|
const deduplicateRelationships = (
|
|
relationships: HistoricalSourceGraphInput["relationships"],
|
|
): HistoricalSourceGraphInput["relationships"] => {
|
|
const seen = new Set<string>();
|
|
const result: HistoricalSourceGraphInput["relationships"] = [];
|
|
for (const relationship of relationships) {
|
|
const key = relationshipKey(relationship);
|
|
if (seen.has(key)) continue;
|
|
seen.add(key);
|
|
result.push(relationship);
|
|
}
|
|
return result;
|
|
};
|
|
|
|
/** Sort parse failures deterministically while retaining distinct reasons. */
|
|
export const normalizeHistoricalSourceParseFailures = (
|
|
failures: HistoricalSourceGraphInput["parse_failures"],
|
|
): HistoricalSourceGraphInput["parse_failures"] =>
|
|
[...failures]
|
|
.sort((left, right) =>
|
|
compareUnicodeCodePoints(
|
|
historicalSourceParseFailureKey(left),
|
|
historicalSourceParseFailureKey(right),
|
|
),
|
|
)
|
|
.filter((value, index, array) => {
|
|
if (index === 0) return true;
|
|
const previous = array[index - 1];
|
|
return (
|
|
previous === undefined ||
|
|
historicalSourceParseFailureKey(value) !==
|
|
historicalSourceParseFailureKey(previous)
|
|
);
|
|
});
|
|
|
|
const buildProvenance = (
|
|
options: ReferenceSourceImportOptions,
|
|
): HistoricalSourceGraphInput["provenance"] => ({
|
|
importer: options.importer ?? "rea-reference-source-import",
|
|
importer_version: options.importerVersion ?? null,
|
|
caller: options.caller,
|
|
});
|
|
|
|
const deriveInventoryState = (
|
|
input: Pick<
|
|
HistoricalSourceGraphInput,
|
|
| "entries"
|
|
| "relationships"
|
|
| "parse_failures"
|
|
| "exclusions"
|
|
| "limitations"
|
|
>,
|
|
): "complete" | "partial" | "unknown" => {
|
|
if (input.limitations.length > 0) return "partial";
|
|
if (input.exclusions.length > 0) return "partial";
|
|
if (input.parse_failures.length > 0) return "partial";
|
|
const partialEntry = input.entries.some(
|
|
(entry) =>
|
|
entry.limitations.length > 0 ||
|
|
(entry.kind === "file" && entry.content_state !== "hashed") ||
|
|
(entry.kind === "directory" && entry.tree_state !== "enumerated") ||
|
|
(entry.kind === "symlink" && entry.target_state !== "internal"),
|
|
);
|
|
if (partialEntry) return "partial";
|
|
const partialRelationship = input.relationships.some(
|
|
({ parse_state, resolution }) =>
|
|
parse_state !== "parsed" ||
|
|
["unresolved", "unknown"].includes(resolution),
|
|
);
|
|
if (partialRelationship) return "partial";
|
|
return "complete";
|
|
};
|
|
|
|
const sortExclusions = (
|
|
exclusions: HistoricalSourceGraphInput["exclusions"],
|
|
): HistoricalSourceGraphInput["exclusions"] =>
|
|
[...exclusions].sort((left, right) => {
|
|
const byPath = compareUnicodeCodePoints(left.path, right.path);
|
|
if (byPath !== 0) return byPath;
|
|
const byReason = compareUnicodeCodePoints(left.reason, right.reason);
|
|
if (byReason !== 0) return byReason;
|
|
return compareUnicodeCodePoints(
|
|
"pattern" in left ? left.pattern : "",
|
|
"pattern" in right ? right.pattern : "",
|
|
);
|
|
});
|
|
|
|
const createShouldExclude =
|
|
(
|
|
exclusions: HistoricalSourceGraphInput["exclusions"],
|
|
secrets: PreparedReferenceSourceImport["secrets"],
|
|
ignored: PreparedReferenceSourceImport["ignored"],
|
|
): ((path: string, kind: ReferenceSourceEntryKind) => boolean) =>
|
|
(path, kind) => {
|
|
const patternPath = kind === "directory" ? `${path}/` : path;
|
|
const secretMatch = secrets.test(patternPath);
|
|
if (secretMatch.ignored) {
|
|
if (!secretMatch.rule)
|
|
throw new Error(`Ignored secret path has no matching rule: ${path}`);
|
|
exclusions.push({
|
|
path,
|
|
reason: "configured-secret",
|
|
pattern: secretMatch.rule.pattern,
|
|
});
|
|
return true;
|
|
}
|
|
const match = ignored.test(patternPath);
|
|
if (!match.ignored) return false;
|
|
if (!match.rule)
|
|
throw new Error(`Ignored path has no matching rule: ${path}`);
|
|
const reason = match.rule.mark;
|
|
if (
|
|
reason !== "project-ignored" &&
|
|
reason !== "default-ignored" &&
|
|
reason !== "caller-excluded"
|
|
)
|
|
throw new Error(`Ignored path has unknown rule origin: ${path}`);
|
|
exclusions.push({ path, reason, pattern: match.rule.pattern });
|
|
return true;
|
|
};
|
|
|
|
/**
|
|
* Import a reference source directory into a committed historical source graph.
|
|
*
|
|
* The import is deterministic, parallel-safe, and never executes source, hooks,
|
|
* git subprocesses, or network requests. Paths explicitly excluded by the
|
|
* caller's reference-source policy are omitted from the graph.
|
|
*/
|
|
export const importReferenceSource = async (
|
|
options: ReferenceSourceImportOptions,
|
|
resources: ArtifactResourceScope,
|
|
): Promise<Result<HistoricalSourceGraph, ReferenceSourceImportError>> => {
|
|
if (isAborted(options.signal)) return err(cancelled());
|
|
const prepared = await prepareReferenceSourceImport(options, resources);
|
|
if (!prepared.ok) return prepared;
|
|
const { ignored, root, secrets } = prepared.value;
|
|
if (isAborted(options.signal)) return err(cancelled());
|
|
|
|
const exclusions: HistoricalSourceGraphInput["exclusions"] = [];
|
|
const shouldExclude = createShouldExclude(exclusions, secrets, ignored);
|
|
|
|
const [readResult, vcsResult] = await Promise.all([
|
|
readReferenceSource(root, resources, {
|
|
...(options.signal === undefined ? {} : { signal: options.signal }),
|
|
shouldExclude,
|
|
}),
|
|
readReferenceSourceVcs(root, resources, options.signal),
|
|
]);
|
|
|
|
if (!readResult.ok)
|
|
return err(
|
|
combineReadFailures(
|
|
readResult.error,
|
|
vcsResult.ok ? undefined : vcsResult.error,
|
|
readResult.error.partial,
|
|
),
|
|
);
|
|
if (!vcsResult.ok)
|
|
return err(
|
|
combineReadFailures(vcsResult.error, undefined, readResult.value),
|
|
);
|
|
|
|
const vcs = vcsResult.value;
|
|
|
|
if (isAborted(options.signal)) return err(cancelled(readResult.value));
|
|
|
|
const read = readResult.value;
|
|
const filePaths = new Set(
|
|
read.entries
|
|
.filter((entry) => entry.status === "read" && entry.kind === "file")
|
|
.map((entry) => entry.path),
|
|
);
|
|
|
|
const { entries, relationships, parseFailures, limitations } =
|
|
parseReferenceSourceEntries(read, filePaths, options.signal);
|
|
|
|
if (isAborted(options.signal)) return err(cancelled(read));
|
|
|
|
const uniqueRelationships = deduplicateRelationships(relationships);
|
|
const uniqueFailures = normalizeHistoricalSourceParseFailures(parseFailures);
|
|
|
|
const sortedEntries = [...entries].sort((left, right) =>
|
|
compareUnicodeCodePoints(left.path, right.path),
|
|
);
|
|
const sortedExclusions = sortExclusions(exclusions);
|
|
const sortedLimitations = [...limitations].sort(compareUnicodeCodePoints);
|
|
|
|
const input: HistoricalSourceGraphInput = {
|
|
schema: "HistoricalSourceGraph",
|
|
authority: "historical-reference",
|
|
root_alias: "$REFERENCE_ROOT",
|
|
inventory_state: deriveInventoryState({
|
|
entries: sortedEntries,
|
|
relationships: uniqueRelationships,
|
|
parse_failures: uniqueFailures,
|
|
exclusions: sortedExclusions,
|
|
limitations: sortedLimitations,
|
|
}),
|
|
entries: sortedEntries,
|
|
relationships: uniqueRelationships.sort((left, right) =>
|
|
compareUnicodeCodePoints(relationshipKey(left), relationshipKey(right)),
|
|
),
|
|
parse_failures: uniqueFailures,
|
|
exclusions: sortedExclusions,
|
|
languages: historicalSourceLanguages(sortedEntries),
|
|
manifests: historicalSourceManifests(sortedEntries),
|
|
vcs,
|
|
provenance: buildProvenance(options),
|
|
limitations: sortedLimitations,
|
|
};
|
|
|
|
try {
|
|
return ok(createHistoricalSourceGraph(input));
|
|
} catch (cause: unknown) {
|
|
return err(
|
|
failure(
|
|
"parse",
|
|
cause instanceof Error ? cause.message : "Graph failed validation",
|
|
),
|
|
);
|
|
}
|
|
};
|
|
|
|
const combineReadFailures = (
|
|
primary: ReferenceSourceReaderError,
|
|
other: ReferenceSourceReaderError | undefined,
|
|
partial: ReferenceSourceRead | undefined,
|
|
): ReferenceSourceImportError => {
|
|
const code =
|
|
primary.code === "cancelled" || other?.code === "cancelled"
|
|
? "cancelled"
|
|
: primary.code;
|
|
const cleanup = mergeCleanup(primary.cleanup, other?.cleanup);
|
|
const message =
|
|
other === undefined
|
|
? primary.message
|
|
: `${primary.message}; additional reference metadata failure: ${other.message}`;
|
|
const cause =
|
|
other === undefined
|
|
? primary.cause
|
|
: new AggregateError(
|
|
[primary, other],
|
|
"Reference source inventory and metadata both failed",
|
|
{ cause: primary },
|
|
);
|
|
return {
|
|
...failure(code, message),
|
|
...(cleanup === undefined ? {} : { cleanup }),
|
|
...(partial === undefined ? {} : { partial }),
|
|
...(cause === undefined ? {} : { cause }),
|
|
};
|
|
};
|
|
|
|
const mergeCleanup = (
|
|
first: AnalysisCleanupObservation | undefined,
|
|
second: AnalysisCleanupObservation | undefined,
|
|
): AnalysisCleanupObservation | undefined => {
|
|
if (first === undefined) return second;
|
|
if (second === undefined) return first;
|
|
return {
|
|
reason: `${first.reason}; ${second.reason}`,
|
|
resources: [...new Set([...first.resources, ...second.resources])],
|
|
};
|
|
};
|