Skip to main content

API Reference

7 min read

Core API Reference

Core functions and types for Directive.


createModule

Create a module definition.

// Define a module's schema, constraints, resolvers, and effects
function createModule<M extends ModuleSchema>(
  name: string,       // unique identifier for this module
  config: ModuleConfig<M>
): ModuleDef<M>

createModule Parameters

ParameterTypeDescription
namestringUnique module identifier
configModuleConfigModule configuration

Config Options

PropertyTypeDescription
schemaSchemaType definitions for facts, events, requirements
init(facts) => voidInitialize facts with default values
deriveRecord<string, Derivation>Computed values
effectsRecord<string, Effect>Side effect handlers
constraintsRecord<string, Constraint>Declarative rules
resolversRecord<string, Resolver>Requirement handlers

createModuleFactory

Create a factory that produces named module instances from a single definition.

function createModuleFactory<M extends ModuleSchema>(
  config: ModuleConfig<M>
): (name: string) => ModuleDef<M>

createModuleFactory Parameters

ParameterTypeDescription
configModuleConfigModule configuration (same as createModule, without name)

Returns

A function (name: string) => ModuleDef<M> that creates named instances.

Example

const chatRoom = createModuleFactory({
  schema: { facts: { messages: t.array<string>() } },
  init: (facts) => { facts.messages = []; },
});

const system = createSystem({
  modules: {
    lobby: chatRoom("lobby"),
    support: chatRoom("support"),
  },
});

createSystem

Create a runtime system.

// Create a live runtime from a module definition
function createSystem<M extends ModuleSchema>(
  config: SystemConfig<M>
): SingleModuleSystem<M>

createSystem Parameters

PropertyTypeDescription
moduleModuleModule definition
pluginsPlugin[]Optional plugins
debugDebugConfigDebug options
errorBoundaryErrorBoundaryConfigError handling configuration
initialFactsRecord<string, unknown>Initial fact values for hydration

System Properties

facts

Proxy object for reading and writing facts.

// Write a fact (triggers constraint evaluation and effects)
system.facts.count = 10;

// Read a fact (fully typed from your schema)
const count = system.facts.count;

derive

Read-only proxy for derivations.

// Derivations recompute automatically when their tracked facts change
const total = system.derive.cartTotal;

events

Accessor for the system's event definitions.

constraints

Runtime control for constraints.

// Temporarily suppress a constraint (e.g., during maintenance)
system.constraints.disable("myConstraint");

// Re-enable it to resume normal evaluation
system.constraints.enable("myConstraint");

effects

Runtime control for effects.

system.effects.disable("myEffect");     // pause a side effect
system.effects.enable("myEffect");      // resume it
system.effects.isEnabled("myEffect");   // check current state

debug

Access the time-travel API (or null if not enabled).

// Time-travel is only available when debug.timeTravel is enabled
if (system.debug) {
  system.debug.goBack();               // undo last state change
  system.debug.goForward();            // redo
  console.log(system.debug.snapshots); // all captured snapshots
}

isRunning / isSettled / isInitialized / isReady

Boolean status properties for the system lifecycle.

system.isRunning;      // true after start() is called
system.isSettled;      // true when all constraints and resolvers finish
system.isInitialized;  // true after module init() completes
system.isReady;        // true after the first reconciliation pass

System Methods

start

Start the system (runs module init and first reconciliation).

system.start();

stop

Stop the system (pauses reconciliation).

system.stop();

destroy

Clean up system resources.

system.destroy();

registerModule

Register a module dynamically on a running namespaced system.

system.registerModule(name: string, module: ModuleDef): void

The module is immediately wired into the constraint, resolver, effect, and derivation graphs. Throws if called during reconciliation or on a destroyed system.

dispatch

Dispatch an event.

// Send a typed event into the system for constraints and effects to react to
system.dispatch({ type: "EVENT_NAME", payload: data });

batch

Batch multiple fact mutations into a single reconciliation cycle.

// Group multiple writes so constraints evaluate once (not per write)
system.batch(() => {
  system.facts.a = 1;
  system.facts.b = 2;
});

read

Read a derivation value by ID.

// Read a specific derivation by its string ID
const total = system.read("cartTotal");

subscribe

Subscribe to fact or derivation changes. Accepts any combination of fact and derivation keys (auto-detected). Returns an unsubscribe function.

// Subscribe to fact changes
system.subscribe(["count"], () => console.log("count changed"));

// Subscribe to derivation changes
system.subscribe(["doubled"], () => console.log("doubled changed"));

// Subscribe to mix of facts and derivations
system.subscribe(["count", "doubled"], () => console.log("something changed"));

Signature:

subscribe(ids: Array<ObservableKeys<M>>, listener: () => void): () => void

watch

Watch a fact or derivation and receive old/new values. Accepts both fact and derivation keys (auto-detected). Returns an unsubscribe function.

Has three typed overloads: derivation-specific, fact-specific, and a generic fallback.

// Watch a fact
system.watch("count", (newVal, oldVal) => {
  console.log(`count: ${oldVal} -> ${newVal}`);
});

// Watch a derivation
system.watch("doubled", (newVal, oldVal) => {
  console.log(`doubled: ${oldVal} -> ${newVal}`);
});

