@gj-kit/nest-operations-jobs core concepts
An unauthenticated trigger, or tuning that lets a job run twice, fails before the scheduler’s first call.
Why this boundary exists
Section titled “Why this boundary exists”Hand-rolled cron endpoints fail while every dashboard stays green. Swallow the whole unique-constraint violation inside your claim and a permanently blocked job becomes a stream of SKIPPED/200 responses; set the stale-run budget below the heartbeat interval and a healthy run looks reapable to the next trigger, which starts a second body. Meanwhile the trigger route ships with a short shared secret compared by ===, the stored run row can disagree with the status you returned without anyone noticing, and a scheduler attempt deadline set under the job’s own timeout records long runs that actually succeeded as failures.
A trigger is an operations boundary
Section titled “A trigger is an operations boundary”A scheduled or operator-triggered route needs host-owned authorization and durable run storage. The package coordinates execution but never defines business work.
Run state is a discriminated result
Section titled “Run state is a discriminated result”Completed, failed, skipped, and superseded runs do not share fields. Narrowing prevents a summary from claiming an execution state that never happened.
What the package enforces
Section titled “What the package enforces”- Omitting
authis a compile error —authis a required field of OperationsJobsModuleOptions, so a module wired without it does not type-check. An emptyauth, or a secret under 32 characters, throws ERR_JOB_AUTH_MISCONFIGURED while forRoot assembles the module. - Result fields exist only after narrowing — On JobExecutionResult,
error,reasonandsummaryare unreachable until you switch onstatus; three @ts-expect-error fixtures pin that. - The tuning that voids single execution — createJobRunner throws ERR_JOB_INVALID when staleRunAfterMs is under 2x heartbeatIntervalMs — the floor below which a healthy run’s watermark can outlive the liveness budget between its own beats.
- Your store’s atomicity, in your suite — jobRunStoreContractCases() returns 13 framework-free cases covering obligations S1-S6 — a concurrent claim burst, two concurrent reaps of the same three rows — that you run against your real database. Supply
inspectand it returns 16, adding S7. - /core provably contains no Nest — A guard test scans src/core/, src/testing/ and every built dist/core.* and dist/testing.* chunk for @nestjs, rxjs and reflect-metadata, and a control case asserts dist/index.js does contain @nestjs — so an empty result means the scanner actually looked.
What remains application-owned
Section titled “What remains application-owned”Keep job-trigger authorization and app data ownership in the host application. Never turn a convenience route into an unauthenticated operations endpoint.
Continue with the two verified recipes.