2.4 KiB
job-snapshot
Purpose
Projects a mutable ScheduledJob record (the scheduler's internal data structure) to an immutable JobStatus snapshot for public consumption via JobScheduler.jobs(). Extracted into its own module so the scheduler core stays under the 300-line hard limit and so the shape transformation has a single obvious home.
Features
- Pure function — no side effects, no allocation beyond the returned object.
- Honours
exactOptionalPropertyTypesby conditionally spreading optional fields. - Output object is safe to freeze at the caller.
Usage: Basic Example
import { snapshotJob } from "@suaveplan/job-scheduler";
const status = snapshotJob(internalJobRecord);
console.log(status.id, status.status, status.runs);
Usage: Advanced Example
import { snapshotJob } from "@suaveplan/job-scheduler";
function adminDashboard(jobs: Iterable<ScheduledJob>): JobStatus[] {
const out: JobStatus[] = [];
for (const j of jobs) {
const snap = snapshotJob(j);
if (snap.failures > 0) out.push(snap);
}
return out;
}
API Reference
snapshotJob(job: ScheduledJob): JobStatus— returns a plain object with every public field ofJobStatus.
Implementation Notes
The conditional spread (...(value !== undefined ? { key: value } : {})) is required because the repo TSConfig sets exactOptionalPropertyTypes: true — assigning undefined to an optional field is a compile-time error. Inline-spreading a guard object is the smallest pattern that preserves the omit-vs-undefined distinction. The function deliberately does not freeze its return value; the caller decides whether to freeze the whole array or each individual entry.
Keeping this projection in its own module rather than inlining inside the scheduler class has two side benefits: first, the scheduler core stays under the 300-line file budget that the repo enforces; second, unit-testing the projection in isolation is trivial because every input field can be exercised without spinning up a full scheduler, a queue, and an event bus. The trade-off — a single extra import in job-scheduler.ts — is negligible, especially because the function is small enough that the bundler inlines it during tree-shaking anyway. When new optional fields are added to JobStatus, the only place that needs updating is this file plus the JobStatus type definition, which is exactly where the change should live.