trellis
Version:
Agentic State Engine — event-sourced causal graph with branching, decision traces, and realtime sync for AI-native applications
150 lines (148 loc) • 4.63 kB
JavaScript
import {
compareVersions,
readLockfile
} from "./chunk-QQF4WF7W.js";
import "./chunk-2ESYSVXG.js";
// src/registry/migrate.ts
function findFieldsByName(current, name) {
return current.filter((f) => f.name === name);
}
function compareSchemas(current, pinned, newFields) {
const diff = {
schemaId: current["@id"],
oldVersion: current.version,
newVersion: pinned.version,
addedFields: [],
removedFields: [],
changedFields: [],
compatible: true
};
if (current.version === pinned.version) {
return diff;
}
if (compareVersions(current.version, pinned.version) > 0) {
diff.compatible = false;
diff.reason = `Current version (${current.version}) is newer than pinned (${pinned.version}) \u2014 downgrade not supported`;
return diff;
}
if (newFields && newFields.length > 0) {
const currentNames = new Set(current.fields.map((f) => f.name));
for (const pf of newFields) {
const existing = findFieldsByName(current.fields, pf.name);
if (existing.length === 0) {
diff.addedFields.push(pf);
} else {
const match = existing[0];
if (match.valueType !== pf.valueType) {
diff.compatible = false;
diff.reason = `Field ${pf.name} type changed from ${match.valueType} to ${pf.valueType}`;
diff.changedFields.push({ name: pf.name, from: String(match.valueType), to: String(pf.valueType) });
} else if (pf.required && !match.required) {
diff.changedFields.push({ name: pf.name, from: "optional", to: "required" });
}
}
}
const newNames = new Set(newFields.map((f) => f.name));
for (const cf of current.fields) {
if (!newNames.has(cf.name)) {
diff.removedFields.push(cf);
if (diff.compatible) {
diff.compatible = false;
diff.reason = `Field ${cf.name} removed in new version`;
}
}
}
}
return diff;
}
function applyMigration(kernel, diff, newFields) {
const current = kernel.getOntology(diff.schemaId);
if (!current) {
throw new Error(`Schema ${diff.schemaId} not found in graph`);
}
let mergedFields = [...current.fields];
if (newFields && diff.addedFields.length > 0) {
const existingNames = new Set(current.fields.map((f) => f.name));
for (const f of diff.addedFields) {
if (!existingNames.has(f.name)) {
mergedFields.push(f);
}
}
}
kernel.updateOntology(diff.schemaId, {
version: diff.newVersion,
fields: mergedFields
});
const migrationFact = {
e: diff.schemaId,
a: "migration",
v: JSON.stringify({
from: diff.oldVersion,
to: diff.newVersion,
at: (/* @__PURE__ */ new Date()).toISOString(),
fieldsAdded: diff.addedFields.map((f) => f.name),
changes: diff.changedFields.length > 0 ? diff.changedFields : void 0
})
};
kernel.store?.addFacts?.([migrationFact]);
}
function migrateLockfileSchemas(kernel, lockfile, client, scopeFilter, dryRun) {
const report = {
migrated: [],
skipped: [],
incompatible: [],
errors: []
};
for (const [pkgName, pkgEntry] of Object.entries(lockfile.resolved)) {
if (scopeFilter && !pkgName.includes(scopeFilter)) {
continue;
}
for (const [schemaId, locked] of Object.entries(pkgEntry.schemas)) {
try {
const current = kernel.getOntology(schemaId);
if (!current) {
report.errors.push(`${schemaId}: not found in graph \u2014 skipping`);
continue;
}
const diff = compareSchemas(current, locked);
if (!diff.compatible) {
report.incompatible.push(schemaId);
continue;
}
if (diff.oldVersion === diff.newVersion) {
report.skipped.push(schemaId);
continue;
}
if (!dryRun) {
if (diff.addedFields.length > 0 || diff.changedFields.length > 0) {
applyMigration(kernel, diff, diff.addedFields);
} else {
applyMigration(kernel, diff);
}
}
report.migrated.push(schemaId);
} catch (err) {
report.errors.push(`${schemaId}: ${err.message}`);
}
}
}
return report;
}
function createMigrateHandler(rootPath, kernel, client, scope, dryRun) {
const lockfile = readLockfile(rootPath);
if (!lockfile) {
return {
migrated: [],
skipped: [],
incompatible: [],
errors: ["No lockfile found"]
};
}
return migrateLockfileSchemas(kernel, lockfile, client, scope, dryRun);
}
export {
applyMigration,
compareSchemas,
createMigrateHandler,
migrateLockfileSchemas
};