Files
skills/docs/foundation/flow/job-scheduler/src/registry/job-registry.md
2026-05-29 08:20:56 +01:00

2.9 KiB

job-registry

(no submodules)

Purpose

Provides a string-to-jobId binding so application code can refer to scheduled jobs by stable name ("nightly-cleanup", "hourly-report") without leaking the scheduler's internal id generator (crypto.randomUUID() by default). The registry is a thin Map wrapper with explicit semantics for duplicate names — it throws rather than silently overwriting, because silent overwrite has been a recurring source of "ghost job" bugs in similar systems where a redeploy registers the same name twice and the original job leaks.

Features

  • register(name, jobId) — throws on duplicate name with a typed JobSchedulerError.
  • unregister(name) — returns boolean for unambiguous removal semantics.
  • resolve(name) -> jobId | undefined — pure lookup, never throws.
  • list() — frozen snapshot for diagnostics and admin endpoints.
  • Zero dependencies beyond the package's own error class.

Usage: Basic Example

import { JobRegistry } from "@suaveplan/job-scheduler";

const registry = new JobRegistry();
registry.register("nightly", "uuid-1");
console.log(registry.resolve("nightly")); // "uuid-1"
registry.unregister("nightly");

Usage: Advanced Example

import {
    createJobScheduler,
    JobRegistry,
    JobSchedulerError,
    JOB_SCHEDULER_CODES,
} from "@suaveplan/job-scheduler";

const scheduler = createJobScheduler();
const registry = new JobRegistry();

const jobId = scheduler.schedule("0 * * * *", async () => {
    // ...
});
registry.register("hourly-report", jobId);

try {
    registry.register("hourly-report", "different-id");
} catch (err) {
    if (
        err instanceof JobSchedulerError &&
        err.code === JOB_SCHEDULER_CODES.JOB_NOT_FOUND
    ) {
        // Duplicate-name guard tripped — surface a CLI-friendly message.
    }
}

for (const { name, jobId } of registry.list()) {
    console.log(name, "->", jobId);
}

API Reference

  • class JobRegistry
    • register(name: string, jobId: string): void — throws JobSchedulerError on duplicate name.
    • unregister(name: string): boolean — true when removed, false when unknown.
    • resolve(name: string): string | undefined — pure lookup.
    • list(): ReadonlyArray<{ name: string; jobId: string }> — frozen snapshot.

Implementation Notes

The duplicate-name branch reuses JOB_SCHEDULER.JOB_NOT_FOUND rather than introducing a fourth registry code. The decision is deliberate: the registry is a 12-line wrapper over Map, and growing the error registry just for it would couple the package's public contract to a peripheral feature. The context payload on the thrown error includes the conflicting name, so consumers branching on the duplicate-name case can recognise it without ambiguity. The registry is not synchronised — concurrent registration from two async tasks may race; if you need atomicity, wrap it in a Mutex from @suaveplan/timing.