Skip to content

@ironflow/core

Shared types, schemas, constants, and utilities for the Ironflow JavaScript SDK.

Note: You typically don’t need to install this package directly. It’s automatically included as a dependency of @ironflow/browser and @ironflow/node.

Terminal window
npm install @ironflow/core

Import type definitions for type-safe development:

import type {
// Event types
IronflowEvent,
EventFilter,
EventSourceType,
// Function types
IronflowFunction,
FunctionConfig,
FunctionContext,
FunctionHandler,
Trigger,
RetryConfig,
ConcurrencyConfig,
ExecutionMode,
// Step types
StepClient,
Duration,
ParallelOptions,
// Run types
Run,
RunInfo,
RunStatus,
ListRunsOptions,
ListRunsResult,
// Trigger & emit types
TriggerResult,
InvokeResult,
InvokeSyncOptions,
InvokeSyncResult,
EmitOptions,
EmitResult,
EmitSyncResult,
// Subscription types
Subscription,
SubscribeOptions,
SubscriptionCallbacks,
SubscriptionEvent,
SubscriptionErrorInfo,
EventMetadata,
ConnectionState,
BufferConfig,
AckHandle,
AckableSubscription,
// Consumer group types
AckMode,
BackpressureMode,
ConsumerGroupConfig,
ConsumerGroup,
ConsumerGroupStatus,
// KV types
KVBucketConfig,
KVBucketInfo,
KVEntry,
KVPutResult,
KVListKeysResult,
KVListBucketsResult,
KVWatchEvent,
KVWatchCallbacks,
KVWatchOptions,
KVWatcher,
// Config types
ConfigResponse,
ConfigEntry,
ConfigSetResult,
ConfigWatchCallbacks,
// Branded ID types
RunId,
FunctionId,
StepId,
EventId,
// Logger
Logger,
// Server types
ServerCapabilities,
} from "@ironflow/core";

Represents an event in the system.

interface IronflowEvent<T = unknown> {
id: string;
name: string;
version: number;
data: T;
timestamp: Date;
idempotencyKey?: string;
source?: string;
metadata?: Record<string, unknown>;
}

The metadata field carries user-defined key-value pairs attached at emit time (via the metadata option in client.emit() or the REST API). It is available in function handlers as event.metadata and in subscription events when includeMetadata is enabled.

Represents a workflow run.

interface Run {
id: string;
functionId: string;
eventId: string;
status: RunStatus;
attempt: number;
maxAttempts: number;
input?: unknown;
output?: unknown;
error?: {
message: string;
code?: string;
};
startedAt?: Date;
endedAt?: Date;
createdAt: Date;
updatedAt: Date;
}
type RunStatus =
| "pending"
| "running"
| "completed"
| "failed"
| "cancelled"
| "paused"
| "waiting_for_capacity"
| "waiting";

Configuration for an Ironflow function.

interface FunctionConfig {
id: string;
name?: string;
description?: string; // Human-readable description shown in the dashboard
triggers: Trigger[];
retry?: RetryConfig;
timeout?: number; // milliseconds (default: 600000)
concurrency?: ConcurrencyConfig;
debounce?: DebounceConfig; // Collapse rapid-fire events into a single invocation
mode?: ExecutionMode; // "push" | "pull"
actorKey?: string;
schema?: ZodType; // Zod schema; validated at runtime before the handler runs
secrets?: string[]; // Secret names this function requires
stepTimeout?: string; // Default timeout for step.run() calls ("30s", "5m")
recording?: boolean; // Enable audit recording
recordingProfile?: "all" | "run_lifecycle" | "steps"; // Workflow event families to capture
recordingRetention?: string; // Retention period ("7d", "30d", "90d", "forever")
cancelOn?: CancelOnConfig[]; // Auto-cancel running runs when matching events arrive (CancelOnConfig is not re-exported from the package root)
metadata?: Record<string, unknown>; // Custom metadata (e.g., service, team, owner)
}

Client for recorded execution (durable steps).

