Node SDK (TypeScript)

ESM TypeScript SDK. Lives at workers/node/. Built on the platform fetch + node:test. No decorator support is required — TypeScript decorators are still opt-in (experimentalDecorators), so the SDK ships a defineHandler builder instead.

Install

npm install @conduit/worker        # once published to npm
# Until v0.1 ships:
npm install ./workers/node

Required runtime: Node 20+ (uses platform fetch and AbortController).

Quick start with defineHandler

import {
  Client, defineHandler, ExternalTask, HandlerResult,
  Runner, Variable,
} from "@conduit/worker";

export const httpCall = defineHandler({
  topic: "http.call",
  async handle(task: ExternalTask): Promise<HandlerResult> {
    const url = task.variable("url") as string;
    const res = await fetch(url);
    return HandlerResult.complete([
      Variable.long("status", res.status),
      Variable.string("body", await res.text()),
    ]);
  },
});

const client = new Client({ baseUrl: "http://localhost:8080" });
const runner = new Runner(client, { workerId: "http-worker-1" });
runner.register(httpCall);
await runner.run();

defineHandler is a typed identity — it gives you autocomplete on the shape and lets runner.register(handler) know topic without you typing it twice.

Direct registration

runner.register also accepts a (topic, fn) pair:

runner.register("policy.check", async (task) => HandlerResult.bpmnError("POLICY_VIOLATION", "not allowed"));

RunnerConfig

const config = {
  workerId: "http-worker-1",
  maxJobs: 10,                       // 1..100, default 10
  lockDurationSecs: 30,
  pollIntervalMs: 1000,              // idle back-off
};
const runner = new Runner(client, config);

Reporting outcomes

ReturnEngine call
HandlerResult.complete([Variable, ...])/complete
HandlerResult.bpmnError(code, message, [Variable, ...]?)/bpmn-error
Throw / reject/failure
import { HandlerResult } from "@conduit/worker";

return HandlerResult.complete([Variable.long("status", 201)]);
return HandlerResult.bpmnError("PAYMENT_DECLINED", "card declined");
throw new Error("upstream timeout");        // engine decrements retries

Variables

Variable.string("name", "alice");
Variable.long("count", 42);
Variable.double("ratio", 0.75);
Variable.boolean("approved", true);
Variable.json("payload", { x: 1 });
Variable.null("tombstone");

Read off the task:

const orderId = task.variable("order_id") as string | undefined;
const all: Record<string, unknown> = task.variableMap();

Custom fetch

The client config accepts an optional fetch override — useful for tests, proxies, or mTLS:

new Client({ baseUrl, fetch: myInstrumentedFetch });

Building and running

cd workers/node
npm install
npm test                 # node:test against a fake fetch
npm run build            # tsc → dist/
node dist/my-worker.js

Idempotency

Same contract as every other SDK — see Idempotency.