@xemahq/declarative-reconciler
v0.1.0
Published
This package belongs to **Layer 1** — a pure, framework-agnostic runtime SDK that depends only on Node built-ins (no `@xemahq/*`, no NestJS, no zod).
Readme
@xemahq/declarative-reconciler
This package belongs to Layer 1 — a pure, framework-agnostic runtime SDK
that depends only on Node built-ins (no @xemahq/*, no NestJS, no zod).
It is the generic engine behind "Xema-as-Code": it converges an actual set
toward a desired set by a stable natural key and a content hash, with a
retire pass for owned items that are no longer desired. It generalizes the
upsert-by-hash + retire-removed logic previously hand-written in each boot
seeder (e.g. agent-contribution.service.ts).
Usage
Implement a ReconcilerPort for your domain and drive it:
import { reconcile, stableHash, type ReconcilerPort } from '@xemahq/declarative-reconciler';
const port: ReconcilerPort<DesiredPortal, ActualPortal> = {
computeDesired: () => derivePortalsFromInstalledBiomes(orgId),
readActual: () => listPortals({ orgId, managedBy: 'seeder' }), // own rows only
hashOf: (d) => stableHash(d.spec),
appliedHashOf: (a) => a.appliedHash,
create: (d) => createPortal(d),
update: (d, a) => updatePortal(a.physicalId, d),
retire: (a) => archivePortal(a.physicalId),
};
const result = await reconcile(port); // { created, updated, retired, unchanged, entries }planReconcile(port)returns the diff with no side effects — use it forxema plan, dry-runs, and the frontend drift view.reconcile(port)plans then applies, fail-fast (any port error propagates).
Ownership scoping (only reconcile rows you own) lives inside readActual, so
the retire pass can never delete UI- or IaC-owned rows.