interface StepClient {
run<T>(name: string, fn: () => Promise<T>, options?: StepRunOptions): Promise<T>;
sleep(name: string, duration: Duration): Promise<void>;
sleepUntil(name: string, until: Date | string): Promise<void>;
waitForEvent<T>(name: string, filter: EventFilter): Promise<IronflowEvent<T>>;
parallel<T extends unknown[]>(
name: string,
branches: { [K in keyof T]: (step: StepClient) => Promise<T[K]> },
options?: ParallelOptions,
): Promise<T>;
map<T, R>(
name: string,
items: T[],
fn: (item: T, step: StepClient, index: number) => Promise<R>,
options?: ParallelOptions,
): Promise<R[]>;
compensate(stepName: string, fn: () => Promise<void>): void;
invoke<T = unknown>(functionId: string, input?: unknown, options?: { timeout?: string }): Promise<T>;
invokeAsync(functionId: string, input?: unknown): Promise<{ runId: string }>;
publish(topic: string, data: unknown, options?: PublishOptions): Promise<PublishResult>;
}

Logger interface used throughout the SDK.

interface Logger {
debug(message: string, data?: Record<string, unknown>): void;
info(message: string, data?: Record<string, unknown>): void;
warn(message: string, data?: Record<string, unknown>): void;
error(message: string, data?: Record<string, unknown>): void;
}

Configuration for creating a KV bucket.

interface KVBucketConfig {
name: string;
description?: string;
ttlSeconds?: number;
maxValueSize?: number;
maxBytes?: number;
history?: number;
}

A key-value entry returned by get.

interface KVEntry {
key: string;
value: unknown;
revision: number;
created_at: string;
operation: string; // "put" | "delete"
}

A change event delivered over WebSocket.

interface KVWatchEvent {
type: "kv_update";
key: string;
value: string;
revision: number;
operation: "put" | "delete";
bucket: string;
}
interface KVWatchCallbacks {
onUpdate: (event: KVWatchEvent) => void;
onError?: (error: Error) => void;
onClose?: () => void;
}

Options for emitting events.

interface EmitOptions {
version?: number; // Event schema version (default: 1)
idempotencyKey?: string;
metadata?: Record<string, unknown>;
namespace?: string; // default: "default"
}

One run’s outcome from emitSync(). emitSync() returns EmitSyncResult[]: an event can match several triggers, and every matched run is reported. An event that matches nothing returns [].

interface EmitSyncResult {
runId: string; // ID of the triggered run
functionId: string; // ID of the function that handled the event
status: RunStatus; // Final run status
output: unknown; // Function return value
error?: { message: string; code?: string };
durationMs: number; // Wall-clock time from trigger to completion
waitTimedOut: boolean; // Wait budget expired; the run is still going
}

emitSync() never throws on a run outcome — with N results there is no unambiguous choice of which failure to raise. Inspect status, error and waitTimedOut per element. Transport, protocol and validation errors still throw. See ADR 0067.

Argument and return type of the ID-keyed invoke() method, which targets one function and produces exactly one run.

interface InvokeSyncOptions<TInput = unknown> {
data: TInput;
timeout?: number; // server-side wait budget in ms (default 30000)
idempotencyKey?: string;
metadata?: Record<string, unknown>;
}
type InvokeSyncResult = Omit<EmitSyncResult, "waitTimedOut">;

Because it guarantees a single run, invoke() does throw RunFailedError, RunCancelledError and non-retryable RunWaitTimeoutError — which is why waitTimedOut is absent from the result.

timeout is a server-side wait budget, not a transport deadline. InvokeFunctionSync ties the run’s lifetime to the request context, so aborting the HTTP request cancels the run server-side.

Return type of emit(). Returned immediately after the event is accepted — the runs may still be in progress.

interface InvokeResult {
runIds: string[]; // IDs of created runs
eventId: string; // ID of the stored event
}

Note: TriggerResult is a deprecated alias for InvokeResult and will be removed in a future release.


Runtime validation schemas using Zod:

import {
// Push mode schemas
PushRequestSchema,
PushRequestEventSchema,
CompletedStepSchema,
ResumeContextSchema,
// Response schemas
RunStatusSchema,
TriggerResponseSchema,
TriggerSyncResponseSchema,
RunResponseSchema,
ListRunsResponseSchema,
HealthResponseSchema,
ErrorResponseSchema,
// Consumer group schemas
ConsumerGroupResponseSchema,
ListConsumerGroupsResponseSchema,
// Worker schemas
JobAssignmentSchema,
JobEventSchema,
// WebSocket schemas
WSServerMessageSchema,
WSEventMessageSchema,
// Validation helpers
parseAndValidate,
validate,
} from "@ironflow/core";
// Type-safe parsing with error handling
const result = RunStatusSchema.safeParse(rawStatus);
if (result.success) {
console.log(result.data);
}

