@warlock.js/queue 5.13.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 (63) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/LICENSE +21 -0
  3. package/README.md +121 -0
  4. package/cjs/define-job-DideGKQK.cjs +468 -0
  5. package/cjs/define-job-DideGKQK.cjs.map +1 -0
  6. package/cjs/index.cjs +180 -0
  7. package/cjs/index.cjs.map +1 -0
  8. package/cjs/notifications/index.cjs +68 -0
  9. package/cjs/notifications/index.cjs.map +1 -0
  10. package/esm/config.d.mts +21 -0
  11. package/esm/config.d.mts.map +1 -0
  12. package/esm/config.mjs +32 -0
  13. package/esm/config.mjs.map +1 -0
  14. package/esm/dashboard.d.mts +47 -0
  15. package/esm/dashboard.d.mts.map +1 -0
  16. package/esm/dashboard.mjs +61 -0
  17. package/esm/dashboard.mjs.map +1 -0
  18. package/esm/define-job.d.mts +24 -0
  19. package/esm/define-job.d.mts.map +1 -0
  20. package/esm/define-job.mjs +102 -0
  21. package/esm/define-job.mjs.map +1 -0
  22. package/esm/duration.d.mts +11 -0
  23. package/esm/duration.d.mts.map +1 -0
  24. package/esm/duration.mjs +28 -0
  25. package/esm/duration.mjs.map +1 -0
  26. package/esm/errors.d.mts +36 -0
  27. package/esm/errors.d.mts.map +1 -0
  28. package/esm/errors.mjs +55 -0
  29. package/esm/errors.mjs.map +1 -0
  30. package/esm/failed-jobs.d.mts +23 -0
  31. package/esm/failed-jobs.d.mts.map +1 -0
  32. package/esm/failed-jobs.mjs +39 -0
  33. package/esm/failed-jobs.mjs.map +1 -0
  34. package/esm/index.d.mts +10 -0
  35. package/esm/index.mjs +10 -0
  36. package/esm/job-registry.mjs +39 -0
  37. package/esm/job-registry.mjs.map +1 -0
  38. package/esm/notifications/index.d.mts +2 -0
  39. package/esm/notifications/index.mjs +3 -0
  40. package/esm/notifications/queue-notification-dispatcher.d.mts +34 -0
  41. package/esm/notifications/queue-notification-dispatcher.d.mts.map +1 -0
  42. package/esm/notifications/queue-notification-dispatcher.mjs +67 -0
  43. package/esm/notifications/queue-notification-dispatcher.mjs.map +1 -0
  44. package/esm/process-job.mjs +32 -0
  45. package/esm/process-job.mjs.map +1 -0
  46. package/esm/queue-connector.d.mts +36 -0
  47. package/esm/queue-connector.d.mts.map +1 -0
  48. package/esm/queue-connector.mjs +73 -0
  49. package/esm/queue-connector.mjs.map +1 -0
  50. package/esm/queue-manager.d.mts +36 -0
  51. package/esm/queue-manager.d.mts.map +1 -0
  52. package/esm/queue-manager.mjs +109 -0
  53. package/esm/queue-manager.mjs.map +1 -0
  54. package/esm/types.d.mts +149 -0
  55. package/esm/types.d.mts.map +1 -0
  56. package/llms-full.txt +229 -0
  57. package/llms.txt +13 -0
  58. package/package.json +70 -0
  59. package/skills/configure-queue/SKILL.md +55 -0
  60. package/skills/define-jobs/SKILL.md +51 -0
  61. package/skills/manage-failed-jobs/SKILL.md +38 -0
  62. package/skills/overview/SKILL.md +26 -0
  63. package/skills/queue-notifications/SKILL.md +33 -0
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: define-jobs
3
+ description: 'Define and dispatch background jobs with `@warlock.js/queue`: `defineJob({ name, queue?, attempts?, backoff?, removeOnComplete?, removeOnFail?, handle(payload, ctx) })` returns a typed job; `job.dispatch(payload, { delay, priority, jobId, attempts, backoff })`; the handler context (`id`, `attempt`, `maxAttempts`, `progress()`, `log()`); reading a job with `job.find(id)`. Triggers: `defineJob`, `.dispatch(`, `ctx.progress`, `JobContext`, `DispatchOptions`, `backoff`, `attempts`, `priority`, `jobId`; "run this in the background", "retry with backoff", "delay a job", "job progress", "idempotent dispatch". Skip: config and workers — `@warlock.js/queue/configure-queue/SKILL.md`; failed jobs — `@warlock.js/queue/manage-failed-jobs/SKILL.md`.'
4
+ ---
5
+
6
+ # Define and dispatch jobs
7
+
8
+ ```ts title="src/app/invoices/jobs/send-invoice.job.ts"
9
+ import { defineJob } from "@warlock.js/queue";
10
+
11
+ export const sendInvoice = defineJob({
12
+ name: "invoices.send", // unique across the app
13
+ attempts: 5, // total tries, default 1
14
+ backoff: { type: "exponential", delay: 2000 }, // or a number = fixed ms
15
+ async handle(payload: { invoiceId: string }, ctx) {
16
+ await ctx.log(`attempt ${ctx.attempt} of ${ctx.maxAttempts}`);
17
+ await ctx.progress(50);
18
+ // throw to fail this attempt; it is retried until attempts run out
19
+ return { sent: true }; // stored as the job result
20
+ },
21
+ });
22
+ ```
23
+
24
+ The job module must be imported by the process that runs workers — in a Warlock app, anything under `src/app` that is loaded at boot.
25
+
26
+ ## Dispatch
27
+
28
+ ```ts
29
+ const { id } = await sendInvoice.dispatch({ invoiceId: "42" });
30
+
31
+ await sendInvoice.dispatch({ invoiceId: "43" }, {
32
+ delay: "10m", // ms number or "500ms" | "30s" | "10m" | "2h" | "1d"
33
+ priority: 1, // 1 runs first; larger numbers later
34
+ jobId: "invoice:43", // a second dispatch with a live id is ignored
35
+ });
36
+ ```
37
+
38
+ Option precedence: dispatch options > `defineJob` > `queue.defaultJobOptions`.
39
+
40
+ ## Read a job
41
+
42
+ ```ts
43
+ const job = await sendInvoice.find(id);
44
+ // { id, name, queue, state, payload, progress, attemptsMade, result, failedReason, createdAt, finishedAt }
45
+ ```
46
+
47
+ ## Rules
48
+
49
+ - A job name with no handler in the worker process fails at once, without retries.
50
+ - Payloads are stored as JSON: pass ids, not model instances.
51
+ - Defining the same name again replaces the handler (this is what keeps dev reloads working), so keep names unique.
@@ -0,0 +1,38 @@
1
+ ---
2
+ name: manage-failed-jobs
3
+ description: 'Inspect and retry failed `@warlock.js/queue` jobs: `failedJobs({ queue, start, end })` returns `FailedJob[]` (`id`, `name`, `payload`, `attemptsMade`, `failedReason`, `stacktrace`, `failedAt`, `retry()`), `retryFailedJob(id, { queue })` (throws `FailedJobNotFoundError`), and the optional bull-board UI via `queueDashboard(server, { basePath, queues })` — needs `@bull-board/api` + `@bull-board/fastify`, loaded only on call, missing ones throw `QueueDashboardDependencyError`. Triggers: `failedJobs`, `retryFailedJob`, `queueDashboard`, `bull-board`; "list failed jobs", "retry a failed job", "queue dashboard", "job admin UI". Skip: defining retries — `@warlock.js/queue/define-jobs/SKILL.md`.'
4
+ ---
5
+
6
+ # Failed jobs
7
+
8
+ A job is failed once it has used every attempt, or failed in a way that cannot be retried (for example, no handler for its name). Failed jobs are kept unless you set `removeOnFail`.
9
+
10
+ ```ts
11
+ import { failedJobs, retryFailedJob } from "@warlock.js/queue";
12
+
13
+ const failed = await failedJobs({ queue: "default", start: 0, end: 49 }); // newest first
14
+
15
+ for (const job of failed) {
16
+ console.log(job.name, job.failedReason, job.attemptsMade);
17
+ }
18
+
19
+ await failed[0]?.retry(); // back to waiting
20
+ await retryFailedJob("invoice:43"); // by id; throws FailedJobNotFoundError if not failed
21
+ ```
22
+
23
+ ## Dashboard (optional)
24
+
25
+ ```sh
26
+ npm install @bull-board/api @bull-board/fastify
27
+ ```
28
+
29
+ ```ts
30
+ import { getHttpServer } from "@warlock.js/core";
31
+ import { queueDashboard } from "@warlock.js/queue";
32
+
33
+ await queueDashboard(getHttpServer(), { basePath: "/admin/queues" });
34
+ ```
35
+
36
+ - Call it before the HTTP server starts listening.
37
+ - Shows every queue that has a job, plus the default queue, unless you pass `queues`.
38
+ - It has no authentication of its own, and it can retry and delete jobs. Protect the path.
@@ -0,0 +1,26 @@
1
+ ---
2
+ name: overview
3
+ description: 'Front door for `@warlock.js/queue` — durable background jobs for Warlock apps on BullMQ + Redis (Redis is required): `defineJob` + `.dispatch()`, retries/backoff, delay, priority, progress, failed-job listing/retry, in-process workers started by `queueConnector()` with graceful shutdown, a BullMQ backend for notifications `.queue()`, and an optional bull-board dashboard. TRIGGER when: importing from `@warlock.js/queue`; "background job", "job queue", "run this later", "retry failed jobs", "BullMQ in Warlock", "worker process". Skip: in-memory batching inside one process — that is core''s `Queue` class (`@warlock.js/core`); cron-style schedules — `@warlock.js/scheduler/overview/SKILL.md`; a known task — load `configure-queue`, `define-jobs`, `manage-failed-jobs` or `queue-notifications`.'
4
+ ---
5
+
6
+ # `@warlock.js/queue` — overview
7
+
8
+ Durable background jobs. Jobs are stored in **Redis** (required), retried on failure, and processed by workers — inside the app process by default.
9
+
10
+ ## Mental model
11
+
12
+ - **Job definition** — `defineJob({ name, handle })` registers a handler by name and returns a typed job.
13
+ - **Dispatch** — `job.dispatch(payload, options)` stores the job in Redis. Any process with the same definition and a worker can run it.
14
+ - **Worker** — one per queue name, started by `queueConnector()` (or `startWorkers()`); routes each job to the handler with its name.
15
+ - **Shutdown** — workers stop taking jobs, active jobs finish (up to `workers.shutdownTimeout`), then connections close.
16
+
17
+ ## Not core's `Queue`
18
+
19
+ `@warlock.js/core` exports `Queue` — an in-memory batcher that flushes items by size or interval inside one process. No storage, no retries, lost on exit. Use `@warlock.js/queue` when work must survive a restart or be retried.
20
+
21
+ ## Skills index
22
+
23
+ - [`configure-queue`](@warlock.js/queue/configure-queue/SKILL.md) — `src/config/queue.ts`, `queueConnector()`, workers on/off, shutdown.
24
+ - [`define-jobs`](@warlock.js/queue/define-jobs/SKILL.md) — `defineJob`, `dispatch` options, retries, progress, `find`.
25
+ - [`manage-failed-jobs`](@warlock.js/queue/manage-failed-jobs/SKILL.md) — `failedJobs`, `retryFailedJob`, the bull-board dashboard.
26
+ - [`queue-notifications`](@warlock.js/queue/queue-notifications/SKILL.md) — `queueNotificationDispatcher()` for notifications `.queue()`.
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: queue-notifications
3
+ description: 'Send `@warlock.js/notifications` `.queue()` deliveries through BullMQ with `queueNotificationDispatcher({ queue?, attempts?, backoff? })` from `@warlock.js/queue/notifications` — put it in the `queue` slot of `src/config/notifications.ts`; the queue workers deliver. Honours `SendOptions.delay` (number = seconds, or "10m"), retries a failing `channel.send`, and fails at once for a channel missing from the worker config. Notifications keeps no queue dependency. Triggers: `queueNotificationDispatcher`, `@warlock.js/queue/notifications`, `NotificationConfig.queue`; "queue notifications with BullMQ", "retry notification delivery", "delayed notification". Skip: the herald backend — `@warlock.js/notifications/queue-notifications/SKILL.md`.'
4
+ ---
5
+
6
+ # Queue notifications with BullMQ
7
+
8
+ ```ts title="src/config/notifications.ts"
9
+ import { type NotificationConfig, mailChannel } from "@warlock.js/notifications";
10
+ import { queueNotificationDispatcher } from "@warlock.js/queue/notifications";
11
+
12
+ const config: NotificationConfig = {
13
+ channels: { mail: mailChannel() },
14
+ queue: queueNotificationDispatcher({ attempts: 3, backoff: { type: "exponential", delay: 5000 } }),
15
+ };
16
+
17
+ export default config;
18
+ ```
19
+
20
+ Also configure the queue itself (`configure-queue`). No separate notifications worker is needed: the delivery is an ordinary job, run by the queue workers.
21
+
22
+ ```ts
23
+ await orderShipped.queue(user, { order }); // enqueued, delivered by a worker
24
+ await orderShipped.queue(user, { order }, { delay: "10m" }); // delivered in 10 minutes
25
+ ```
26
+
27
+ ## Behaviour
28
+
29
+ - The job carries the rendered payload and resolved route; the worker looks the channel up by name in its own notifications config and calls `channel.send`.
30
+ - `delay`: a number is seconds (notifications' convention); a string is a duration.
31
+ - `channel.send` throws → retried per `attempts` / `backoff`, then listed by `failedJobs()`.
32
+ - Channel not configured in the worker → fails at once, no retries.
33
+ - Deliveries run under the job name `warlock.notifications.deliver`.