UNPKG

@lunora/cli

Version:

The Lunora CLI: init, dev, deploy, codegen, run, reset, and migrate commands

80 lines (77 loc) 3.22 kB
import { existsSync, readFileSync, writeFileSync } from 'node:fs'; import { evaluateSchemaDrift, parseSchemaSnapshot, SchemaSnapshotParseError, serializeSchemaSnapshot } from '@lunora/codegen'; const readBaseline = (snapshotPath) => { if (!existsSync(snapshotPath)) { return { status: "absent" }; } let content; try { content = readFileSync(snapshotPath, "utf8"); } catch { return { status: "corrupt" }; } try { const snapshot = parseSchemaSnapshot(content); return snapshot === void 0 ? { status: "corrupt" } : { snapshot, status: "ok" }; } catch (error) { if (error instanceof SchemaSnapshotParseError) { return { status: "corrupt" }; } throw error; } }; const writeBaseline = (snapshotPath, snapshot) => { writeFileSync(snapshotPath, serializeSchemaSnapshot(snapshot), "utf8"); }; const corruptBaselineResult = (context) => { const reason = `schema-drift gate: the committed baseline ${context.snapshotPath} is unreadable or malformed, so schema drift cannot be checked. Fix it (e.g. resolve a merge conflict in lunora/.lunora-schema.json), or pass --update-schema-baseline to regenerate it from the current schema.`; if (context.updateBaseline && !context.readOnly) { context.logger.warn( `schema baseline was unreadable; regenerating from the current schema on success (--update-schema-baseline): ${context.snapshotPath}` ); return { blocked: false, changes: [], reason, rebless: context.rebless }; } if (!context.readOnly) { context.logger.error(reason); } return { blocked: true, changes: [], reason }; }; const blockedDecisionResult = (decision, context) => { if (!context.readOnly) { context.logger.error(decision.reason); } if (context.updateBaseline && !context.readOnly) { context.logger.warn(`schema baseline will be re-blessed despite breaking drift on success (--update-schema-baseline): ${context.snapshotPath}`); return { blocked: false, changes: decision.changes, reason: decision.reason, rebless: context.rebless }; } return { blocked: true, changes: decision.changes, reason: decision.reason }; }; const runSchemaDriftGate = (options) => { const { allowDrift, codegen, logger, readOnly = false, updateBaseline = false } = options; const snapshotPath = codegen.schemaSnapshotPath; const baseline = readBaseline(snapshotPath); const context = { logger, readOnly, rebless: () => { writeBaseline(snapshotPath, codegen.schemaSnapshot); }, snapshotPath, updateBaseline }; if (baseline.status === "corrupt") { return corruptBaselineResult(context); } const decision = evaluateSchemaDrift({ allowDrift, baseline: baseline.status === "ok" ? baseline.snapshot : void 0, current: codegen.schemaSnapshot }); if (decision.blocked) { return blockedDecisionResult(decision, context); } if (decision.changes.length > 0) { if (!readOnly) { logger.info(decision.reason); } return { blocked: false, changes: decision.changes, reason: decision.reason, rebless: readOnly ? void 0 : context.rebless }; } return { blocked: false, changes: decision.changes, reason: decision.reason }; }; export { runSchemaDriftGate as r };