Default configuration values:

import {
DEFAULT_SERVER_URL, // 'http://localhost:9123'
DEFAULT_WS_URL, // 'ws://localhost:9123/ws'
DEFAULT_PORT, // 9123
DEFAULT_HOST, // 'localhost'
DEFAULT_RECONNECT, // { ENABLED, MAX_ATTEMPTS, ... }
DEFAULT_WORKER, // { MAX_CONCURRENT_JOBS, ... }
DEFAULT_TIMEOUTS, // { CLIENT, FUNCTION, TRIGGER_SYNC, INVOKE_FUNCTION_SYNC, SYNC_TRANSPORT_HEADROOM }
DEFAULT_RETRY, // { MAX_ATTEMPTS, INITIAL_DELAY_MS, ... }
RUN_STATUS, // { PENDING (deprecated), RUNNING, COMPLETED, FAILED, CANCELLED, PAUSED, WAITING_FOR_CAPACITY, WAITING }
STEP_STATUS, // { COMPLETED, FAILED, WAITING }
ENV_VARS, // { SERVER_URL, SIGNING_KEY, API_KEY, LOG_LEVEL }
} from "@ironflow/core";
ConstantValueDescription
DEFAULT_SERVER_URL'http://localhost:9123'Default Ironflow server URL
DEFAULT_WS_URL'ws://localhost:9123/ws'Default WebSocket URL
DEFAULT_RECONNECT.ENABLEDtrueAuto-reconnect enabled by default
DEFAULT_RECONNECT.MAX_ATTEMPTS10Maximum reconnection attempts
DEFAULT_RECONNECT.INITIAL_DELAY_MS1000Initial backoff delay
DEFAULT_RECONNECT.MAX_DELAY_MS30000Maximum backoff delay
DEFAULT_WORKER.MAX_CONCURRENT_JOBS10Default concurrent job limit
DEFAULT_WORKER.HEARTBEAT_INTERVAL_MS30000Worker heartbeat interval
DEFAULT_TIMEOUTS.CLIENT30000Client request timeout
DEFAULT_TIMEOUTS.FUNCTION600000Function execution timeout

Built-in error types with error code classification:

