# 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 `exactOptionalPropertyTypes` by conditionally spreading optional fields. - Output object is safe to freeze at the caller. ## Usage: Basic Example ```ts import { snapshotJob } from "@suaveplan/job-scheduler"; const status = snapshotJob(internalJobRecord); console.log(status.id, status.status, status.runs); ``` ## Usage: Advanced Example ```ts import { snapshotJob } from "@suaveplan/job-scheduler"; function adminDashboard(jobs: Iterable): 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 of `JobStatus`. ## 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.