trellis
Version:
Agentic State Engine — event-sourced causal graph with branching, decision traces, and realtime sync for AI-native applications
66 lines (64 loc) • 1.82 kB
JavaScript
// src/core/ontology/sync-policy.ts
var RealtimeFieldError = class extends Error {
field;
status = 400;
constructor(field) {
super(
`Field "${field}" has sync:realtime \u2014 use trellis/realtime, not durable mutate`
);
this.name = "RealtimeFieldError";
this.field = field;
}
};
var EntityConflictError = class extends Error {
id;
status = 409;
constructor(id) {
super(`Entity already exists: ${id}`);
this.name = "EntityConflictError";
this.id = id;
}
};
function effectiveFieldSync(field) {
if (field.sync) return field.sync;
if (field.valueType === "formula" || field.valueType === "rollup" || field.computed === true) {
return "derived";
}
return "durable";
}
function findSchemaForType(ontologies, type) {
const list = Array.from(ontologies);
const byId = list.find((s) => s["@id"] === type);
if (byId) return byId;
const trellisId = `trellis:${type}`;
const byTrellis = list.find((s) => s["@id"] === trellisId);
if (byTrellis) return byTrellis;
const byLabel = list.find(
(s) => (s.tier ?? "user") !== "core" && (s.label === type || s["@id"].endsWith(`:${type}`))
);
return byLabel;
}
function filterDurableAttributes(attributes, schema) {
if (!schema) return attributes;
const byName = new Map(schema.fields.map((f) => [f.name, f]));
const out = {};
for (const [key, value] of Object.entries(attributes)) {
const field = byName.get(key);
if (!field) {
out[key] = value;
continue;
}
const tier = effectiveFieldSync(field);
if (tier === "derived") continue;
if (tier === "realtime") throw new RealtimeFieldError(key);
out[key] = value;
}
return out;
}
export {
RealtimeFieldError,
EntityConflictError,
effectiveFieldSync,
findSchemaForType,
filterDurableAttributes
};