mirror of
https://github.com/morluto/rea.git
synced 2026-11-14 18:52:48 +00:00
refactor: remove legacy seams and consolidate resource ownership (#10349)
This commit is contained in:
1 parent
5cddc69c51
commit
bbe2bcc979
301 files changed
+3148
-2804
No files matched your search
@@ -172,7 +172,7 @@ const electronModuleReferencedBy = ({
|
||||
};
|
||||
|
||||
/** Whether any analyzed source loads an Electron module. */
|
||||
export const electronModuleReferenced = (
|
||||
const electronModuleReferenced = (
|
||||
analysis: JavaScriptArtifactAnalysis,
|
||||
): boolean => analysis.files.some(electronModuleReferencedBy);
|
||||
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { expect, it } from "vitest";
|
||||
import {
|
||||
analyzeAndAdoptJavaScriptArtifactFiles,
|
||||
javaScriptAnalysisOrder,
|
||||
} from "./JavaScriptArtifactAnalysis.js";
|
||||
import { analyzeAndAdoptJavaScriptArtifactFiles } from "./JavaScriptArtifactAnalysis.js";
|
||||
import { createJavaScriptSemanticGraphProjection } from "./JavaScriptSemanticGraphBuilder.js";
|
||||
import { AnalysisResourceConstraintError } from "../../domain/analysisErrorCore.js";
|
||||
import { analyzeJavaScriptStaticSource } from "../../../tests/fixtures/javascriptStaticAnalysis.js";
|
||||
@@ -37,24 +34,6 @@ const inventory = [
|
||||
manifest("package.json"),
|
||||
];
|
||||
|
||||
it("offers application JavaScript before dependency JavaScript", () => {
|
||||
expect(javaScriptAnalysisOrder(inventory).map(({ path }) => path)).toEqual([
|
||||
"node_modules/a/package.json",
|
||||
"out-global-online/main/index.js",
|
||||
"out-global-online/preload/index.js",
|
||||
"package.json",
|
||||
"node_modules/a/index.js",
|
||||
"node_modules/b/lib/yaml.js",
|
||||
]);
|
||||
// A package merely named like a dependency directory is not one.
|
||||
expect(
|
||||
javaScriptAnalysisOrder([
|
||||
source("node_modules.js"),
|
||||
source("src/node_modules_shim.js"),
|
||||
]).map(({ path }) => path),
|
||||
).toEqual(["node_modules.js", "src/node_modules_shim.js"]);
|
||||
});
|
||||
|
||||
it("spends a limited result budget on first-party sources and keeps inventory order", async () => {
|
||||
const javascript = analyzeJavaScriptStaticSource("export const x = 1;");
|
||||
const analyzed: string[] = [];
|
||||
@@ -107,6 +86,38 @@ it("spends a limited result budget on first-party sources and keeps inventory or
|
||||
expect(outcome.interruption).toBeNull();
|
||||
});
|
||||
|
||||
it("does not treat files merely named like dependency directories as dependencies", async () => {
|
||||
const files = [
|
||||
source("node_modules/dep/index.js"),
|
||||
source("node_modules.js"),
|
||||
source("src/node_modules_shim.js"),
|
||||
];
|
||||
const analyzed: string[] = [];
|
||||
const javascript = analyzeJavaScriptStaticSource("export const x = 1;");
|
||||
await analyzeAndAdoptJavaScriptArtifactFiles(
|
||||
{
|
||||
files,
|
||||
unavailable_relevant_files: [],
|
||||
containers: [],
|
||||
text_bytes_read: 60,
|
||||
invalid_utf8_files: 0,
|
||||
},
|
||||
{
|
||||
analyze: async (file) => {
|
||||
analyzed.push(file.path);
|
||||
return ok({ javascript, module: null, projection: null });
|
||||
},
|
||||
},
|
||||
createJavaScriptSemanticGraphProjection(),
|
||||
async () => {},
|
||||
);
|
||||
expect(analyzed).toEqual([
|
||||
"node_modules.js",
|
||||
"src/node_modules_shim.js",
|
||||
"node_modules/dep/index.js",
|
||||
]);
|
||||
});
|
||||
|
||||
it("spends capacity in the caller's requested order for explicitly selected sources", async () => {
|
||||
const files = [
|
||||
source("node_modules/selected/index.js"),
|
||||
|
||||
@@ -329,7 +329,7 @@ const dependencySourcePath = (path: string): boolean =>
|
||||
* preloads, and Electron windows are examined. Offer application JavaScript
|
||||
* first and dependency JavaScript last; metadata files keep their position.
|
||||
*/
|
||||
export const javaScriptAnalysisOrder = (
|
||||
const javaScriptAnalysisOrder = (
|
||||
files: readonly JavaScriptArtifactFile[],
|
||||
): JavaScriptArtifactFile[] => {
|
||||
const application: JavaScriptArtifactFile[] = [];
|
||||
|
||||
@@ -17,7 +17,6 @@ import { javascriptArtifactAnalysisScopes } from "./JavaScriptArtifactAnalysisTy
|
||||
import { completeJavaScriptAnalysisSteps } from "./JavaScriptAnalysisControl.js";
|
||||
import { buildImmutableJavaScriptArtifactGraphSteps as buildGraph } from "./JavaScriptArtifactGraphBuilder.js";
|
||||
import { JavaScriptJsonDocuments } from "./JavaScriptJsonDocuments.js";
|
||||
import { electronModuleReferenced } from "./ElectronBoundaryAnalysis.js";
|
||||
|
||||
const SHA256 = "a".repeat(64);
|
||||
const PATH = "main.js";
|
||||
@@ -178,25 +177,14 @@ describe("Electron semantic coverage", () => {
|
||||
expect(electronSemantics).toBeUndefined();
|
||||
});
|
||||
|
||||
it("does not count a type-only Electron import as boundary coverage", () => {
|
||||
// A `.js` fixture cannot carry `import type`, so the module predicate is
|
||||
// exercised directly here.
|
||||
const analysisWith = (
|
||||
kind: "require" | "type-import",
|
||||
): JavaScriptArtifactAnalysis =>
|
||||
({
|
||||
files: [
|
||||
{
|
||||
file: fileFor(""),
|
||||
javascript: null,
|
||||
semantic: {
|
||||
ir: { moduleLinks: [{ kind, specifier: "electron" }] },
|
||||
},
|
||||
},
|
||||
],
|
||||
}) as unknown as JavaScriptArtifactAnalysis;
|
||||
expect(electronModuleReferenced(analysisWith("require"))).toBe(true);
|
||||
expect(electronModuleReferenced(analysisWith("type-import"))).toBe(false);
|
||||
it("does not count a type-only Electron import as boundary coverage", async () => {
|
||||
const result = await graphFor(
|
||||
'import type { BrowserWindow } from "electron";\nexport type Window = BrowserWindow;\n',
|
||||
undefined,
|
||||
"main.ts",
|
||||
);
|
||||
expect(result.electronSemantics).toBeUndefined();
|
||||
expect(result.limitations).not.toContain(EMPTY_COVERAGE);
|
||||
});
|
||||
|
||||
it("matches a supported Electron subpath and ignores an unrelated package", async () => {
|
||||
|
||||
@@ -9,6 +9,10 @@ import type { NextAppSourceContext } from "../../domain/javascript/javascriptRou
|
||||
import { compositeKey } from "../../domain/unicodeCodePointOrder.js";
|
||||
import type { JavaScriptJsonDocuments } from "./JavaScriptJsonDocuments.js";
|
||||
|
||||
/** Identify a package directory within its owning container. */
|
||||
const packageDirectoryKey = (container: string, directory: string): string =>
|
||||
compositeKey([container, directory]);
|
||||
|
||||
/** Package declarations and inventoried directories own source-router applicability. */
|
||||
export const createJavaScriptNextAppContext = (
|
||||
fileSet: Pick<
|
||||
@@ -25,7 +29,7 @@ export const createJavaScriptNextAppContext = (
|
||||
fileSet.files
|
||||
.filter(({ kind }) => kind === "package-json")
|
||||
.map((file) => [
|
||||
compositeKey([file.container_sha256, posix.dirname(file.path)]),
|
||||
packageDirectoryKey(file.container_sha256, posix.dirname(file.path)),
|
||||
file,
|
||||
]),
|
||||
);
|
||||
@@ -33,7 +37,7 @@ export const createJavaScriptNextAppContext = (
|
||||
fileSet.unavailable_relevant_files
|
||||
.filter(({ kind }) => kind === "package-json")
|
||||
.map((file) => [
|
||||
compositeKey([file.container_sha256, posix.dirname(file.path)]),
|
||||
packageDirectoryKey(file.container_sha256, posix.dirname(file.path)),
|
||||
file,
|
||||
]),
|
||||
);
|
||||
@@ -44,7 +48,7 @@ export const createJavaScriptNextAppContext = (
|
||||
;
|
||||
directory = posix.dirname(directory)
|
||||
) {
|
||||
const key = compositeKey([file.container_sha256, directory]);
|
||||
const key = packageDirectoryKey(file.container_sha256, directory);
|
||||
const unavailable = unavailablePackages.get(key);
|
||||
if (unavailable !== undefined)
|
||||
return {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { JAVASCRIPT_ANALYSIS_RESOURCE_DEFAULTS } from "../../domain/javascript/javascriptAnalysisResourceControls.js";
|
||||
import { javaScriptSemanticQueryInputSchema } from "../../domain/javascript/javascriptSemanticQuerySchemas.js";
|
||||
import {
|
||||
primitiveByteExpansionSource,
|
||||
@@ -9,8 +10,6 @@ import { expect, it } from "vitest";
|
||||
import { aliasSelectionBudgetSource } from "../../../tests/fixtures/javascriptAliasSelectionBudget.js";
|
||||
|
||||
import {
|
||||
SEMANTIC_GRAPH_FILE_NODE_CEILING,
|
||||
SEMANTIC_GRAPH_NODE_CEILING,
|
||||
createJavaScriptSemanticGraphProjection,
|
||||
projectJavaScriptSemanticFileSteps,
|
||||
} from "./JavaScriptSemanticGraphBuilder.js";
|
||||
@@ -972,7 +971,9 @@ it("bounds semantic node projection and reports the ceiling in coverage", () =>
|
||||
).join("\n");
|
||||
const graph = graphFor(statements);
|
||||
|
||||
expect(graph.nodes.length).toBeLessThanOrEqual(SEMANTIC_GRAPH_NODE_CEILING);
|
||||
expect(graph.nodes.length).toBeLessThanOrEqual(
|
||||
JAVASCRIPT_ANALYSIS_RESOURCE_DEFAULTS.semanticNodes,
|
||||
);
|
||||
expect(graph.coverage.truncated).toBe(true);
|
||||
expect(graph.coverage.status).toBe("partial");
|
||||
expect(graph.coverage.omitted_nodes).toBeNull();
|
||||
@@ -1136,9 +1137,13 @@ it("leaves a small application complete and untruncated", () => {
|
||||
it("does not mark a file truncated when it exactly fills its budget", () => {
|
||||
// Each unbound reference projects one expression, plus the module root.
|
||||
const graph = graphFor(
|
||||
"external;\n".repeat(SEMANTIC_GRAPH_FILE_NODE_CEILING - 1),
|
||||
"external;\n".repeat(
|
||||
JAVASCRIPT_ANALYSIS_RESOURCE_DEFAULTS.semanticNodesPerFile - 1,
|
||||
),
|
||||
);
|
||||
expect(graph.nodes).toHaveLength(
|
||||
JAVASCRIPT_ANALYSIS_RESOURCE_DEFAULTS.semanticNodesPerFile,
|
||||
);
|
||||
expect(graph.nodes).toHaveLength(SEMANTIC_GRAPH_FILE_NODE_CEILING);
|
||||
expect(graph.coverage).toMatchObject({
|
||||
truncated: false,
|
||||
omitted_nodes: 0,
|
||||
@@ -1160,7 +1165,9 @@ it("only records node loss when the budget rejects a new identity", () => {
|
||||
expect(state.fileNodeBudget).toBe(0);
|
||||
expect(state.fileNodesDropped).toBe(false);
|
||||
|
||||
expect(addSemanticGraphNode(state, semanticNodeFor("first"))).toBe(first);
|
||||
expect(addSemanticGraphNode(state, semanticNodeFor("first"))).toEqual(first);
|
||||
expect(state.nodes.size).toBe(2);
|
||||
expect(state.fileNodeBudget).toBe(0);
|
||||
expect(state.fileNodesDropped).toBe(false);
|
||||
|
||||
const blocked = addSemanticGraphNode(state, semanticNodeFor("third"));
|
||||
|
||||
@@ -111,36 +111,6 @@ interface FileContext extends SemanticFlowProjectionContext {
|
||||
>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resource-safety ceiling on retained semantic nodes.
|
||||
*
|
||||
* The semantic projection emits one node per AST expression, call site, binding
|
||||
* and property slot. That is unbounded in input size. A small tree of five files
|
||||
* already yields on the order of 170,000 nodes, and larger trees yield millions,
|
||||
* exhausting the heap before the result can be serialized.
|
||||
*
|
||||
* This is a real resource-safety constraint, not a presentation preference, so
|
||||
* the ceiling is applied and reported through the graph's own `coverage` fields
|
||||
* (`truncated`, `omitted_nodes`, `omitted_relations`, `limits`) rather than
|
||||
* silently dropping data.
|
||||
*
|
||||
* Defaults bound work even when ample heap is available. Callers may raise the
|
||||
* capacities explicitly; isolated worker heap, bounded record transport and
|
||||
* parent result admission independently constrain actual retained memory. The
|
||||
* result uses incremental serialization rather than a single JSON string.
|
||||
*/
|
||||
export const SEMANTIC_GRAPH_NODE_CEILING =
|
||||
JAVASCRIPT_ANALYSIS_RESOURCE_DEFAULTS.semanticNodes;
|
||||
|
||||
/**
|
||||
* Per-file share of the semantic node budget.
|
||||
*
|
||||
* Bound each source file independently so one large bundled library cannot
|
||||
* consume the whole tree's budget and starve the application's own modules.
|
||||
*/
|
||||
export const SEMANTIC_GRAPH_FILE_NODE_CEILING =
|
||||
JAVASCRIPT_ANALYSIS_RESOURCE_DEFAULTS.semanticNodesPerFile;
|
||||
|
||||
/** File-local semantic projection that does not retain consumed source IR. */
|
||||
export interface JavaScriptSemanticGraphProjection {
|
||||
/** Node capacity offered to the next independently analyzed source file. */
|
||||
@@ -216,8 +186,12 @@ export const createJavaScriptSemanticGraphProjection = (
|
||||
readonly fileUnknowns?: number;
|
||||
} = {},
|
||||
): JavaScriptSemanticGraphProjection => {
|
||||
const nodeCeiling = limits.totalNodes ?? SEMANTIC_GRAPH_NODE_CEILING;
|
||||
const fileNodeCeiling = limits.fileNodes ?? SEMANTIC_GRAPH_FILE_NODE_CEILING;
|
||||
// Bound total and per-file work so a large dependency cannot starve the graph.
|
||||
const nodeCeiling =
|
||||
limits.totalNodes ?? JAVASCRIPT_ANALYSIS_RESOURCE_DEFAULTS.semanticNodes;
|
||||
const fileNodeCeiling =
|
||||
limits.fileNodes ??
|
||||
JAVASCRIPT_ANALYSIS_RESOURCE_DEFAULTS.semanticNodesPerFile;
|
||||
const unknownCeiling =
|
||||
limits.totalUnknowns ??
|
||||
JAVASCRIPT_ANALYSIS_RESOURCE_DEFAULTS.semanticUnknowns;
|
||||
|
||||
Reference in new issue
Block a user