@stateledger/core
Public surface of the core package.
defineMachine(config)
Factory that returns a MachineFactory. The config is validated at runtime and its states and transitions are narrowed at the type level.
typescript
function defineMachine<C extends MachineConfig>(config: C): MachineFactory<C>;
type MachineConfig = {
readonly name: string;
readonly states: readonly string[];
readonly initialState: string;
readonly transitions: readonly { readonly from: string; readonly to: string }[];
readonly version?: number;
readonly guards?: Record<string, GuardFn>;
readonly callbacks?: Record<string, CallbackFn>;
};
type MachineFactory<C> = {
config: C;
for<TxHandle, Subject>(
subjectId: string,
opts: BindOptions<TxHandle, Subject>,
): Machine<C, TxHandle, Subject>;
};factory.for(subjectId, opts)
Binds a machine definition to a specific subject. Returns a live Machine instance.
typescript
type BindOptions<TxHandle, Subject> = {
adapter: Adapter<TxHandle>;
tx?: TxHandle; // Join an outer transaction (optional)
actor?: Actor | string; // Default actor recorded on every transition
subject?: Subject; // Full business subject exposed to guards/callbacks
};Machine methods
- transitionTo(target, opts?): Promise<TransitionRow>Attempt the transition. Throws
InvalidTransition,GuardRejected, orOptimisticConcurrencyError. - in(from).transitionTo(target)Type-only sugar. Narrows autocomplete to states reachable from
from. Runtime behaviour is identical totransitionTo(target). - readCurrent(): Promise<TransitionRow | null>Latest transition row for this subject. Null if the subject has never transitioned.
- readHistory(): Promise<TransitionRow[]>All transitions for this subject, oldest first. Includes state, actor, timestamp, metadata for each hop.
- stateAt(at: Date): Promise<State | null>Point-in-time query. Returns the state as of the given timestamp, or null if the subject didn’t exist yet.
TransitionOptions
typescript
type TransitionOptions = {
metadata?: Record<string, unknown>; // Arbitrary JSON stored on the row
actor?: Actor | string; // Override the bind-time actor
};Types you'll import
typescript
type Actor = { id: string; type: string } | string;
type TransitionRow<State = string> = {
id: string;
machine: string;
subjectId: string;
fromState: State | null;
toState: State;
actor: string | null;
metadata: Record<string, unknown> | null;
transitionedAt: Date;
createdAt: Date;
};
type GuardContext<Subject> = {
subject: Subject;
from: string;
to: string;
actor: string | null;
metadata: Record<string, unknown>;
};
type CallbackContext<TxHandle, Subject> = GuardContext<Subject> & {
tx: TxHandle;
subjectId: string;
};
type InferMachineState<F> = /* union of the machine's state literals */;Errors
All errors extend StateledgerError. See the errors reference for exact field shapes and handling patterns.
- •
InvalidTransition - •
GuardRejected - •
OptimisticConcurrencyError - •
StaleSubjectError - •
AdapterError
@stateledger/prisma
typescript
function createPrismaAdapter(
client: PrismaClient | Prisma.TransactionClient,
options?: {
tableName?: string; // default "state_transitions"
locking?: "pessimistic" | "optimistic"; // default "pessimistic"
},
): Adapter<PrismaRawCalls>;
// Shipped schema SQL you can run at setup or paste into a migration:
const STATELEDGER_SCHEMA_SQL: string;
const STATELEDGER_SCHEMA_STATEMENTS: string[];@stateledger/drizzle
typescript
function createDrizzleAdapter(
db: DrizzleClient | DrizzleTx,
options?: { tableName?: string; locking?: "pessimistic" | "optimistic" },
): Adapter<DrizzleTx>;
const STATELEDGER_SCHEMA_SQL: string;@stateledger/memory
typescript
class InMemoryAdapter implements Adapter<InMemoryTx>;
// Use: const adapter = new InMemoryAdapter();@stateledger/outbox
typescript
// Enqueue
function enqueue<Payload extends Record<string, unknown>>(
tx: OutboxTx,
input: EnqueueInput<Payload>,
): Promise<OutboxRecord<Payload>>;
function enqueueBatch<Payload extends Record<string, unknown>>(
tx: OutboxTx,
inputs: readonly EnqueueInput<Payload>[],
): Promise<OutboxRecord<Payload>[]>;
// Worker
function createWorker(config: WorkerConfig): WorkerHandle;
function defaultBackoffMs(attempt: number): number;
// Schema
const OUTBOX_SCHEMA_STATEMENTS: readonly string[];
const OUTBOX_SCHEMA_SQL: string;
// Errors
class OutboxError extends Error {}
class OutboxAdapterError extends OutboxError {}
class UnknownKindError extends OutboxError {
readonly kind: string;
readonly recordId: string;
}
// Structural tx handle | any Prisma/Drizzle/pg client satisfies this
type OutboxTx = {
$queryRawUnsafe<T = unknown>(query: string, ...values: any[]): Promise<T>;
$executeRawUnsafe(query: string, ...values: any[]): Promise<unknown>;
};