94 lines
2.8 KiB
JavaScript
94 lines
2.8 KiB
JavaScript
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
|
|
import {
|
|
scanOptionalRecipeSources,
|
|
validateRecipeCatalog,
|
|
} from "./lib/optional-recipes.mjs";
|
|
|
|
const catalog = JSON.parse(
|
|
await readFile("config/recipes/frontend-capability-recipes.json", "utf8"),
|
|
);
|
|
const packageDocument = JSON.parse(await readFile("package.json", "utf8"));
|
|
|
|
const cleanupCatalog = structuredClone(catalog);
|
|
cleanupCatalog.recipes.find(
|
|
/** @param {{id: string}} recipe */ (recipe) => recipe.id === "realtime",
|
|
).lifecycleMethods = [];
|
|
|
|
const dependencyCatalog = structuredClone(catalog);
|
|
dependencyCatalog.productionRuntimeDependencies = ["zustand"];
|
|
|
|
const workflowCatalog = structuredClone(catalog);
|
|
workflowCatalog.recipes.find(
|
|
/** @param {{id: string}} recipe */ (recipe) => recipe.id === "client-workflow",
|
|
).serverStatePolicy = "copied-server-state";
|
|
|
|
const sourceViolations = await scanOptionalRecipeSources(
|
|
"tests/fixtures/optional-recipes/forbidden",
|
|
{ scanProductionBoundary: false },
|
|
);
|
|
const productionViolations = await scanOptionalRecipeSources(
|
|
"tests/fixtures/optional-recipes/forbidden/production-import",
|
|
{ scanProductionBoundary: true },
|
|
);
|
|
sourceViolations.push(...productionViolations);
|
|
const ruleIds = new Set(sourceViolations.map(({ ruleId }) => ruleId));
|
|
const results = [
|
|
{
|
|
id: "cleanup-omission",
|
|
passed: validateRecipeCatalog(cleanupCatalog, packageDocument).some(
|
|
(violation) => violation === "realtime:CLEANUP_CONTRACT_MISSING",
|
|
),
|
|
},
|
|
{
|
|
id: "unselected-runtime-dependency",
|
|
passed: validateRecipeCatalog(dependencyCatalog, packageDocument).includes(
|
|
"UNSELECTED_RUNTIME_DEPENDENCY",
|
|
),
|
|
},
|
|
{
|
|
id: "server-state-policy",
|
|
passed: validateRecipeCatalog(workflowCatalog, packageDocument).includes(
|
|
"client-workflow:SERVER_STATE_DUPLICATION_POLICY",
|
|
),
|
|
},
|
|
{
|
|
id: "vendor-direct-import",
|
|
passed: ruleIds.has("VENDOR_IMPORT_OUTSIDE_ADAPTER"),
|
|
},
|
|
{
|
|
id: "credential-leak",
|
|
passed: ruleIds.has("CREDENTIAL_LEAK_PATH"),
|
|
},
|
|
{
|
|
id: "server-state-source-duplication",
|
|
passed: ruleIds.has("CLIENT_STORE_DUPLICATES_SERVER_STATE"),
|
|
},
|
|
{
|
|
id: "production-imports-recipe",
|
|
passed: ruleIds.has("PRODUCTION_IMPORTS_RECIPE"),
|
|
},
|
|
];
|
|
const report = {
|
|
schemaVersion: 1,
|
|
results,
|
|
passed: results.every(({ passed }) => passed),
|
|
};
|
|
await mkdir("artifacts/quality", { recursive: true });
|
|
await writeFile(
|
|
"artifacts/quality/optional-recipe-fixtures.json",
|
|
`${JSON.stringify(report, null, 2)}\n`,
|
|
);
|
|
if (!report.passed) {
|
|
process.stderr.write(
|
|
`Optional recipe negative fixtures failed: ${results
|
|
.filter(({ passed }) => !passed)
|
|
.map(({ id }) => id)
|
|
.join(", ")}\n`,
|
|
);
|
|
process.exit(1);
|
|
}
|
|
process.stdout.write(
|
|
`Optional recipe negative fixtures: PASS (${results.length} forbidden cases rejected)\n`,
|
|
);
|