Examples

With Steps

Multi-step signals where each step's output pipes to the next.

signals/process-order.ts

import { signal, z } from "station-signal"; export const processOrder = signal("processOrder")  .input(z.object({ orderId: z.string(), amount: z.number() }))  .timeout(30_000)  .step("validate", async (input) => {    console.log(`[validate] Checking order ${input.orderId}...`);    if (input.amount <= 0) throw new Error("Invalid amount");    return { orderId: input.orderId, amount: input.amount, validated: true };  })  .step("charge", async (prev) => {    console.log(`[charge] Charging $${prev.amount} for order ${prev.orderId}...`);    await new Promise((r) => setTimeout(r, 500));    const chargeId = `ch_${Math.random().toString(36).slice(2, 10)}`;    return { orderId: prev.orderId, chargeId };  })  .step("fulfill", async (prev) => {    console.log(`[fulfill] Fulfilling order ${prev.orderId} (charge: ${prev.chargeId})...`);    await new Promise((r) => setTimeout(r, 300));    return { orderId: prev.orderId, status: "fulfilled", chargeId: prev.chargeId };  })  .build();

runner.ts (relevant parts)

const runner = new SignalRunner({  signalsDir: path.join(import.meta.dirname, "signals"),  subscribers: [    new ConsoleSubscriber(),    {      onStepCompleted({ run, step }) {        console.log(`  step "${step.name}" done (run ${run.id})`);      },    },  ],});

.step() chains sequential operations. Each step receives the previous step's return value. Use .build() instead of .run() when using steps. The onStepCompleted subscriber hook fires after each step finishes.

Run it: pnpm --filter example-with-steps start


← All examples