Examples

Beacon

Three long-running, supervised processes — a server, a poller, and a reconnecting client — kept alive by the BeaconRunner. See the Beacons API for the full reference.

beacons/health-server.ts — server mode

import { beacon, z } from "station-beacon";import { createServer } from "node:http"; export const healthServer = beacon("health-server")  .config(z.object({ port: z.number().default(8099) }))  .withConfig({ port: 8099 })  .restart("always")  .backoff("1s", { max: "10s" })  .run(async (ctx) => {    let hits = 0;    const server = createServer((_req, res) => {      hits++;      res.writeHead(200, { "content-type": "application/json" });      res.end(JSON.stringify({ ok: true, hits }));    });    await new Promise<void>((resolve, reject) => {      server.once("error", reject);      server.listen(ctx.config.port, resolve);    });    ctx.ready();    ctx.onStop(async () => { await new Promise<void>((r) => server.close(() => r())); });    await ctx.untilStopped();  });

beacons/uptime-poller.ts — poll mode

import { beacon } from "station-beacon"; export const uptimePoller = beacon("uptime-poller").poll("3s", async (ctx) => {  try {    const res = await fetch("http://localhost:8099/", { signal: ctx.signal });    const body = (await res.json()) as { hits: number };    ctx.log(`health-server up — ${body.hits} total hits`);  } catch (err) {    if (ctx.signal.aborted) return;    ctx.log(`health-server DOWN: ${(err as Error).message}`);  }});

beacons/stream-client.ts — client mode

import { beacon } from "station-beacon"; // Simulates a flaky upstream that drops the connection; throwing lets the// supervisor reconnect with exponential backoff.export const streamClient = beacon("stream-client")  .restart("on-failure")  .backoff("500ms", { factor: 2, max: "8s" })  .heartbeat("2s")  .run(async (ctx) => {    ctx.log(`connecting (incarnation ${ctx.incarnation})…`);    ctx.ready();    const dropsAfterMs = 4_000 + (ctx.incarnation % 3) * 1_000;    const startedAt = Date.now();    while (!ctx.signal.aborted) {      ctx.heartbeat();      await new Promise((r) => setTimeout(r, 1_000));      if (Date.now() - startedAt > dropsAfterMs) throw new Error("connection dropped");    }  });

runner.ts

import path from "node:path";import { BeaconRunner, ConsoleBeaconSubscriber } from "station-beacon"; const runner = BeaconRunner.create(path.join(import.meta.dirname, "beacons"), {  subscribers: [new ConsoleBeaconSubscriber()],  pollIntervalMs: 500,}); await runner.start(); // Ctrl-C runs each beacon's onStop cleanup, then exits

Watch the client drop and get restarted with a growing backoff (500ms → 1s → 2s…). The server stays up under restart("always"); the poller reports live hit counts. Press Ctrl-C for a graceful shutdown.

Run it: pnpm --filter simple-example-15-beacon start


← All examples