TypeScript SDK
The TypeScript SDK ships as a single package with two modules:
| Export | Purpose |
|---|---|
createClient | Low-level HTTP client — fetch-and-lock, complete, fail, extend lock |
createWorker / subscribe / runWorker | Worker abstraction — polling loop, heartbeat, auto-complete |
Installation
bun add @orrery/sdkcreateClient
Creating a client
import { createClient } from "@orrery/sdk";
const client = createClient({ baseUrl: "http://localhost:3000" });
// baseUrl defaults to ORRERY_URL env var, then "http://localhost:3000"Methods
// Fetch and lock tasks
const tasks = await client.fetchAndLock({
workerId: "my-worker",
subscriptions: [
{ topic: "validatePayment" },
{ topic: "chargeCard", processDefinitionIds: ["order-process"] },
],
maxTasks: 4, // default: 1
lockDurationMs: 30000, // default: 30 000
});
// Complete a task
await client.completeTask(task.id, workerId, { approved: true });
// Fail a task
await client.failTask(task.id, workerId, "payment declined", 2, 5000);
// args: id, workerId, errorMessage, retries (default 0), retryTimeoutMs (default 0)
// Extend the lock (heartbeat)
await client.extendLock(task.id, workerId, 30000);Errors are thrown as Error objects with message "HTTP {status}: {body}".
ExternalTask shape
interface ExternalTask {
id: string;
topic: string;
processInstanceId: string;
processDefinitionId: string;
elementId: string;
variables: Record<string, unknown>;
workerId: string;
lockedUntil: string; // ISO 8601
retryCount: number;
maxRetries: number;
createdAt: string; // ISO 8601
}Worker
createWorker + subscribe + runWorker wrap the polling loop. The worker automatically extends locks (heartbeat at 50% of lockDurationMs), completes tasks on success, and fails them on error.
Minimal example
import { createWorker, subscribe, runWorker } from "@orrery/sdk";
let worker = createWorker({ baseUrl: "http://localhost:3000" });
worker = subscribe(
worker,
{ topic: "validatePayment" },
async (task) => {
const amount = task.variables.amount as number;
return { valid: amount > 0 };
},
);
await runWorker(worker); // blocks until SIGINT / SIGTERMsubscribe is immutable — it returns a new worker with the handler registered.
Worker options
const worker = createWorker({
workerId: "payment-worker-1", // default: "worker-{timestamp}"
baseUrl: "http://localhost:3000",
lockDurationMs: 60000, // default: 30 000
requestTimeoutMs: 20000, // default: 20 000
concurrency: 8, // default: 4
});If baseUrl is omitted, the worker reads ORRERY_URL from the environment and falls back to http://localhost:3000.
Multiple topics
worker = subscribe(worker, { topic: "validatePayment" }, async (task) => {
return { valid: true };
});
worker = subscribe(
worker,
{ topic: "chargeCard", processDefinitionIds: ["order-process"] },
async (task) => {
return { chargeId: "ch_abc123" };
},
);
worker = subscribe(worker, { topic: "sendReceipt" }, async (task) => {
return {};
});processDefinitionIds filters tasks to only those definitions. Omit it (or pass an empty array) to receive tasks from any definition.
Error handling
Throw any error to fail the task. The error message is stored and the task is retried according to its retry configuration.
worker = subscribe(worker, { topic: "chargeCard" }, async (task) => {
const amount = task.variables.amount as number;
if (!amount) throw new Error("missing amount variable");
if (amount > 10_000) throw new Error("amount exceeds limit");
return { charged: true, amount };
});Task context
The handler receives an ExternalTask with the full task context:
worker = subscribe(worker, { topic: "processOrder" }, async (task) => {
console.log("task: ", task.id);
console.log("topic: ", task.topic);
console.log("instance: ", task.processInstanceId);
console.log("definition:", task.processDefinitionId);
console.log("retry: ", task.retryCount, "/", task.maxRetries);
const orderId = task.variables.orderId;
const amount = task.variables.amount as number;
return { total: amount };
});Source:
sdks/typescript/in the main repository.