@gj-kit/nest-operations-jobs
An unauthenticated trigger, or tuning that lets a job run twice, fails before the scheduler’s first call.
- npm
0.1.2 - 230+ unit tests
- 11 @ts-expect-error guards
- 13 store contract cases
- 0 runtime dependencies
Keep learning
Section titled “Keep learning”Why this exists
Section titled “Why this 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.
What it does about it
Section titled “What it does about it”- 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.
Golden path
Section titled “Golden path”Outcome: An authenticated operations boundary backed by application-owned run storage.
1. Install
Section titled “1. Install”pnpm add @gj-kit/nest-operations-jobs2. Keep the app-owned boundary explicit
Section titled “2. Keep the app-owned boundary explicit”Implement JobRunStore, then configure a 32+ character shared secret or a token verifier before module registration.
3. Start with the smallest integration
Section titled “3. Start with the smallest integration”Copy this first, then replace only the app-owned values named above.
import { OperationsJobsModule, type JobRunStore } from '@gj-kit/nest-operations-jobs';
declare const store: JobRunStore; // Your database-backed run store.declare const secret: string; // At least 32 characters; keep it outside source control.
export const operations = OperationsJobsModule.forRoot({ store, auth: { secret }, trigger: { path: 'internal/jobs' },});What that looks like
Section titled “What that looks like”error is unreachable until status is narrowed, and recorded sits on every branch because a stored row that disagrees with the returned status is exactly what deserves a page.
import type { JobExecutionResult } from '@gj-kit/nest-operations-jobs/core';
declare const result: JobExecutionResult; // await runner.execute(...)declare function pageOncall(jobKey: string, runId: string): void; // the app owns this
export function report(): void { // console.error(result.error.code); // -> error TS2339: Property 'error' does not exist on type 'JobExecutionResult'. if (result.status === 'TIMED_OUT') { console.error(result.jobKey, result.error.code); // 'ERR_JOB_TIMEOUT' } else if (result.status === 'SKIPPED') { console.warn(result.jobKey, result.reason); // 'overlap' - the only value }
// `recorded` sits on every branch: 'superseded' means a reaper already // finalised the row, so a second body may run under a different runId. if (result.recorded === 'superseded') pageOncall(result.jobKey, result.runId);}Use it when
Section titled “Use it when”Use it when a Nest application needs scheduled or operator-triggered work with explicit concurrency, authorization, and run persistence.
Do not use it when
Section titled “Do not use it when”Do not hide product business rules, queue infrastructure, or application authorization policy behind this integration.
Runtime and peers
Section titled “Runtime and peers”Engines: node >=20
| Peer | Supported range | Required for |
|---|---|---|
@nestjs/common |
`^10 | |
@nestjs/core |
`^10 | |
reflect-metadata |
`^0.1.13 | |
rxjs |
^7 |
the package integration |
Public subpaths
Section titled “Public subpaths”| Entry | Exported symbols |
|---|---|
Root entry |
60 |
./core |
61 |
./testing |
13 |
Safety boundary
Section titled “Safety boundary”Keep job-trigger authorization and app data ownership in the host application. Never turn a convenience route into an unauthenticated operations endpoint.
API reference
Section titled “API reference”complete API reference contains every public function, class, type, constant, and error contract generated from release declarations. Each item shows its import path, release signature, and package version.
Package relationships
Section titled “Package relationships”| Relationship | Package | Why combine them |
|---|---|---|
| Standalone | — | This package can be adopted without another GJ Kit package. |
| Optional companion | @gj-kit/nest-notifications |
Notify operators or users after an application-owned job result. |