mirror of
https://github.com/morluto/rea.git
synced 2026-11-15 19:23:24 +00:00
* feat(javascript): make the member-mutation work allowance a runtime setting The member-mutation alias-state allowance from #11144 was a fixed constant, so a refused source could not be admitted without a code change. Register javascript.member_mutation_alias_states (default 524288, 1 to 8388608 alias states) with the launch seed REA_JAVASCRIPT_MEMBER_MUTATION_ALIAS_STATES. The application, web bundle and reference-source boundaries resolve it from the accepted operation snapshot and launch environment, refuse a malformed seed before analysis, and send it to the workers with each source. A refusal names the setting, its effective value, source and range, and the recovery; worker parents rebuild it from the allowance they sent. Refs #8294 * fix(javascript): keep the setting and recovery of a refused web script A web bundle script refused by the member-mutation allowance kept only its reason and reported limits, so the public unknown lost the setting, value, source and recovery. Carry the refusal's structured limit and remediation on the per-script result and its javascript_analysis unknown, grouping scripts only when that recovery agrees. Assert structured limit facts and the owning formatter's recovery instead of fixed wording. * fix(browser): keep only registered setting limits on a refused web script * feat(javascript): keep syntax facts for a source whose member-mutation phase is refused (#11187) * feat(javascript): keep syntax facts for a source whose member-mutation phase is refused A source refused by the member-mutation work allowance during static analysis lost every fact, so the application graph counted it as skipped. Reference-source import failed outright on such a file. Checkpoint the parser, comment and text facts the static owner can still state, with parse status partial, examination not-examined and refused_phase member-mutations, before reporting the refusal; the semantic phase is not started because it indexes the same mutations. Reference-source import keeps the file's declaration-level imports and records that its CommonJS require calls were not examined. Refs #8294 * test(javascript): compare the reference-source refusal with its owning formatter * feat(javascript): keep a refused web script's parse and text facts A captured script whose member-mutation phase was refused lost every fact in analyze_web_bundle, so it was neither counted as parsed nor kept its vendor markers. The analyzer now returns the script as unavailable with partial facts: refused phase, parse status and the inferences read from script text alone. Findings gathered before the refusal are dropped, because they may have read an incomplete mutation index. The worker parent states the refusal and its setting recovery from the allowance it sent, through the same helper the isolated analyzer uses for every unavailable script. The bundle counts the script as parsed, keeps its text inferences, and its javascript_analysis unknown carries refused_phase. Refs #8294 * chore(mcp): record the member-mutation setting in the advertised summary * fix(javascript): rebuild a parked source worker when the member-mutation allowance changes A worker parked on a resource scope was reused when heap, timeout and precision matched, but its requests carry the allowance it was built with. After update_analysis_settings accepted a new allowance, the next analysis on that scope kept the old one. The reuse check now compares the allowance's value and source, so a changed setting gets a worker built with it. The web bundle worker already takes the allowance per script.
153 lines
5.4 KiB
TypeScript
153 lines
5.4 KiB
TypeScript
import { mkdir, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { expect, it } from "vitest";
|
|
|
|
import { MAXIMUM_JAVASCRIPT_MEMBER_MUTATION_ALIAS_STATES } from "../../../src/domain/analysisResourceLimits.js";
|
|
import { javaScriptMemberMutationRefusal } from "../../../src/domain/javascript/javascriptSemanticWorkAllowance.js";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { analyzeJavaScriptApplication } from "../../support/javascriptApplicationScope.js";
|
|
|
|
/** Four conditional self-selections before one member write (#8294). */
|
|
const SOURCE = [
|
|
"function probe(input, env) { var m; if (env.first) m = env.first.info;",
|
|
...Array.from(
|
|
{ length: 4 },
|
|
(_, index) =>
|
|
`if (/case${String(index)}/.test(input)) m = m[${String(index % 2)}];`,
|
|
),
|
|
"m[1] = input.length; return m; } module.exports = probe;",
|
|
].join(" ");
|
|
|
|
const createApplication = async (): Promise<string> => {
|
|
const root = await createTestTempDirectory("rea-member-mutation-setting-");
|
|
const app = join(root, "app");
|
|
await mkdir(app);
|
|
await writeFile(join(app, "main.js"), `${SOURCE}\n`);
|
|
await writeFile(
|
|
join(app, "package.json"),
|
|
JSON.stringify({ name: "fixture", main: "main.js" }),
|
|
);
|
|
return app;
|
|
};
|
|
|
|
/** The per-source analysis failure the application graph retains. */
|
|
const sourceFailure = (value: unknown): Record<string, unknown> | undefined => {
|
|
if (value === null || typeof value !== "object") return undefined;
|
|
const record = value as Record<string, unknown>;
|
|
if (
|
|
typeof record.analysis_failure === "object" &&
|
|
record.analysis_failure !== null
|
|
)
|
|
return record.analysis_failure as Record<string, unknown>;
|
|
for (const child of Object.values(record)) {
|
|
const found = sourceFailure(child);
|
|
if (found !== undefined) return found;
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
/** The static coverage of the one source whose member-mutation phase was refused. */
|
|
const refusedCoverage = (value: unknown): unknown => {
|
|
if (value === null || typeof value !== "object") return undefined;
|
|
const record = value as Record<string, unknown>;
|
|
const coverage = record.analysis_coverage as
|
|
| { readonly examination?: { readonly refused_phase?: string } }
|
|
| undefined;
|
|
if (coverage?.examination?.refused_phase !== undefined) return record;
|
|
for (const child of Object.values(record)) {
|
|
const found = refusedCoverage(child);
|
|
if (found !== undefined) return found;
|
|
}
|
|
return undefined;
|
|
};
|
|
|
|
it("applies the selected member-mutation allowance and reports it on refusal", async () => {
|
|
const app = await createApplication();
|
|
const admitted = await analyzeJavaScriptApplication({ input_path: app });
|
|
if (!admitted.ok) throw admitted.error;
|
|
expect(sourceFailure(admitted.value)).toBeUndefined();
|
|
const cases = [
|
|
{
|
|
options: {
|
|
environment: { REA_JAVASCRIPT_MEMBER_MUTATION_ALIAS_STATES: "8" },
|
|
},
|
|
source: "setting",
|
|
},
|
|
{
|
|
// The accepted runtime override wins over the launch seed.
|
|
options: {
|
|
environment: { REA_JAVASCRIPT_MEMBER_MUTATION_ALIAS_STATES: "16" },
|
|
settings: () => ({
|
|
revision: 1,
|
|
overrides: { "javascript.member_mutation_alias_states": 8 },
|
|
}),
|
|
},
|
|
source: "runtime",
|
|
},
|
|
] as const;
|
|
for (const { options, source } of cases) {
|
|
const refused = await analyzeJavaScriptApplication(
|
|
{ input_path: app },
|
|
options,
|
|
);
|
|
if (!refused.ok) throw refused.error;
|
|
// Structured limit facts, and the recovery the owning formatter gives.
|
|
const owner = javaScriptMemberMutationRefusal(
|
|
"analyze_javascript_application",
|
|
{ aliasStates: 8, source },
|
|
);
|
|
expect(sourceFailure(refused.value), source).toMatchObject({
|
|
code: "resource_constraint",
|
|
retryable: false,
|
|
details: {
|
|
limit: {
|
|
setting: "javascript.member_mutation_alias_states",
|
|
effective_value: 8,
|
|
source,
|
|
unit: "count",
|
|
minimum: 1,
|
|
maximum: MAXIMUM_JAVASCRIPT_MEMBER_MUTATION_ALIAS_STATES,
|
|
update_mode: "runtime",
|
|
},
|
|
},
|
|
remediation: { action: owner.remediationAction },
|
|
});
|
|
// The refused source keeps its syntax-level facts, marked with the phase.
|
|
expect(refusedCoverage(refused.value), source).toMatchObject({
|
|
path: "main.js",
|
|
parse_status: "partial",
|
|
analysis_coverage: {
|
|
syntax: { status: "complete", error_count: 0 },
|
|
examination: {
|
|
status: "not-examined",
|
|
refused_phase: "member-mutations",
|
|
},
|
|
},
|
|
});
|
|
expect(
|
|
(
|
|
refused.value.normalized_result as {
|
|
readonly statistics: { readonly parsed_javascript_files: number };
|
|
}
|
|
).statistics.parsed_javascript_files,
|
|
).toBe(1);
|
|
}
|
|
const raised = await analyzeJavaScriptApplication(
|
|
{ input_path: app },
|
|
{ environment: { REA_JAVASCRIPT_MEMBER_MUTATION_ALIAS_STATES: "4096" } },
|
|
);
|
|
if (!raised.ok) throw raised.error;
|
|
expect(sourceFailure(raised.value)).toBeUndefined();
|
|
expect(refusedCoverage(raised.value)).toBeUndefined();
|
|
});
|
|
|
|
it("refuses a malformed launch seed before analysis", async () => {
|
|
const app = await createApplication();
|
|
const refused = await analyzeJavaScriptApplication(
|
|
{ input_path: app },
|
|
{ environment: { REA_JAVASCRIPT_MEMBER_MUTATION_ALIAS_STATES: "0" } },
|
|
);
|
|
if (refused.ok) throw new Error("A malformed seed was admitted");
|
|
expect(refused.error._tag).toBe("ConfigurationError");
|
|
});
|