import {
IronflowError,
ConnectionError,
SubscriptionError,
ValidationError,
SchemaValidationError,
SignatureError,
TimeoutError,
StepError,
StepTimeoutError,
FunctionNotFoundError,
RunNotFoundError,
RunWaitTimeoutError,
RunFailedError,
RunCancelledError,
NonRetryableError,
NotConfiguredError,
UnauthenticatedError,
UnauthorizedError,
EnterpriseRequiredError,
ConflictError,
ContendedError,
InjectionUnverifiedError,
InvokeError,
InvokeTimeoutError,
AgentInvokeTimeoutError,
MemoryCatchupTimeoutError,
QueueFullError,
isRetryable,
isIronflowError,
toError,
} from "@ironflow/core";
try {
await someOperation();
} catch (error) {
if (error instanceof IronflowError) {
console.log(error.code); // Error code
console.log(error.message); // Error message
console.log(error.retryable); // Whether retryable
if (isRetryable(error)) {
// Retry the operation
}
}
}
Error ClassDescription
IronflowErrorBase error class with code, retryable, details
ConnectionErrorWebSocket/HTTP connection failures (retryable)
SubscriptionErrorSubscription-related errors
ValidationErrorInvalid input or schema validation failures
SchemaValidationErrorZod schema validation failures
SignatureErrorWebhook signature verification failures
TimeoutErrorOperation timeout with timeoutMs property
StepErrorStep execution failures with stepId and stepName
StepTimeoutErrorstep.run() exceeded its timeout; has stepName and timeout (string) properties (retryable)
FunctionNotFoundErrorFunction not found with functionId property
RunNotFoundErrorRun not found with runId property
RunWaitTimeoutErrorinvoke() stopped waiting while the run remained active; has runId, functionId, runStatus, and timeoutMs properties (non-retryable). emitSync() reports the same condition as waitTimedOut on the result instead of throwing
RunFailedErrorThrown by invoke() when the targeted run fails; has runId and output properties. emitSync() reports a failure as status + error per result instead
RunCancelledErrorThrown by invoke() when the targeted run is cancelled; has runId property. emitSync() reports it as status per result instead
NonRetryableErrorPermanent failures that should not be retried
NotConfiguredErrorClient not configured (call configure() first)
UnauthenticatedErrorHTTP 401 — missing or invalid API key
UnauthorizedErrorHTTP 403 — API key lacks required permissions
EnterpriseRequiredErrorHTTP 402 — legacy; retained for wire compatibility. Ironflow ships a single build with no Enterprise/Core split (ADR 0015), so the server never returns 402.
ConflictErrorHTTP 409 that is not a lost race — e.g. a deduplicated resumeRun(). Wait, do not retry
ContendedErrorHTTP 409 from a lost compare-and-set race (Connect aborted); nothing was applied, so re-read and reissue
InjectionUnverifiedErrorConnect aborted with Ironflow-Error-Reason: injection_unverified; injectStepOutput() wrote the step but could not confirm the run stood still. Unlike ContendedError the write did land — read the step rather than reissuing
InvokeErrorstep.invoke() failed; has functionId, childRunId, and errorCause properties
InvokeTimeoutErrorstep.invoke() timed out; extends InvokeError, adds timeoutMs property
AgentInvokeTimeoutErrorAgent invoke() exceeded its wait deadline
MemoryCatchupTimeoutErrorAgent run timed out catching up its memory stream during resume
QueueFullError@ironflow/browser offline write queue is at maxItems or maxBytes; the write was not stored

Normalizes any thrown value to an Error instance. Useful in catch blocks where the caught value may be a string, number, or plain object rather than an Error.

function toError(error: unknown): Error
try {
await riskyOperation();
} catch (err) {
const error = toError(err); // always an Error instance
console.error(error.message);
}

import {
generateId,
createRunId,
createStepId,
createEventId,
createFunctionId,
} from "@ironflow/core";
// Generate a unique ID (no arguments)
const id = generateId(); // 'm1abc23-x4y5z6'
// Create branded IDs for type safety
const runId = createRunId("run_abc123");
const stepId = createStepId("step_def456");
import { parseDuration } from "@ironflow/core";
parseDuration("30s"); // 30000 (ms)
parseDuration("5m"); // 300000
parseDuration("2h"); // 7200000
parseDuration("1d"); // 86400000
import { createLogger, createNoopLogger } from "@ironflow/core";
// Create a logger with prefix
const logger = createLogger({ prefix: "[myapp]" });
logger.info("Starting...");
logger.debug("Debug info", { data: 123 });
// Create a no-op logger (disables logging)
const noopLogger = createNoopLogger();
import {
calculateBackoff,
sleep,
createDeferred,
safeJsonParse,
isObject,
deepMerge,
} from "@ironflow/core";

Utilities for event schema evolution:

import {
createUpcasterRegistry,
defineEvent,
createEventDefinitionRegistry,
} from "@ironflow/core";
import type { UpcasterFn, EventDefinition } from "@ironflow/core";
// Define versioned events
const orderCreatedV1 = defineEvent({ name: "order.created", version: 1 });
const orderCreatedV2 = defineEvent({
name: "order.created",
version: 2,
upcast: (data: any) => ({ ...data, address: null }),
});
// Registry applies upcasters in chain
const registry = createEventDefinitionRegistry();
registry.register(orderCreatedV1);
registry.register(orderCreatedV2);
// Upcast v1 event data to latest version
const upcasted = registry.upcastEvent("order.created", oldData, 1);
// Pass to serve() or createWorker() for automatic upcasting
// See @ironflow/node docs for serve({ eventDefinitions: registry })

Access generated service definitions and message schemas:

import {
// Service definitions
IronflowService,
PubSubService,
WorkerService,
// Message schemas
RunSchema as ProtoRunSchema,
EventSchema as ProtoEventSchema,
} from "@ironflow/core/gen";