@softure-ai/jobs 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/LICENSE +21 -0
  3. package/README.md +224 -2
  4. package/dist/contract.d.ts +48 -0
  5. package/dist/contract.d.ts.map +1 -0
  6. package/dist/contract.js +2 -0
  7. package/dist/contract.js.map +1 -0
  8. package/dist/handlers.d.ts +60 -0
  9. package/dist/handlers.d.ts.map +1 -0
  10. package/dist/handlers.js +29 -0
  11. package/dist/handlers.js.map +1 -0
  12. package/dist/index.d.ts +39 -0
  13. package/dist/index.d.ts.map +1 -0
  14. package/dist/index.js +36 -0
  15. package/dist/index.js.map +1 -0
  16. package/dist/messages/en.d.ts +12 -0
  17. package/dist/messages/en.d.ts.map +1 -0
  18. package/dist/messages/en.js +12 -0
  19. package/dist/messages/en.js.map +1 -0
  20. package/dist/messages/index.d.ts +17 -0
  21. package/dist/messages/index.d.ts.map +1 -0
  22. package/dist/messages/index.js +4 -0
  23. package/dist/messages/index.js.map +1 -0
  24. package/dist/options.d.ts +23 -0
  25. package/dist/options.d.ts.map +1 -0
  26. package/dist/options.js +56 -0
  27. package/dist/options.js.map +1 -0
  28. package/dist/schema.d.ts +265 -0
  29. package/dist/schema.d.ts.map +1 -0
  30. package/dist/schema.js +22 -0
  31. package/dist/schema.js.map +1 -0
  32. package/dist/server/admin.d.ts +15 -0
  33. package/dist/server/admin.d.ts.map +1 -0
  34. package/dist/server/admin.js +56 -0
  35. package/dist/server/admin.js.map +1 -0
  36. package/dist/server/backoff.d.ts +6 -0
  37. package/dist/server/backoff.d.ts.map +1 -0
  38. package/dist/server/backoff.js +16 -0
  39. package/dist/server/backoff.js.map +1 -0
  40. package/dist/server/claim.d.ts +42 -0
  41. package/dist/server/claim.d.ts.map +1 -0
  42. package/dist/server/claim.js +109 -0
  43. package/dist/server/claim.js.map +1 -0
  44. package/dist/server/context.d.ts +5 -0
  45. package/dist/server/context.d.ts.map +1 -0
  46. package/dist/server/context.js +2 -0
  47. package/dist/server/context.js.map +1 -0
  48. package/dist/server/enqueue.d.ts +38 -0
  49. package/dist/server/enqueue.d.ts.map +1 -0
  50. package/dist/server/enqueue.js +86 -0
  51. package/dist/server/enqueue.js.map +1 -0
  52. package/dist/server/handler-check.d.ts +4 -0
  53. package/dist/server/handler-check.d.ts.map +1 -0
  54. package/dist/server/handler-check.js +23 -0
  55. package/dist/server/handler-check.js.map +1 -0
  56. package/dist/server/health.d.ts +3 -0
  57. package/dist/server/health.d.ts.map +1 -0
  58. package/dist/server/health.js +11 -0
  59. package/dist/server/health.js.map +1 -0
  60. package/dist/server/index.d.ts +10 -0
  61. package/dist/server/index.d.ts.map +1 -0
  62. package/dist/server/index.js +11 -0
  63. package/dist/server/index.js.map +1 -0
  64. package/dist/server/job-row.d.ts +6 -0
  65. package/dist/server/job-row.d.ts.map +1 -0
  66. package/dist/server/job-row.js +28 -0
  67. package/dist/server/job-row.js.map +1 -0
  68. package/dist/server/logger.d.ts +11 -0
  69. package/dist/server/logger.d.ts.map +1 -0
  70. package/dist/server/logger.js +27 -0
  71. package/dist/server/logger.js.map +1 -0
  72. package/dist/server/maintenance.d.ts +22 -0
  73. package/dist/server/maintenance.d.ts.map +1 -0
  74. package/dist/server/maintenance.js +51 -0
  75. package/dist/server/maintenance.js.map +1 -0
  76. package/dist/server/options.d.ts +7 -0
  77. package/dist/server/options.d.ts.map +1 -0
  78. package/dist/server/options.js +22 -0
  79. package/dist/server/options.js.map +1 -0
  80. package/dist/server/process.d.ts +16 -0
  81. package/dist/server/process.d.ts.map +1 -0
  82. package/dist/server/process.js +88 -0
  83. package/dist/server/process.js.map +1 -0
  84. package/dist/server/run-due.d.ts +28 -0
  85. package/dist/server/run-due.d.ts.map +1 -0
  86. package/dist/server/run-due.js +40 -0
  87. package/dist/server/run-due.js.map +1 -0
  88. package/dist/testing/index.d.ts +6 -0
  89. package/dist/testing/index.d.ts.map +1 -0
  90. package/dist/testing/index.js +10 -0
  91. package/dist/testing/index.js.map +1 -0
  92. package/dist/worker/index.d.ts +3 -0
  93. package/dist/worker/index.d.ts.map +1 -0
  94. package/dist/worker/index.js +5 -0
  95. package/dist/worker/index.js.map +1 -0
  96. package/dist/worker/process.d.ts +26 -0
  97. package/dist/worker/process.d.ts.map +1 -0
  98. package/dist/worker/process.js +79 -0
  99. package/dist/worker/process.js.map +1 -0
  100. package/dist/worker/run-job-worker.d.ts +29 -0
  101. package/dist/worker/run-job-worker.d.ts.map +1 -0
  102. package/dist/worker/run-job-worker.js +149 -0
  103. package/dist/worker/run-job-worker.js.map +1 -0
  104. package/migrations/0001_create_jobs.sql +39 -0
  105. package/module.json +12 -0
  106. package/package.json +60 -4
  107. package/src/contract.ts +54 -0
  108. package/src/handlers.ts +81 -0
  109. package/src/index.ts +45 -0
  110. package/src/messages/en.ts +11 -0
  111. package/src/messages/index.ts +6 -0
  112. package/src/options.ts +64 -0
  113. package/src/schema.ts +25 -0
  114. package/src/server/admin.ts +57 -0
  115. package/src/server/backoff.ts +22 -0
  116. package/src/server/claim.ts +148 -0
  117. package/src/server/context.ts +5 -0
  118. package/src/server/enqueue.ts +117 -0
  119. package/src/server/handler-check.ts +27 -0
  120. package/src/server/health.ts +12 -0
  121. package/src/server/index.ts +19 -0
  122. package/src/server/job-row.ts +33 -0
  123. package/src/server/logger.ts +34 -0
  124. package/src/server/maintenance.ts +67 -0
  125. package/src/server/options.ts +24 -0
  126. package/src/server/process.ts +112 -0
  127. package/src/server/run-due.ts +64 -0
  128. package/src/testing/index.ts +15 -0
  129. package/src/worker/index.ts +4 -0
  130. package/src/worker/process.ts +90 -0
  131. package/src/worker/run-job-worker.ts +183 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ # Changelog
