Reconciler
Use deploy and destroy when a Node.js application needs durable resource lifecycle operations without starting the Notation CLI. Notation owns reconciliation intent, graph ordering, provider calls, and resource state; the application owns the outer Yieldstar workflow and runtime.
import { SqliteSchedulerClient, SqliteStoreClient, SqliteTaskQueueClient, SqliteTimersClient, createSqliteDb } from "@yieldstar/sqlite-runtime/node";
import { DurableStateBackend, deploy as deployResources, destroy as destroyResources } from "@notation/reconciler/durable";
import { workflow } from "yieldstar";
const database = createSqliteDb({ path: ".notation/workflows.db" });
const schedulerClient = new SqliteSchedulerClient({
taskQueueClient: new SqliteTaskQueueClient(database),
timersClient: new SqliteTimersClient(database),
});
const storeClient = new SqliteStoreClient({ db: database, schedulerClient });
const state = new DurableStateBackend(storeClient, "my-application");
export const deploy = workflow(async function* (step, event) {
yield* deployResources(step, {
executionId: event.executionId,
resources,
state,
});
});
export const destroy = workflow(async function* (step, event) {
yield* destroyResources(step, {
executionId: event.executionId,
resources,
state,
});
});The outer workflow supplies durable step execution, timers, shared stores, waiting, and scheduling. Checkpointed provider results are replayed from the heap after a crash, retryable provider conditions suspend on a durable timer, and conditional state writes use Yieldstar store identity and version. Provider operations and event consumers are bound by the crash-window contract stated in the reconciler internals.
Each live resource is one Yieldstar store. Yieldstar's UUIDv7 store instanceId and version are authoritative for conditional update and delete; Notation exposes the version unchanged as the resource state's version.
Operations against the same deployment — the deploymentId the DurableStateBackend is constructed with — are serialized through a deployment hold naming the holding executionId. Resume a crashed operation with the same execution ID; use a new globally unique execution ID for every new deploy or destroy. An execution that must wait emits a reconciler.hold.waiting event naming the holder before it suspends, which also identifies a crashed holder that should be resumed instead. If the holder is genuinely abandoned, clear its hold with clearDeploymentHold.
Pass the complete desired set on every deployment. Persisted resources absent from that set are deleted through the supplied resource registry. Destroy removes current resources in reverse dependency order and then removes any persisted orphans whose resource type is registered.
The runnable Node SQLite composition is in examples/reconciler.