everything-dev
Version:
A consolidated product package for building Module Federation apps with oRPC APIs.
112 lines (110 loc) • 4.8 kB
JavaScript
import { GateBlocked } from "./domain.mjs";
import { audit } from "./audit.mjs";
import { migrateRouteMounts } from "./steps/migrate-route-mounts.mjs";
import { apply } from "./apply.mjs";
import { AUTH_FIXTURE_PATH, composeFindings, probeComposeSeam, readEveryPluginVersion } from "./checks/compose-seam.mjs";
import { migrateI18nSeam } from "./steps/migrate-i18n-seam.mjs";
import { patchDbTopology } from "./steps/patch-db-topology.mjs";
import { portRouteGuards } from "./steps/port-route-guards.mjs";
import { existsSync } from "node:fs";
import { join } from "node:path";
import { Effect } from "effect";
import { mkdir, writeFile } from "node:fs/promises";
//#region src/services/scaffold/upgrade-audit/index.ts
/**
* v1 → v2 upgrade train (NEARBuilders/everything-dev#455).
*
* Audits an everything-dev v1 child app for upgrade readiness (hardline
* mount-conformance gate, config surface, legacy imports) and applies the
* deterministic v2 transforms (authored descriptor, bun→pnpm catalog
* migration, consumerized refs, import rewrites, obsolete-file deletion,
* plugin dep declarations). State-aware: a tree with an authored
* `bos.app.ts` is already on the v2 train — the audit reads
* `bos.config.json` and is for pre-upgrade trees only.
*/
const toPlainFinding = (finding) => ({
check: finding.check,
path: finding.path,
severity: finding.severity,
message: finding.message
});
const isV2Tree = (projectDir) => existsSync(join(projectDir, "bos.app.ts"));
const writeManifestFile = async (projectDir, manifest) => {
await mkdir(join(projectDir, ".bos"), { recursive: true });
await writeFile(join(projectDir, ".bos/v2-upgrade-manifest.json"), `${JSON.stringify(manifest, null, 2)}\n`);
};
/** Run the v1 audit: gate findings, manifest emit, no tree writes. */
const auditV1Tree = async (projectDir) => {
const report = await Effect.runPromise(audit(projectDir));
const findings = report.findings.map(toPlainFinding);
await writeManifestFile(projectDir, report.manifest);
return {
tier: report.tier,
gateBlocked: report.manifest.blockerCount > 0,
findings,
manifest: report.manifest
};
};
/** Apply the v2 transforms; the hardline gate must pass first. */
const applyV1Tree = async (projectDir, dryRun) => {
try {
const logs = await Effect.runPromise(apply(projectDir, dryRun));
const migrated = [];
const notes = [];
for (const log of logs) {
for (const path of log.result.writes) migrated.push(`${log.name}: ${path}`);
for (const path of log.result.deletes) migrated.push(`${log.name}: deleted ${path}`);
for (const note of log.result.notes) notes.push(`${log.name}: ${note}`);
}
return {
ok: true,
outcome: {
migrated,
notes
}
};
} catch (error) {
if (error instanceof GateBlocked) return {
ok: false,
blockers: error.findings.map(toPlainFinding)
};
throw error;
}
};
/** Re-run a post-upgrade maintenance step (regression guard after `bos sync`). */
const maintainV1Tree = async (projectDir, step, dryRun) => {
switch (step) {
case "route-mounts": return await Effect.runPromise(migrateRouteMounts(projectDir, dryRun));
case "port-route-guards": return await Effect.runPromise(portRouteGuards(projectDir, dryRun));
case "db-topology": return await Effect.runPromise(patchDbTopology(projectDir, dryRun));
case "i18n-seam": return await Effect.runPromise(migrateI18nSeam(projectDir, dryRun));
case "compose-seam": {
const probe = await Effect.runPromise(probeComposeSeam(projectDir, { live: false }));
const version = await Effect.runPromise(readEveryPluginVersion(projectDir));
const findings = composeFindings(probe, version);
return {
writes: [],
deletes: [],
notes: [
`every-plugin ${version}, auth manifest: ${AUTH_FIXTURE_PATH}`,
`seam: ${describeComposeProbe(probe)}`,
...probe.composeLog,
...probe.error ? [`error: ${probe.error}`] : [],
...findings.map((finding) => `[${finding.severity}] ${finding.message}`)
]
};
}
}
};
const describeComposeProbe = (probe) => {
switch (probe.seam) {
case "ok": return probe.composeLog.some((line) => line.includes("core ui wins")) ? "compose ok — core ui wins; shadowed auth routes skipped cleanly" : "compose ok — note: no /login shadow exercised (core ui no longer claims /login)";
case "absent-ui-manifest": return "ui/src/manifest.gen.json absent — run `bos types gen` first";
case "cascade-bug": return "KNOWN BUG — false \"unresolvable parentage\" for the /login shadow cascade";
case "path-collision": return "REGRESSION — the /login shadow is treated as a hard path collision";
case "compose-failed": return "compose failed";
}
};
//#endregion
export { applyV1Tree, auditV1Tree, describeComposeProbe, isV2Tree, maintainV1Tree };
//# sourceMappingURL=index.mjs.map