2
+
3
+ Newest first. Each version lists what changed for an app that uses `@softure-ai/jobs`. When an app has run a version in
4
+ production, the version gets a line `verified in: <app>@<commit>` ([docs/05](../../docs/05-adoption-playbook.md),
5
+ "Definition of done").
6
+
7
+ ## 0.1.0
8
+
9
+ - First version (#471): background jobs in Postgres (`jobs.jobs`). `jobs({ queues })` in `softure.config.ts`;
10
+ `enqueueJob` inside the caller's transaction with an optional `dedupKey`; `cancelJob`, `retryJob`, `getJob`,
11
+ `getQueueStats`, `pruneJobs`; handlers with a zod payload schema (`defineJobHandler`, `failJob`, `retryJobLater`);
12
+ `runJobWorker` and `runJobWorkerProcess` (`@softure-ai/jobs/worker`): claim with `FOR UPDATE SKIP LOCKED` under a
13
+ lease, a heartbeat for long jobs, fenced completion, retry with exponential backoff and jitter, a terminal `failed`
14
+ state, pruning, and release of unfinished jobs on SIGTERM; `runDueJobs` (`@softure-ai/jobs/testing`) runs due jobs
15
+ in-process for tests and cron-driven apps.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 SOFTURE
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,3 +1,225 @@
1
- # Temporary Holding Version
1
+ # @softure-ai/jobs
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Background jobs in Postgres: an app enqueues work in the same transaction as its own write, and a worker process
4
+ runs it with leases, retries and a terminal failure state. Built for the case an adopting app solved by hand (a job
5
+ table of its own and a `scripts/worker.ts` loop): a slow or unreliable call after a request, such as an AI call per
6
+ received message.
7
+
8
+ ## 1. What it provides
9
+
10
+ A job queue in one table (`jobs.jobs`) with enqueue inside the caller's transaction and dedup keys, claims with
11
+ `FOR UPDATE SKIP LOCKED` under a lease, fenced completion, retry with exponential backoff, a worker process with
12
+ graceful shutdown, and an in-process runner for tests and cron-driven apps.
13
+
14
+ ## 2. Installation
15
+
16
+ ```bash
17
+ npm install @softure-ai/jobs @softure-ai/core @softure-ai/db drizzle-orm zod
18
+ ```
19
+
20
+ The worker runs as its own process. With `@softure-ai/deploy`, `softure-deploy init` sees `@softure-ai/jobs` in
21
+ `package.json` and adds `scripts/worker.ts`, its bundle in the image and a `worker` service next to the app (deploy
22
+ README, "Job worker").
23
+
24
+ ## 3. Configuration
25
+
26
+ ```ts
27
+ import { defineSoftureConfig } from "@softure-ai/core";
28
+ import { jobs } from "@softure-ai/jobs";
29
+
30
+ export default defineSoftureConfig({
31
+ // database, locale, timezone, appOrigin …
32
+ modules: [
33
+ jobs({
34
+ queues: {
35
+ "ai.reply": { maxAttempts: 5, backoff: { baseSeconds: 10, maxSeconds: 600 }, leaseSeconds: 120, concurrency: 4 },
36
+ "mail.digest": {},
37
+ },
38
+ }),
39
+ ],
40
+ });
41
+ ```
42
+
43
+ | Option | Default | Meaning |
44
+ | --- | --- | --- |
45
+ | `queues.<name>.maxAttempts` | `5` | attempts before a job is `failed`; the first run counts |
46
+ | `queues.<name>.backoff` | `{ baseSeconds: 10, maxSeconds: 3600 }` | delay after attempt n is `min(max, base * 2^(n-1))`, shortened by up to 20 % at random |
47
+ | `queues.<name>.leaseSeconds` | `300` | how long a claimed job is reserved; the worker extends it while the handler runs, so it bounds how long a crashed worker's job waits, not how long a handler may take |
48
+ | `queues.<name>.concurrency` | `1` | jobs of the queue one worker process runs at once |
49
+ | `pollIntervalMs` | `1000` | how often an idle worker looks for due jobs |
50
+ | `shutdownGraceMs` | `20000` | how long a stopping worker waits for running handlers before releasing their jobs |
51
+ | `retention` | `{ succeededDays: 7, failedDays: 30, cancelledDays: 7 }` | how long finished jobs are kept; the worker prunes once an hour |
52
+
53
+ A queue name is lowercase letters, digits, `_`, `.` and `-`. A queue the config does not define is refused by
54
+ `enqueueJob` and by the worker: a typo is a bug, not a new queue.
55
+
56
+ ### Handlers
57
+
58
+ One handler per queue, with the zod schema of its payload. The payload read back from the table is parsed with it
59
+ before the handler runs; a payload that does not match fails the job with `jobs.invalid_payload` and is not retried.
60
+
61
+ ```ts
62
+ // src/jobs/handlers.ts
63
+ import { ok } from "@softure-ai/core";
64
+ import { defineJobHandler, failJob, retryJobLater, type JobHandlers } from "@softure-ai/jobs";
65
+ import { z } from "zod";
66
+
67
+ export const handlers: JobHandlers = {
68
+ "ai.reply": defineJobHandler({
69
+ payload: z.object({ messageId: z.string() }),
70
+ run: async ({ messageId }, { db, signal, job }) => {
71
+ const message = await findMessage(db, messageId);
72
+ if (message === null) return failJob("app.message_gone"); // failed at once, no retry
73
+ const reply = await callModel(message, { signal });
74
+ if (reply.status === 429) return retryJobLater("app.rate_limited", reply.retryAfterSeconds);
75
+ if (!reply.ok) return reply; // an Err: retried with the queue's backoff
76
+ await saveReply(db, messageId, reply.value);
77
+ return ok();
78
+ },
79
+ }),
80
+ };
81
+ ```
82
+
83
+ - `ok()`: done, the job is `succeeded`.
84
+ - An `Err` (any `Result` failure): retried after the backoff delay, until `maxAttempts`; then `failed`.
85
+ - `failJob(code)`: `failed` at once.
86
+ - `retryJobLater(code, seconds)`: retried after `seconds` instead of the backoff (a provider's `Retry-After`); still
87
+ counts as an attempt.
88
+ - A throw: retried like an `Err` with the code `jobs.handler_threw`, and logged with the error.
89
+
90
+ **Handlers run at least once.** A worker can die after the side effect and before recording it, a lease can end, a
91
+ stopping worker releases unfinished jobs: the job then runs again. Make handlers idempotent (check whether the reply
92
+ was already saved, pass an idempotency key to the provider).
93
+
94
+ `context.signal` aborts when the job is taken from the handler: the worker's grace period ended on shutdown, or its
95
+ lease could not be extended. Pass it to `fetch` and other cancellable calls; anything the handler returns afterwards
96
+ is not recorded. `context.db` is the worker's pool, not a transaction; `context.job.attempts` is this attempt's number.
97
+
98
+ ### Enqueueing
99
+
100
+ ```ts
101
+ import { enqueueJob } from "@softure-ai/jobs/server";
102
+
103
+ await db.transaction(async (tx) => {
104
+ const [message] = await tx.insert(messages).values(input).returning();
105
+ await enqueueJob({ db: tx, clock, config }, { queue: "ai.reply", payload: { messageId: message.id }, dedupKey: message.id });
106
+ });
107
+ ```
108
+
109
+ | Field | Meaning |
110
+ | --- | --- |
111
+ | `queue` | a configured queue |
112
+ | `payload` | JSON for the handler, `{}` when there is none; stored as `jsonb`, so a `Date` comes back as a string. Ids, not personal data |
113
+ | `runAt` / `delaySeconds` | when the job is due; default now; one or the other |
114
+ | `dedupKey` | while a pending or running job of the queue has this key, the call returns that job (`isDuplicate: true`) instead of writing a second one; a finished job frees the key |
115
+ | `maxAttempts` | this job's own limit instead of the queue's |
116
+
117
+ Inside the caller's transaction the job exists exactly when the caller's change commits; a rolled-back transaction
118
+ leaves no job behind.
119
+
120
+ ### Running the worker
121
+
122
+ ```ts
123
+ // scripts/worker.ts
124
+ import { runJobWorkerProcess } from "@softure-ai/jobs/worker";
125
+ import config from "../softure.config";
126
+ import { handlers } from "../src/jobs/handlers";
127
+
128
+ await runJobWorkerProcess({ config, handlers });
129
+ ```
130
+
131
+ `runJobWorkerProcess` opens its own pool (`database.url`, or the app's `database.handle`) with one connection per job
132
+ that can run at once plus one, runs the worker until SIGTERM or SIGINT, closes the pool and sets `process.exitCode`
133
+ (0 after a clean stop, 1 when it could not start). Pass `logger: createLogger("jobs")` from
134
+ `@softure-ai/observability` to send its lines to OpenTelemetry; by default they go to the console. `queues: [...]`
135
+ runs only some queues (one worker per queue group).
136
+
137
+ The worker claims due jobs of every queue it has a handler for, up to each queue's `concurrency`, oldest due first.
138
+ A claim counts as an attempt and takes a lease, which a heartbeat extends while the handler runs. A worker that dies
139
+ leaves its jobs `running` until their lease ends; the next claim then takes them over (or fails them with
140
+ `jobs.lease_expired` when no attempt is left). Every write after a claim is fenced by a claim token, so the worker
141
+ whose lease was taken over cannot record an outcome over the newer attempt. Several workers can run side by side:
142
+ `SKIP LOCKED` hands each job to one of them.
143
+
144
+ On SIGTERM the worker stops claiming, waits up to `shutdownGraceMs` for running handlers, then aborts the rest and
145
+ releases their jobs (pending again, due now, the attempt given back). Database errors while claiming or recording are
146
+ logged and retried on the next round; a database restart does not end the worker.
147
+
148
+ `runJobWorker({ db, config, handlers, signal })` is the loop without the process part, for an app that runs it inside
149
+ a process of its own.
150
+
151
+ ### Without a worker
152
+
153
+ `runDueJobs(ctx, { handlers })` (from `@softure-ai/jobs/server` or `/testing`) runs every due job once, in this process,
154
+ one after another, and returns `{ succeeded, retried, failed, stale }`. A cron can drain a queue with it; call
155
+ `pruneJobs(ctx)` from the same cron.
156
+
157
+ ### Steering and overview
158
+
159
+ From `@softure-ai/jobs/server`, each on `ctx = { db, clock, config }`:
160
+
161
+ | Function | Result |
162
+ | --- | --- |
163
+ | `getJob(ctx, id)` | the `Job` or `null` |
164
+ | `cancelJob(ctx, id)` | a pending job becomes `cancelled`; `jobs.not_cancellable` for a running or finished one, `jobs.not_found` |
165
+ | `retryJob(ctx, id)` | a failed or cancelled job runs again from scratch (attempts 0, due now); `jobs.not_retryable`, `jobs.dedup_key_taken` while a live job has its key, `jobs.not_found` |
166
+ | `getQueueStats(ctx)` | per queue: counts per status, the due backlog and the `run_at` of its oldest due job |
167
+ | `pruneJobs(ctx)` | deletes finished jobs past their retention; returns the count |
168
+
169
+ `getQueueStats` suits an app health check of its own, `ops({ checks: { "app.jobs": … } })`, e.g. unavailable when the
170
+ oldest due job of a queue waited more than ten minutes (no worker is running). An ops script (`@softure-ai/ops/scripts`)
171
+ can wrap `retryJob` and `cancelJob` for the operator.
172
+
173
+ ## 4. Mounting
174
+
175
+ Nothing to mount: the module has no routes or pages. The worker is a process (§3, "Running the worker").
176
+
177
+ ## 5. Migrations and tables
178
+
179
+ Schema `jobs`, one table, `migrations/0001_create_jobs.sql`:
180
+
181
+ `jobs.jobs`: `id` (bigint identity; a string in the API), `queue`, `payload` (jsonb), `status` (`pending`, `running`,
182
+ `succeeded`, `failed`, `cancelled`), `attempts`, `max_attempts`, `run_at`, `locked_until`, `locked_by` (the worker,
183
+ `<hostname>:<pid>`), `claim_token`, `dedup_key`, `last_error` (an error code, never a message), `created_at`,
184
+ `updated_at`, `finished_at`. Checks keep the lease columns set exactly while `running` and `finished_at` set exactly
185
+ for finished jobs; a partial unique index allows one live job per queue and dedup key.
186
+
187
+ The app role needs `SELECT, INSERT, UPDATE, DELETE` on the table, which the `@softure-ai/ops` roles recipe grants on
188
+ every schema the migrator creates.
189
+
190
+ ## 6. Environment variables
191
+
192
+ None of its own. The worker reads the app's config, so it gets the app's `DATABASE_URL` and whatever its handlers need.
193
+
194
+ ## 7. Switches
195
+
196
+ None.
197
+
198
+ ## 8. Appearance
199
+
200
+ No UI.
201
+
202
+ ## 9. Copy
203
+
204
+ `errors.*` in `src/messages/en.ts`, one key per error code (`invalid_payload`, `handler_threw`, `lease_expired`,
205
+ `not_found`, `not_cancellable`, `not_retryable`, `dedup_key_taken`), for an admin screen that shows a job's state.
206
+
207
+ ## 10. Hooks
208
+
209
+ The handlers (§3) are the app's code. `logger` (`info`, `warn`, `error`; `@softure-ai/observability`'s `Logger` fits)
210
+ receives the worker's lines: started, stopped, a thrown handler with its error, a lost lease, a release, pruning.
211
+
212
+ ## 11. GDPR
213
+
214
+ No export or deletion contributor: jobs are transient and pruned (§3, `retention`). Keep personal data out of
215
+ payloads (ids only); then a deleted account leaves nothing personal in `jobs.jobs`. `last_error` holds codes, and the
216
+ logger gets payload paths, never values, for a payload that does not match its schema.
217
+
218
+ ## 12. Limitations
219
+
220
+ - Polling, no `LISTEN/NOTIFY`: a job waits up to `pollIntervalMs` when the worker is idle.
221
+ - No cron schedules or recurring jobs: a cron (or a handler that enqueues its next run) does that.
222
+ - No priorities within a queue and no rate limits per queue beyond `concurrency`; split work into queues instead.
223
+ - At-least-once delivery (§3, "Handlers run at least once").
224
+ - PGlite has one connection, so tests run claims one after another; concurrent claiming is Postgres behaviour
225
+ (`SKIP LOCKED`) and is not exercised in unit tests.
@@ -0,0 +1,48 @@
1
+ import type { Err, ErrorCode } from "@softure-ai/core";
2
+ export type JobsErrorCode = "jobs.invalid_payload" | "jobs.handler_threw" | "jobs.lease_expired" | "jobs.not_found" | "jobs.not_cancellable" | "jobs.not_retryable" | "jobs.dedup_key_taken";
3
+ export declare const JOB_STATUSES: readonly ["pending", "running", "succeeded", "failed", "cancelled"];
4
+ export type JobStatus = (typeof JOB_STATUSES)[number];
5
+ /** One job as the module's functions return it. */
6
+ export interface Job {
7
+ readonly id: string;
8
+ readonly queue: string;
9
+ readonly payload: unknown;
10
+ readonly status: JobStatus;
11
+ /** Claims so far; the running attempt's number while `running`. */
12
+ readonly attempts: number;
13
+ readonly maxAttempts: number;
14
+ /** When a pending job is due. */
15
+ readonly runAt: Date;
16
+ readonly lockedUntil: Date | null;
17
+ readonly lockedBy: string | null;
18
+ readonly dedupKey: string | null;
19
+ /** The error code of the last failed attempt. */
20
+ readonly lastError: string | null;
21
+ readonly createdAt: Date;
22
+ readonly updatedAt: Date;
23
+ readonly finishedAt: Date | null;
24
+ }
25
+ /**
26
+ * A handler's failure. Retried with the queue's backoff unless `retry` is false or the attempts are used up;
27
+ * `retryAfterSeconds` replaces the backoff delay for this attempt (a provider's `Retry-After`).
28
+ */
29
+ export type JobFailure = Err<ErrorCode> & {
30
+ readonly retry?: boolean;
31
+ readonly retryAfterSeconds?: number;
32
+ };
33
+ /** What happened to a job after one attempt. */
34
+ export type JobOutcome = {
35
+ readonly kind: "succeeded";
36
+ } | {
37
+ readonly kind: "retried";
38
+ readonly error: string;
39
+ readonly runAt: Date;
40
+ } | {
41
+ readonly kind: "failed";
42
+ readonly error: string;
43
+ }
44
+ /** The job was taken over (lease ended) or released before the attempt finished; nothing was recorded. */
45
+ | {
46
+ readonly kind: "stale";
47
+ };
48
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEvD,MAAM,MAAM,aAAa,GACrB,sBAAsB,GACtB,oBAAoB,GACpB,oBAAoB,GACpB,gBAAgB,GAChB,sBAAsB,GACtB,oBAAoB,GACpB,sBAAsB,CAAC;AAE3B,eAAO,MAAM,YAAY,qEAAsE,CAAC;AAEhG,MAAM,MAAM,SAAS,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAEtD,mDAAmD;AACnD,MAAM,WAAW,GAAG;IAClB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,mEAAmE;IACnE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,iCAAiC;IACjC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAC;IACrB,QAAQ,CAAC,WAAW,EAAE,IAAI,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,iDAAiD;IACjD,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,SAAS,EAAE,IAAI,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,IAAI,GAAG,IAAI,CAAC;CAClC;AAED;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,GAAG,CAAC,SAAS,CAAC,GAAG;IACxC,QAAQ,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACrC,CAAC;AAEF,gDAAgD;AAChD,MAAM,MAAM,UAAU,GAClB;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;CAAE,GAC9B;IAAE,QAAQ,CAAC,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,IAAI,CAAA;CAAE,GAC1E;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;CAAE;AACrD,0GAA0G;GACxG;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC"}
@@ -0,0 +1,2 @@
1
+ export const JOB_STATUSES = ["pending", "running", "succeeded", "failed", "cancelled"];
2
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAaA,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,SAAS,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,WAAW,CAAU,CAAC"}
@@ -0,0 +1,60 @@
1
+ import type { Clock, ErrorCode, Ok, SoftureConfig } from "@softure-ai/core";
2
+ import type { Queryable } from "@softure-ai/db";
3
+ import type { z } from "zod";
4
+ import type { Job, JobFailure } from "./contract.js";
5
+ /** What a handler receives besides its payload. */
6
+ export interface JobHandlerContext {
7
+ /** The worker's database: not a transaction; open one with `db.transaction` when the work needs it. */
8
+ readonly db: Queryable;
9
+ readonly clock: Clock;
10
+ readonly config: SoftureConfig;
11
+ /** The job as claimed: `attempts` is this attempt's number, starting at 1. */
12
+ readonly job: Job;
13
+ /**
14
+ * Aborted when the job is taken away from this handler: the worker stops and its grace period ended, or the lease
15
+ * could not be extended. Pass it to `fetch` and other cancellable calls; whatever the handler returns afterwards is
16
+ * not recorded.
17
+ */
18
+ readonly signal: AbortSignal;
19
+ }
20
+ /**
21
+ * What a handler returns: `ok()` when the work is done; an `Err` to try again with the queue's backoff (until the
22
+ * attempts are used up); `failJob(code)` to fail for good; `retryJobLater(code, seconds)` to try again after a delay
23
+ * the handler chooses. A throw is retried like an `Err` with the code `jobs.handler_threw` and logged.
24
+ */
25
+ export type JobRunResult = Ok<unknown> | JobFailure;
26
+ export interface JobHandler<TPayload = unknown> {
27
+ /** The schema of the payload; a payload that does not match fails the job with `jobs.invalid_payload`, untried. */
28
+ readonly payload: z.ZodType<TPayload>;
29
+ readonly run: (payload: TPayload, context: JobHandlerContext) => Promise<JobRunResult>;
30
+ }
31
+ /**
32
+ * A handler of any payload type, as a handler map holds it. `run` takes `never` so every `JobHandler<T>` fits; the
33
+ * runner passes it the payload its own schema parsed.
34
+ */
35
+ export interface AnyJobHandler {
36
+ readonly payload: z.ZodType;
37
+ readonly run: (payload: never, context: JobHandlerContext) => Promise<JobRunResult>;
38
+ }
39
+ /** Handlers by queue name. Every queue a worker runs needs one; a handler for an unknown queue is refused. */
40
+ export type JobHandlers = Readonly<Record<string, AnyJobHandler>>;
41
+ /**
42
+ * Defines a handler with its payload type inferred from the schema:
43
+ *
44
+ * export const replyHandler = defineJobHandler({
45
+ * payload: z.object({ messageId: z.string() }),
46
+ * run: async ({ messageId }, { db }) => { ...; return ok(); },
47
+ * });
48
+ */
49
+ export declare function defineJobHandler<TSchema extends z.ZodType>(handler: {
50
+ readonly payload: TSchema;
51
+ readonly run: (payload: z.output<TSchema>, context: JobHandlerContext) => Promise<JobRunResult>;
52
+ }): JobHandler<z.output<TSchema>>;
53
+ /** A failure that is not retried: the job is `failed` at once with this code. */
54
+ export declare function failJob<E extends ErrorCode>(error: E): JobFailure;
55
+ /**
56
+ * A failure retried after `seconds` instead of the queue's backoff (a provider's `Retry-After`). Still counts as an
57
+ * attempt: after the last one the job is `failed`.
58
+ */
59
+ export declare function retryJobLater<E extends ErrorCode>(error: E, seconds: number): JobFailure;
60
+ //# sourceMappingURL=handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.d.ts","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAC5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAChD,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAC7B,OAAO,KAAK,EAAE,GAAG,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAErD,mDAAmD;AACnD,MAAM,WAAW,iBAAiB;IAChC,uGAAuG;IACvG,QAAQ,CAAC,EAAE,EAAE,SAAS,CAAC;IACvB,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC;IAC/B,8EAA8E;IAC9E,QAAQ,CAAC,GAAG,EAAE,GAAG,CAAC;IAClB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAC;CAC9B;AAED;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,UAAU,CAAC;AAEpD,MAAM,WAAW,UAAU,CAAC,QAAQ,GAAG,OAAO;IAC5C,mHAAmH;IACnH,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;IACtC,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CACxF;AAED;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,OAAO,CAAC;IAC5B,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CACrF;AAED,8GAA8G;AAC9G,MAAM,MAAM,WAAW,GAAG,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC,CAAC;AAElE;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,SAAS,CAAC,CAAC,OAAO,EAAE,OAAO,EAAE;IACnE,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,YAAY,CAAC,CAAC;CACjG,GAAG,UAAU,CAAC,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAKhC;AAED,iFAAiF;AACjF,wBAAgB,OAAO,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,CAAC,GAAG,UAAU,CAEjE;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,CAAC,SAAS,SAAS,EAAE,KAAK,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,CAKxF"}
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Defines a handler with its payload type inferred from the schema:
3
+ *
4
+ * export const replyHandler = defineJobHandler({
5
+ * payload: z.object({ messageId: z.string() }),
6
+ * run: async ({ messageId }, { db }) => { ...; return ok(); },
7
+ * });
8
+ */
9
+ export function defineJobHandler(handler) {
10
+ if (typeof handler.run !== "function") {
11
+ throw new Error("defineJobHandler: run must be a function");
12
+ }
13
+ return handler;
14
+ }
15
+ /** A failure that is not retried: the job is `failed` at once with this code. */
16
+ export function failJob(error) {
17
+ return { ok: false, error, retry: false };
18
+ }
19
+ /**
20
+ * A failure retried after `seconds` instead of the queue's backoff (a provider's `Retry-After`). Still counts as an
21
+ * attempt: after the last one the job is `failed`.
22
+ */
23
+ export function retryJobLater(error, seconds) {
24
+ if (!Number.isFinite(seconds) || seconds < 0) {
25
+ throw new RangeError(`retryJobLater: seconds must be a finite number >= 0, got ${String(seconds)}`);
26
+ }
27
+ return { ok: false, error, retryAfterSeconds: seconds };
28
+ }
29
+ //# sourceMappingURL=handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"handlers.js","sourceRoot":"","sources":["../src/handlers.ts"],"names":[],"mappings":"AAgDA;;;;;;;GAOG;AACH,MAAM,UAAU,gBAAgB,CAA4B,OAG3D;IACC,IAAI,OAAO,OAAO,CAAC,GAAG,KAAK,UAAU,EAAE,CAAC;QACtC,MAAM,IAAI,KAAK,CAAC,0CAA0C,CAAC,CAAC;IAC9D,CAAC;IACD,OAAO,OAAwC,CAAC;AAClD,CAAC;AAED,iFAAiF;AACjF,MAAM,UAAU,OAAO,CAAsB,KAAQ;IACnD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;AAC5C,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,aAAa,CAAsB,KAAQ,EAAE,OAAe;IAC1E,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;QAC7C,MAAM,IAAI,UAAU,CAAC,4DAA4D,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACtG,CAAC;IACD,OAAO,EAAE,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,iBAAiB,EAAE,OAAO,EAAE,CAAC;AAC1D,CAAC"}
@@ -0,0 +1,39 @@
1
+ export declare const MODULE_ID = "jobs";
2
+ /**
3
+ * Enables background jobs in `softure.config.ts`: `jobs({ queues: { "ai.reply": { maxAttempts: 5 } } })`. Jobs run
4
+ * in a worker process (`runJobWorkerProcess` from `@softure-ai/jobs/worker`).
5
+ */
6
+ export declare const jobs: import("@softure-ai/core").ModuleFactory<{}, {
7
+ errors: {
8
+ invalid_payload: string;
9
+ handler_threw: string;
10
+ lease_expired: string;
11
+ not_found: string;
12
+ not_cancellable: string;
13
+ not_retryable: string;
14
+ dedup_key_taken: string;
15
+ };
16
+ }, import("zod").ZodObject<{
17
+ queues: import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodObject<{
18
+ maxAttempts: import("zod").ZodDefault<import("zod").ZodNumber>;
19
+ backoff: import("zod").ZodDefault<import("zod").ZodObject<{
20
+ baseSeconds: import("zod").ZodDefault<import("zod").ZodNumber>;
21
+ maxSeconds: import("zod").ZodDefault<import("zod").ZodNumber>;
22
+ }, import("zod/v4/core").$strict>>;
23
+ leaseSeconds: import("zod").ZodDefault<import("zod").ZodNumber>;
24
+ concurrency: import("zod").ZodDefault<import("zod").ZodNumber>;
25
+ }, import("zod/v4/core").$strict>>;
26
+ pollIntervalMs: import("zod").ZodDefault<import("zod").ZodNumber>;
27
+ shutdownGraceMs: import("zod").ZodDefault<import("zod").ZodNumber>;
28
+ retention: import("zod").ZodDefault<import("zod").ZodObject<{
29
+ succeededDays: import("zod").ZodDefault<import("zod").ZodNumber>;
30
+ failedDays: import("zod").ZodDefault<import("zod").ZodNumber>;
31
+ cancelledDays: import("zod").ZodDefault<import("zod").ZodNumber>;
32
+ }, import("zod/v4/core").$strict>>;
33
+ }, import("zod/v4/core").$strict>>;
34
+ export { JOB_STATUSES, type Job, type JobFailure, type JobOutcome, type JobsErrorCode, type JobStatus, } from "./contract.js";
35
+ export { jobsMessages, type JobsMessages } from "./messages/index.js";
36
+ export { jobsOptionsSchema, type JobQueueOptions, type JobsOptions, type JobsOptionsInput } from "./options.js";
37
+ export { defineJobHandler, failJob, retryJobLater, type JobHandler, type JobHandlerContext, type JobHandlers } from "./handlers.js";
38
+ export { jobs as jobsTable } from "./schema.js";
39
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,eAAO,MAAM,SAAS,SAAS,CAAC;AAEhC;;;GAGG;AACH,eAAO,MAAM,IAAI;;;;;;;;;;;;;;;;;;;;;;;;;;;kCAiBf,CAAC;AAEH,OAAO,EACL,YAAY,EACZ,KAAK,GAAG,EACR,KAAK,UAAU,EACf,KAAK,UAAU,EACf,KAAK,aAAa,EAClB,KAAK,SAAS,GACf,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAE,KAAK,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAE,KAAK,eAAe,EAAE,KAAK,WAAW,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChH,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,aAAa,EAAE,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAE,KAAK,WAAW,EAAE,MAAM,eAAe,CAAC;AACpI,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,36 @@
1
+ // Public API of @softure-ai/jobs: the module factory for softure.config.ts, the handler helpers and the types.
2
+ // Database work is in `@softure-ai/jobs/server`, the worker in `@softure-ai/jobs/worker`, the in-process runner for
3
+ // tests in `@softure-ai/jobs/testing`.
4
+ import { defineModule, resolveMigrationsDir } from "@softure-ai/core";
5
+ import { jobsMessages } from "./messages/index.js";
6
+ import { jobsOptionsSchema } from "./options.js";
7
+ import { checkJobsTable } from "./server/health.js";
8
+ export const MODULE_ID = "jobs";
9
+ /**
10
+ * Enables background jobs in `softure.config.ts`: `jobs({ queues: { "ai.reply": { maxAttempts: 5 } } })`. Jobs run
11
+ * in a worker process (`runJobWorkerProcess` from `@softure-ai/jobs/worker`).
12
+ */
13
+ export const jobs = defineModule({
14
+ manifest: {
15
+ id: MODULE_ID,
16
+ version: "0.1.0",
17
+ dependsOn: {},
18
+ dbSchema: "jobs",
19
+ tables: ["jobs"],
20
+ env: [],
21
+ switches: [],
22
+ routes: {},
23
+ mount: [],
24
+ privacy: { exports: false, deletes: false },
25
+ },
26
+ messages: jobsMessages,
27
+ options: jobsOptionsSchema,
28
+ migrations: { dir: resolveMigrationsDir(import.meta.url, "../migrations/") },
29
+ health: checkJobsTable,
30
+ });
31
+ export { JOB_STATUSES, } from "./contract.js";
32
+ export { jobsMessages } from "./messages/index.js";
33
+ export { jobsOptionsSchema } from "./options.js";
34
+ export { defineJobHandler, failJob, retryJobLater } from "./handlers.js";
35
+ export { jobs as jobsTable } from "./schema.js";
36
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,+GAA+G;AAC/G,oHAAoH;AACpH,uCAAuC;AACvC,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACjD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAEpD,MAAM,CAAC,MAAM,SAAS,GAAG,MAAM,CAAC;AAEhC;;;GAGG;AACH,MAAM,CAAC,MAAM,IAAI,GAAG,YAAY,CAAC;IAC/B,QAAQ,EAAE;QACR,EAAE,EAAE,SAAS;QACb,OAAO,EAAE,OAAO;QAChB,SAAS,EAAE,EAAE;QACb,QAAQ,EAAE,MAAM;QAChB,MAAM,EAAE,CAAC,MAAM,CAAC;QAChB,GAAG,EAAE,EAAE;QACP,QAAQ,EAAE,EAAE;QACZ,MAAM,EAAE,EAAE;QACV,KAAK,EAAE,EAAE;QACT,OAAO,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE;KAC5C;IACD,QAAQ,EAAE,YAAY;IACtB,OAAO,EAAE,iBAAiB;IAC1B,UAAU,EAAE,EAAE,GAAG,EAAE,oBAAoB,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE,gBAAgB,CAAC,EAAE;IAC5E,MAAM,EAAE,cAAc;CACvB,CAAC,CAAC;AAEH,OAAO,EACL,YAAY,GAMb,MAAM,eAAe,CAAC;AACvB,OAAO,EAAE,YAAY,EAAqB,MAAM,qBAAqB,CAAC;AACtE,OAAO,EAAE,iBAAiB,EAAiE,MAAM,cAAc,CAAC;AAChH,OAAO,EAAE,gBAAgB,EAAE,OAAO,EAAE,aAAa,EAA6D,MAAM,eAAe,CAAC;AACpI,OAAO,EAAE,IAAI,IAAI,SAAS,EAAE,MAAM,aAAa,CAAC"}
@@ -0,0 +1,12 @@
1
+ export declare const en: {
2
+ errors: {
3
+ invalid_payload: string;
4
+ handler_threw: string;
5
+ lease_expired: string;
6
+ not_found: string;
7
+ not_cancellable: string;
8
+ not_retryable: string;
9
+ dedup_key_taken: string;
10
+ };
11
+ };
12
+ //# sourceMappingURL=en.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/messages/en.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,EAAE;;;;;;;;;;CAUd,CAAC"}
@@ -0,0 +1,12 @@
1
+ export const en = {
2
+ errors: {
3
+ invalid_payload: "The job's data is not valid.",
4
+ handler_threw: "The job failed unexpectedly. It will be tried again.",
5
+ lease_expired: "The job did not finish in time.",
6
+ not_found: "The job does not exist.",
7
+ not_cancellable: "Only a job that has not started can be cancelled.",
8
+ not_retryable: "Only a failed or cancelled job can be run again.",
9
+ dedup_key_taken: "Another job for the same work is already waiting or running.",
10
+ },
11
+ };
12
+ //# sourceMappingURL=en.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"en.js","sourceRoot":"","sources":["../../src/messages/en.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,EAAE,GAAG;IAChB,MAAM,EAAE;QACN,eAAe,EAAE,8BAA8B;QAC/C,aAAa,EAAE,sDAAsD;QACrE,aAAa,EAAE,iCAAiC;QAChD,SAAS,EAAE,yBAAyB;QACpC,eAAe,EAAE,mDAAmD;QACpE,aAAa,EAAE,kDAAkD;QACjE,eAAe,EAAE,8DAA8D;KAChF;CACF,CAAC"}
@@ -0,0 +1,17 @@
1
+ import { en } from "./en.js";
2
+ /** The built-in English dictionary; an app passes partial overrides or its own translation per locale. */
3
+ export declare const jobsMessages: {
4
+ en: {
5
+ errors: {
6
+ invalid_payload: string;
7
+ handler_threw: string;
8
+ lease_expired: string;
9
+ not_found: string;
10
+ not_cancellable: string;
11
+ not_retryable: string;
12
+ dedup_key_taken: string;
13
+ };
14
+ };
15
+ };
16
+ export type JobsMessages = typeof en;
17
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/messages/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAE7B,0GAA0G;AAC1G,eAAO,MAAM,YAAY;;;;;;;;;;;;CAAS,CAAC;AAEnC,MAAM,MAAM,YAAY,GAAG,OAAO,EAAE,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { en } from "./en.js";
2
+ /** The built-in English dictionary; an app passes partial overrides or its own translation per locale. */
3
+ export const jobsMessages = { en };
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/messages/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,EAAE,EAAE,MAAM,SAAS,CAAC;AAE7B,0GAA0G;AAC1G,MAAM,CAAC,MAAM,YAAY,GAAG,EAAE,EAAE,EAAE,CAAC"}
@@ -0,0 +1,23 @@
1
+ import { z } from "zod";
2
+ export declare const jobsOptionsSchema: z.ZodObject<{
3
+ queues: z.ZodRecord<z.ZodString, z.ZodObject<{
4
+ maxAttempts: z.ZodDefault<z.ZodNumber>;
5
+ backoff: z.ZodDefault<z.ZodObject<{
6
+ baseSeconds: z.ZodDefault<z.ZodNumber>;
7
+ maxSeconds: z.ZodDefault<z.ZodNumber>;
8
+ }, z.core.$strict>>;
9
+ leaseSeconds: z.ZodDefault<z.ZodNumber>;
10
+ concurrency: z.ZodDefault<z.ZodNumber>;
11
+ }, z.core.$strict>>;
12
+ pollIntervalMs: z.ZodDefault<z.ZodNumber>;
13
+ shutdownGraceMs: z.ZodDefault<z.ZodNumber>;
14
+ retention: z.ZodDefault<z.ZodObject<{
15
+ succeededDays: z.ZodDefault<z.ZodNumber>;
16
+ failedDays: z.ZodDefault<z.ZodNumber>;
17
+ cancelledDays: z.ZodDefault<z.ZodNumber>;
18
+ }, z.core.$strict>>;
19
+ }, z.core.$strict>;
20
+ export type JobsOptionsInput = z.input<typeof jobsOptionsSchema>;
21
+ export type JobsOptions = z.output<typeof jobsOptionsSchema>;
22
+ export type JobQueueOptions = JobsOptions["queues"][string];
23
+ //# sourceMappingURL=options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"options.d.ts","sourceRoot":"","sources":["../src/options.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAyCxB,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;kBAiB5B,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AACjE,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC7D,MAAM,MAAM,eAAe,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC"}