// With custom equality
system.watch("config", callback, {
  equalityFn: (a, b) => JSON.stringify(a) === JSON.stringify(b),
});

Options:

PropertyTypeDescription
equalityFn(a: T, b: T) => booleanCustom equality function to control when the callback fires. Defaults to ===.

when

Wait for a predicate to become true. Returns a promise that resolves when the condition is met. Useful for awaiting state transitions.

// Wait for a condition to become true
await system.when((facts) => facts.phase === "ready");

// With timeout
await system.when(
  (facts) => facts.count > 10,
  { timeout: 5000 }
);

Signature:

when(predicate: (facts: Facts<M>) => boolean, options?: { timeout?: number }): Promise<void>

Options:

PropertyTypeDescription
timeoutnumberMaximum time in ms to wait before rejecting. If omitted, waits indefinitely.

whenReady

Wait for the system to be fully ready (after first reconciliation).

// Block until init + first reconciliation completes (useful in tests/SSR)
await system.whenReady();

settle

Wait for all constraints and resolvers to complete.

// Wait for all in-flight constraints and resolvers to finish
await system.settle();

inspect

Get current system state for debugging.

// See what's pending, running, and resolved at this moment
const inspection = system.inspect();
// { unmet, inflight, constraints, resolvers }

explain

Get an explanation of why a requirement exists.

// Trace which constraint generated a requirement and why
const explanation = system.explain("requirement-id");

getSnapshot

Capture current state as a serializable snapshot.

// Serialize current state for persistence or transfer
const snapshot = system.getSnapshot();

restore

Restore from a snapshot.

// Rehydrate state from a previously captured snapshot
system.restore(snapshot);

getDistributableSnapshot

Get a distributable snapshot of computed derivations for use outside the runtime.

// Export selected derivations for use outside the runtime (e.g., edge cache)
const snapshot = system.getDistributableSnapshot({
  includeDerivations: ["effectivePlan", "canUseFeature"],
  ttlSeconds: 3600, // snapshot expires after 1 hour
});

watchDistributableSnapshot

Watch for changes to distributable snapshot derivations.

// Automatically push updated snapshots whenever derivations change
const unsubscribe = system.watchDistributableSnapshot(
  { includeDerivations: ["effectivePlan"] },
  (snapshot) => {
    // Push to Redis/edge cache
  }
);

Builders & Helpers

Type-safe utilities for defining constraints and resolvers outside of createModule(). Useful for shared libraries, reusable definitions, and composable module configurations.

constraintFactory

Create a factory that produces typed constraints for a specific schema.

function constraintFactory<S extends Schema>(): {
  create<R extends Requirement>(constraint: TypedConstraint<S, R>): TypedConstraint<S, R>
}
import { constraintFactory, t } from '@directive-run/core';

const schema = { facts: { count: t.number(), threshold: t.number() } };
const factory = constraintFactory<typeof schema>();

const maxCount = factory.create({
  when: (facts) => facts.count > facts.threshold,
  require: { type: "RESET" },
});

// Use in any module with the same schema
const module = createModule("counter", {
  schema,
  constraints: { maxCount },
});

resolverFactory

Create a factory that produces typed resolvers for a specific schema.

function resolverFactory<S extends Schema>(): {
  create<R extends Requirement>(resolver: TypedResolver<S, R>): TypedResolver<S, R>
}
import { resolverFactory } from '@directive-run/core';

const factory = resolverFactory<typeof schema>();

const fetchUser = factory.create<{ type: "FETCH_USER"; userId: string }>({
  requirement: "FETCH_USER",
  resolve: async (req, context) => {
    context.facts.user = await api.getUser(req.userId);
  },
});

typedConstraint

One-off typed constraint creator. Simpler than constraintFactory when you only need a single definition.

function typedConstraint<S extends Schema, R extends Requirement>(
  constraint: TypedConstraint<S, R>
): TypedConstraint<S, R>
import { typedConstraint } from '@directive-run/core';

const lowStock = typedConstraint<typeof schema, { type: "REORDER" }>({
  when: (facts) => facts.stock < 10,
  require: { type: "REORDER" },
  priority: 50,
});

typedResolver

One-off typed resolver creator. Simpler than resolverFactory when you only need a single definition.

function typedResolver<S extends Schema, R extends Requirement>(
  resolver: TypedResolver<S, R>
): TypedResolver<S, R>
import { typedResolver } from '@directive-run/core';

const resetResolver = typedResolver<typeof schema, { type: "RESET" }>({
  requirement: "RESET",
  resolve: async (_req, context) => {
    context.facts.count = 0;
  },
});

AI Orchestrator Builders

For AI agent orchestrators, @directive-run/ai exports additional builders: constraint() (fluent builder), when() (quick shorthand), and createOrchestratorBuilder() (full orchestrator composition). See Orchestrator and Glossary.


Next Steps

Previous
Overview
Next
Types

We care about your data. We'll never share your email.

Powered by Directive. This signup uses a Directive module with facts, derivations, constraints, and resolvers – zero useState, zero useEffect. Read how it works

Directive - Constraint-Driven State Management for TypeScript