@warlock.js/queue 5.33.0 → 5.34.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 (52) hide show
  1. package/CHANGELOG.md +9 -54
  2. package/README.md +18 -3
  3. package/cjs/index.cjs +644 -168
  4. package/cjs/index.cjs.map +1 -1
  5. package/esm/dashboard-boot.mjs +1 -0
  6. package/esm/dashboard-boot.mjs.map +1 -1
  7. package/esm/dashboard.d.mts.map +1 -1
  8. package/esm/dashboard.mjs +2 -1
  9. package/esm/dashboard.mjs.map +1 -1
  10. package/esm/define-job.d.mts +0 -1
  11. package/esm/define-job.d.mts.map +1 -1
  12. package/esm/define-job.mjs +11 -64
  13. package/esm/define-job.mjs.map +1 -1
  14. package/esm/driver.d.mts +23 -0
  15. package/esm/driver.d.mts.map +1 -0
  16. package/esm/drivers/bullmq.mjs +132 -0
  17. package/esm/drivers/bullmq.mjs.map +1 -0
  18. package/esm/drivers/local-store.mjs +9 -0
  19. package/esm/drivers/local-store.mjs.map +1 -0
  20. package/esm/drivers/local.mjs +307 -0
  21. package/esm/drivers/local.mjs.map +1 -0
  22. package/esm/drivers/memory-store.mjs +48 -0
  23. package/esm/drivers/memory-store.mjs.map +1 -0
  24. package/esm/drivers/sqlite-store.mjs +89 -0
  25. package/esm/drivers/sqlite-store.mjs.map +1 -0
  26. package/esm/failed-jobs.d.mts +2 -8
  27. package/esm/failed-jobs.d.mts.map +1 -1
  28. package/esm/failed-jobs.mjs +5 -30
  29. package/esm/failed-jobs.mjs.map +1 -1
  30. package/esm/index.d.mts +4 -2
  31. package/esm/index.mjs +2 -1
  32. package/esm/job-options.d.mts +9 -0
  33. package/esm/job-options.d.mts.map +1 -0
  34. package/esm/job-options.mjs +20 -0
  35. package/esm/job-options.mjs.map +1 -0
  36. package/esm/queue-driver.d.mts +6 -0
  37. package/esm/queue-driver.d.mts.map +1 -0
  38. package/esm/queue-driver.mjs +37 -0
  39. package/esm/queue-driver.mjs.map +1 -0
  40. package/esm/queue-manager.d.mts +5 -26
  41. package/esm/queue-manager.d.mts.map +1 -1
  42. package/esm/queue-manager.mjs +28 -84
  43. package/esm/queue-manager.mjs.map +1 -1
  44. package/esm/types.d.mts +18 -5
  45. package/esm/types.d.mts.map +1 -1
  46. package/llms-full.txt +129 -3
  47. package/llms.txt +2 -1
  48. package/package.json +4 -4
  49. package/skills/configure-queue/SKILL.md +1 -1
  50. package/skills/index.md +4 -2
  51. package/skills/local-queues/SKILL.md +121 -0
  52. package/skills/overview/SKILL.md +3 -2
package/llms-full.txt CHANGED
@@ -13,7 +13,7 @@ description: 'Configure `@warlock.js/queue`: the declarative `src/config/queue.t
13
13
 
14
14
  # Configure the queue
15
15
 
16
- Redis is required. The config is declarative; the connector applies it.
16
+ BullMQ is the default and requires Redis. Select `driver: "local"` for durable SQLite without Redis (Node >= 22.15), or `driver: "memory"` for tests; see `local-queues` for working examples, leases, and cancellation. The config is declarative; the connector applies it.
17
17
 
18
18
  ```ts title="src/config/queue.ts"
19
19
  import type { QueueConfig } from "@warlock.js/queue";
@@ -168,6 +168,131 @@ Jobs are not run — call the handler to test it. Opt out with `tests.fakes: { q
168
168
  - Defining the same name again replaces the handler (this is what keeps dev reloads working), so keep names unique.
169
169
 
170
170
 
171
+ ## local-queues `@warlock.js/queue/local-queues/SKILL.md`
172
+
173
+ ---
174
+ name: local-queues
175
+ description: 'Run @warlock.js/queue without Redis: driver: "local" for durable single-host SQLite jobs using node:sqlite on Node >= 22.15, or driver: "memory" for real handler execution in tests. Covers local.path, leases, crash recovery, polling, concurrency, cancellation, graceful shutdown, and at-least-once delivery. Use for local/desktop background jobs, testing workers without Redis, or configuring SQLite queues.'
176
+ ---
177
+
178
+ # Local and memory queues
179
+
180
+ Select a backend in the usual queue config; register `queueConnector()` as usual.
181
+ No extra dependency is needed. Existing config defaults to BullMQ.
182
+
183
+ ```ts title="src/config/queue.ts"
184
+ import type { QueueConfig } from "@warlock.js/queue";
185
+
186
+ export default {
187
+ driver: "local",
188
+ prefix: "my-app",
189
+ local: {
190
+ path: "/var/lib/my-app/jobs.sqlite",
191
+ leaseDuration: 30_000,
192
+ pollInterval: 100,
193
+ },
194
+ workers: { concurrency: 3, shutdownTimeout: 5000 },
195
+ defaultJobOptions: { attempts: 3, backoff: { type: "exponential", delay: 1000 } },
196
+ } satisfies QueueConfig;
197
+ ```
198
+
199
+ Node's built-in SQLite is available without flags from Node 22.13, still
200
+ experimental in Node 22.15. A no-flags import/query was verified on Node
201
+ 22.15.0. Require Node >= 22.15 with `node:sqlite` available; unavailable
202
+ runtimes throw on selecting local storage. Sources:
203
+ [Node changelog](https://nodejs.org/en/blog/release/v22.13.0),
204
+ [Node 22.15 API](https://nodejs.org/download/release/v22.15.0/docs/api/sqlite.html).
205
+ BullMQ/memory do not load SQLite.
206
+
207
+ ## Define, dispatch, inspect, and retry
208
+
209
+ ```ts
210
+ import { defineJob, failedJobs, retryFailedJob } from "@warlock.js/queue";
211
+
212
+ const resize = defineJob({
213
+ name: "images.resize",
214
+ async handle(payload: { imageId: string }, ctx) {
215
+ ctx.signal?.throwIfAborted();
216
+ await ctx.progress(50);
217
+ await ctx.log(`resizing ${payload.imageId}`);
218
+ return { ready: true };
219
+ },
220
+ });
221
+
222
+ const { id } = await resize.dispatch(
223
+ { imageId: "42" },
224
+ {
225
+ jobId: "resize-42",
226
+ delay: "2s",
227
+ priority: 1,
228
+ attempts: 4,
229
+ backoff: { type: "fixed", delay: 500 },
230
+ },
231
+ );
232
+ const snapshot = await resize.find(id);
233
+ console.log(snapshot?.state, snapshot?.result);
234
+ for (const job of await failedJobs()) await job.retry();
235
+ // For an id known to be failed: await retryFailedJob(id);
236
+ ```
237
+
238
+ The same API supports fixed/numeric or exponential backoff, priority/FIFO,
239
+ retention, context propagation, `UnrecoverableJobError`, and failed-job retry.
240
+ Payload/context/result/progress are JSON data. Attempts include the first
241
+ execution; crashed leases re-deliver without consuming a settled attempt.
242
+
243
+ ## Memory tests and scripts
244
+
245
+ ```ts
246
+ import {
247
+ closeQueue,
248
+ defineJob,
249
+ setDispatchInterceptor,
250
+ setQueueConfig,
251
+ startWorkers,
252
+ } from "@warlock.js/queue";
253
+
254
+ setDispatchInterceptor(undefined); // opt out of queueFake for real execution
255
+ setQueueConfig({ driver: "memory", workers: { concurrency: 2 } });
256
+ const double = defineJob({ name: "numbers.double", handle: (value: number) => value * 2 });
257
+ await startWorkers();
258
+ const { id } = await double.dispatch(21);
259
+ try {
260
+ const deadline = Date.now() + 5000;
261
+ while ((await double.find(id))?.state !== "completed") {
262
+ if (Date.now() >= deadline) throw new Error("Job did not complete");
263
+ await new Promise((resolve) => setTimeout(resolve, 10));
264
+ }
265
+ console.log((await double.find(id))?.result); // 42
266
+ } finally {
267
+ await closeQueue({ timeout: 1000 });
268
+ }
269
+ ```
270
+
271
+ Memory storage belongs to one process and is cleared by `closeQueue()`.
272
+ Use temporary SQLite files for restart/crash tests. `workers.enabled: false`
273
+ and process-role gating work for every backend.
274
+
275
+ ## Durability and shutdown rules
276
+
277
+ - Use the same absolute file path, namespace (`prefix`), and job definitions
278
+ in every producer/worker. Default file: `.warlock/queue.sqlite`, relative
279
+ to cwd. Parents are created; use writable local disk, never a network share.
280
+ - SQLite WAL plus FULL synchronous writes persist jobs. Short synchronous
281
+ transactions claim jobs atomically. A unique token fences every renewal,
282
+ progress update, and completion; an expired owner cannot write.
283
+ - The lease defaults to 30 seconds, renews every third of its duration, and
284
+ must be at least 30 ms. Polling defaults to 100 ms, minimum 1 ms. Concurrency
285
+ is per queue per process. Avoid blocking the event loop longer than the lease.
286
+ - Shutdown stops claims immediately, finishes handlers within the timeout,
287
+ then aborts `ctx.signal` and releases remaining leases. Observe cancellation
288
+ in handlers and operations. Await close before changing config or restarting.
289
+ - A crashed process's jobs re-deliver after lease expiry. Delivery is at least
290
+ once: make external side effects idempotent. Expired/released handlers that
291
+ ignore cancellation can keep running even though queue writes are fenced.
292
+ - `getQueue()` and bull-board require BullMQ. Use `find()` / `failedJobs()`
293
+ with local/memory queues. Prefer BullMQ for multi-host/high-volume workloads.
294
+
295
+
171
296
  ## manage-failed-jobs `@warlock.js/queue/manage-failed-jobs/SKILL.md`
172
297
 
173
298
  ---
@@ -249,12 +374,12 @@ description: "@warlock.js/queue overview for orientation and choosing the focuse
249
374
 
250
375
  # `@warlock.js/queue` — overview
251
376
 
252
- Durable background jobs. Jobs are stored in **Redis** (required), retried on failure, and processed by workers — inside the app process by default.
377
+ Durable background jobs. Jobs are stored in **Redis** (default), local SQLite without Redis, or memory for tests, retried on failure, and processed by workers — inside the app process by default.
253
378
 
254
379
  ## Mental model
255
380
 
256
381
  - **Job definition** — `defineJob({ name, handle })` registers a handler by name and returns a typed job.
257
- - **Dispatch** — `job.dispatch(payload, options)` stores the job in Redis. Any process with the same definition and a worker can run it.
382
+ - **Dispatch** — `job.dispatch(payload, options)` stores the job in the configured backend. Any process with the same definition and a worker can run it.
258
383
  - **Worker** — one per queue name, started by `queueConnector()` (or `startWorkers()`); routes each job to the handler with its name.
259
384
  - **Shutdown** — workers stop taking jobs, active jobs finish (up to `workers.shutdownTimeout`), then connections close.
260
385
 
@@ -264,6 +389,7 @@ Durable background jobs. Jobs are stored in **Redis** (required), retried on fai
264
389
 
265
390
  ## Skills index
266
391
 
392
+ - `local-queues` — SQLite/memory configuration, leases, crash recovery, cancellation.
267
393
  - `configure-queue` — `src/config/queue.ts`, `queueConnector()`, workers on/off, shutdown.
268
394
  - `define-jobs` — `defineJob`, `dispatch` options, retries, progress, `find`.
269
395
  - `manage-failed-jobs` — `failedJobs`, `retryFailedJob`, the bull-board dashboard.
package/llms.txt CHANGED
@@ -2,12 +2,13 @@
2
2
 
3
3
  > Package: `@warlock.js/queue`
4
4
 
5
- > Durable background jobs for Warlock.js on BullMQ + Redis: defineJob, dispatch with delay/priority, retries with backoff, progress, failed-job access, in-process workers with graceful shutdown.
5
+ > Background jobs for Warlock.js on BullMQ + Redis, local SQLite, or memory: defineJob, dispatch with delay/priority, retries with backoff, progress, failed-job access, in-process workers with graceful shutdown.
6
6
 
7
7
  ## Skills
8
8
 
9
9
  - [configure-queue](@warlock.js/queue/configure-queue/SKILL.md): Configure `@warlock.js/queue`: the declarative `src/config/queue.ts` (`QueueConfig` — `connection`, `prefix`, `defaultQueue`, `defaultJobOptions`, `workers: { enabled, concurrency, shutdownTimeout }`), registering `queueConnector()` in `warlock.config.ts > connectors`, running a dispatch-only process, and graceful shutdown. Programmatic `setQueueConfig` / `startWorkers` / `closeQueue` for scripts and tests. Triggers: `QueueConfig`, `queueConnector`, `setQueueConfig`, `startWorkers`, `closeQueue`, `workers.enabled`, `shutdownTimeout`; "configure the queue", "connect BullMQ to Redis", "disable workers in the web process", "graceful shutdown of jobs". Skip: writing jobs — the `define-jobs` topic.
10
10
  - [define-jobs](@warlock.js/queue/define-jobs/SKILL.md): 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: `setDispatchInterceptor`, `queueFake`, `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 — the `configure-queue` topic; failed jobs — the `manage-failed-jobs` topic.
11
+ - [local-queues](@warlock.js/queue/local-queues/SKILL.md): Run @warlock.js/queue without Redis: driver: "local" for durable single-host SQLite jobs using node:sqlite on Node >= 22.15, or driver: "memory" for real handler execution in tests. Covers local.path, leases, crash recovery, polling, concurrency, cancellation, graceful shutdown, and at-least-once delivery. Use for local/desktop background jobs, testing workers without Redis, or configuring SQLite queues.
11
12
  - [manage-failed-jobs](@warlock.js/queue/manage-failed-jobs/SKILL.md): 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 bull-board UI — `warlock add bull-board` (requires: ["queue"], so it installs queue first when missing) writes a `dashboard: { enabled, path, middleware }` block to `src/config/queue.ts`; a guard middleware is required in production or boot throws `QueueDashboardUnguardedError`. Manual `queueDashboard(server, { basePath, queues, middleware })` also available — needs `@bull-board/api` + `@bull-board/fastify`, loaded only on call, missing ones throw `QueueDashboardDependencyError`. Triggers: `failedJobs`, `retryFailedJob`, `queueDashboard`, `bull-board`, `dashboard.enabled`, `QueueDashboardUnguardedError`; "list failed jobs", "retry a failed job", "queue dashboard", "job admin UI". Skip: defining retries — the `define-jobs` topic.
12
13
  - [overview](@warlock.js/queue/overview/SKILL.md): @warlock.js/queue overview for orientation and choosing the focused skill for the task.
13
14
  - [queue-notifications](@warlock.js/queue/queue-notifications/SKILL.md): Send `@warlock.js/notifications` `.queue()` deliveries through BullMQ with `bullmqQueue({ queue?, attempts?, backoff? })` from `@warlock.js/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. Triggers: `bullmqQueue`, `NotificationConfig.queue`, `QueuePackageNotInstalledError`; "queue notifications with BullMQ", "retry notification delivery", "delayed notification". Skip: the herald backend — the `queue-notifications` topic of the `warlock-js-notifications` skill.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warlock.js/queue",
3
- "description": "Durable background jobs for Warlock.js on BullMQ + Redis: defineJob, dispatch with delay/priority, retries with backoff, progress, failed-job access, in-process workers with graceful shutdown.",
3
+ "description": "Background jobs for Warlock.js on BullMQ + Redis, local SQLite, or memory: defineJob, dispatch with delay/priority, retries with backoff, progress, failed-job access, in-process workers with graceful shutdown.",
4
4
  "warlock": {
5
5
  "environment": "server"
6
6
  },
@@ -11,8 +11,8 @@
11
11
  "peerDependencies": {
12
12
  "@bull-board/api": "^9.0.0",
13
13
  "@bull-board/fastify": "^9.0.0",
14
- "@warlock.js/core": "5.33.0",
15
- "@warlock.js/logger": "5.33.0"
14
+ "@warlock.js/core": "5.34.0",
15
+ "@warlock.js/logger": "5.34.0"
16
16
  },
17
17
  "peerDependenciesMeta": {
18
18
  "@bull-board/api": {
@@ -38,7 +38,7 @@
38
38
  ],
39
39
  "author": "hassanzohdy",
40
40
  "license": "MIT",
41
- "version": "5.33.0",
41
+ "version": "5.34.0",
42
42
  "main": "./cjs/index.cjs",
43
43
  "module": "./esm/index.mjs",
44
44
  "types": "./esm/index.d.mts",
@@ -5,7 +5,7 @@ description: 'Configure `@warlock.js/queue`: the declarative `src/config/queue.t
5
5
 
6
6
  # Configure the queue
7
7
 
8
- Redis is required. The config is declarative; the connector applies it.
8
+ BullMQ is the default and requires Redis. Select `driver: "local"` for durable SQLite without Redis (Node >= 22.15), or `driver: "memory"` for tests; see `local-queues` for working examples, leases, and cancellation. The config is declarative; the connector applies it.
9
9
 
10
10
  ```ts title="src/config/queue.ts"
11
11
  import type { QueueConfig } from "@warlock.js/queue";
package/skills/index.md CHANGED
@@ -1,10 +1,10 @@
1
1
  ---
2
- description: 'Durable background jobs on BullMQ + Redis: retries, delays, priorities, workers, failed-job recovery. Exports `defineJob`, `queueConnector`, `startWorkers`, `closeQueue`, `getQueue`, `failedJobs`, `retryFailedJob`, `mountQueueDashboard`, `QueueConfig`, `QueueJob`. Use for: "run this in the background", "retry a failed job", "delay a job", "survive restarts", "job dashboard". Not this package: cron or recurring schedules → @warlock.js/scheduler; broker pub/sub messaging → @warlock.js/herald; core `Queue` is an in-memory batcher.'
2
+ description: 'Background jobs on BullMQ + Redis, local SQLite, or memory: retries, delays, priorities, workers, failed-job recovery. Exports `defineJob`, `queueConnector`, `startWorkers`, `closeQueue`, `getQueue`, `failedJobs`, `retryFailedJob`, `mountQueueDashboard`, `QueueConfig`, `QueueJob`. Use for: "run this in the background", "retry a failed job", "delay a job", "survive restarts", "job dashboard". Not this package: cron or recurring schedules → @warlock.js/scheduler; broker pub/sub messaging → @warlock.js/herald; core `Queue` is an in-memory batcher.'
3
3
  ---
4
4
 
5
5
  # @warlock.js/queue
6
6
 
7
- Jobs are stored in Redis (required; not bundled), retried on failure, and run by workers, in the app process by default. `defineJob({ name, handle })` registers a handler and returns a typed job; `job.dispatch(payload, options)` enqueues it. Any process with the same definition and a worker can run it. Server-only.
7
+ Jobs are stored in Redis (default; not bundled), local SQLite without Redis, or memory for tests, retried on failure, and run by workers, in the app process by default. `defineJob({ name, handle })` registers a handler and returns a typed job; `job.dispatch(payload, options)` enqueues it. Any process with the same definition and a worker can run it. Server-only.
8
8
 
9
9
  ## The 80% path
10
10
 
@@ -22,3 +22,5 @@ Jobs are stored in Redis (required; not bundled), retried on failure, and run by
22
22
  - Guard the dashboard with middleware; an unguarded one is refused on purpose.
23
23
  - Core `Queue` is an in-memory batcher, lost on exit; use this package when work must survive.
24
24
  - Do not value-import from client code.
25
+
26
+ Use the `local-queues` topic for `driver: "local"` / `"memory"`, Node requirements, leases, crash recovery, and cancellation.
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: local-queues
3
+ description: 'Run @warlock.js/queue without Redis: driver: "local" for durable single-host SQLite jobs using node:sqlite on Node >= 22.15, or driver: "memory" for real handler execution in tests. Covers local.path, leases, crash recovery, polling, concurrency, cancellation, graceful shutdown, and at-least-once delivery. Use for local/desktop background jobs, testing workers without Redis, or configuring SQLite queues.'
4
+ ---
5
+
6
+ # Local and memory queues
7
+
8
+ Select a backend in the usual queue config; register `queueConnector()` as usual.
9
+ No extra dependency is needed. Existing config defaults to BullMQ.
10
+
11
+ ```ts title="src/config/queue.ts"
12
+ import type { QueueConfig } from "@warlock.js/queue";
13
+
14
+ export default {
15
+ driver: "local",
16
+ prefix: "my-app",
17
+ local: {
18
+ path: "/var/lib/my-app/jobs.sqlite",
19
+ leaseDuration: 30_000,
20
+ pollInterval: 100,
21
+ },
22
+ workers: { concurrency: 3, shutdownTimeout: 5000 },
23
+ defaultJobOptions: { attempts: 3, backoff: { type: "exponential", delay: 1000 } },
24
+ } satisfies QueueConfig;
25
+ ```
26
+
27
+ Node's built-in SQLite is available without flags from Node 22.13, still
28
+ experimental in Node 22.15. A no-flags import/query was verified on Node
29
+ 22.15.0. Require Node >= 22.15 with `node:sqlite` available; unavailable
30
+ runtimes throw on selecting local storage. Sources:
31
+ [Node changelog](https://nodejs.org/en/blog/release/v22.13.0),
32
+ [Node 22.15 API](https://nodejs.org/download/release/v22.15.0/docs/api/sqlite.html).
33
+ BullMQ/memory do not load SQLite.
34
+
35
+ ## Define, dispatch, inspect, and retry
36
+
37
+ ```ts
38
+ import { defineJob, failedJobs, retryFailedJob } from "@warlock.js/queue";
39
+
40
+ const resize = defineJob({
41
+ name: "images.resize",
42
+ async handle(payload: { imageId: string }, ctx) {
43
+ ctx.signal?.throwIfAborted();
44
+ await ctx.progress(50);
45
+ await ctx.log(`resizing ${payload.imageId}`);
46
+ return { ready: true };
47
+ },
48
+ });
49
+
50
+ const { id } = await resize.dispatch(
51
+ { imageId: "42" },
52
+ {
53
+ jobId: "resize-42",
54
+ delay: "2s",
55
+ priority: 1,
56
+ attempts: 4,
57
+ backoff: { type: "fixed", delay: 500 },
58
+ },
59
+ );
60
+ const snapshot = await resize.find(id);
61
+ console.log(snapshot?.state, snapshot?.result);
62
+ for (const job of await failedJobs()) await job.retry();
63
+ // For an id known to be failed: await retryFailedJob(id);
64
+ ```
65
+
66
+ The same API supports fixed/numeric or exponential backoff, priority/FIFO,
67
+ retention, context propagation, `UnrecoverableJobError`, and failed-job retry.
68
+ Payload/context/result/progress are JSON data. Attempts include the first
69
+ execution; crashed leases re-deliver without consuming a settled attempt.
70
+
71
+ ## Memory tests and scripts
72
+
73
+ ```ts
74
+ import {
75
+ closeQueue,
76
+ defineJob,
77
+ setDispatchInterceptor,
78
+ setQueueConfig,
79
+ startWorkers,
80
+ } from "@warlock.js/queue";
81
+
82
+ setDispatchInterceptor(undefined); // opt out of queueFake for real execution
83
+ setQueueConfig({ driver: "memory", workers: { concurrency: 2 } });
84
+ const double = defineJob({ name: "numbers.double", handle: (value: number) => value * 2 });
85
+ await startWorkers();
86
+ const { id } = await double.dispatch(21);
87
+ try {
88
+ const deadline = Date.now() + 5000;
89
+ while ((await double.find(id))?.state !== "completed") {
90
+ if (Date.now() >= deadline) throw new Error("Job did not complete");
91
+ await new Promise((resolve) => setTimeout(resolve, 10));
92
+ }
93
+ console.log((await double.find(id))?.result); // 42
94
+ } finally {
95
+ await closeQueue({ timeout: 1000 });
96
+ }
97
+ ```
98
+
99
+ Memory storage belongs to one process and is cleared by `closeQueue()`.
100
+ Use temporary SQLite files for restart/crash tests. `workers.enabled: false`
101
+ and process-role gating work for every backend.
102
+
103
+ ## Durability and shutdown rules
104
+
105
+ - Use the same absolute file path, namespace (`prefix`), and job definitions
106
+ in every producer/worker. Default file: `.warlock/queue.sqlite`, relative
107
+ to cwd. Parents are created; use writable local disk, never a network share.
108
+ - SQLite WAL plus FULL synchronous writes persist jobs. Short synchronous
109
+ transactions claim jobs atomically. A unique token fences every renewal,
110
+ progress update, and completion; an expired owner cannot write.
111
+ - The lease defaults to 30 seconds, renews every third of its duration, and
112
+ must be at least 30 ms. Polling defaults to 100 ms, minimum 1 ms. Concurrency
113
+ is per queue per process. Avoid blocking the event loop longer than the lease.
114
+ - Shutdown stops claims immediately, finishes handlers within the timeout,
115
+ then aborts `ctx.signal` and releases remaining leases. Observe cancellation
116
+ in handlers and operations. Await close before changing config or restarting.
117
+ - A crashed process's jobs re-deliver after lease expiry. Delivery is at least
118
+ once: make external side effects idempotent. Expired/released handlers that
119
+ ignore cancellation can keep running even though queue writes are fenced.
120
+ - `getQueue()` and bull-board require BullMQ. Use `find()` / `failedJobs()`
121
+ with local/memory queues. Prefer BullMQ for multi-host/high-volume workloads.
@@ -5,12 +5,12 @@ description: "@warlock.js/queue overview for orientation and choosing the focuse
5
5
 
6
6
  # `@warlock.js/queue` — overview
7
7
 
8
- Durable background jobs. Jobs are stored in **Redis** (required), retried on failure, and processed by workers — inside the app process by default.
8
+ Durable background jobs. Jobs are stored in **Redis** (default), local SQLite without Redis, or memory for tests, retried on failure, and processed by workers — inside the app process by default.
9
9
 
10
10
  ## Mental model
11
11
 
12
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.
13
+ - **Dispatch** — `job.dispatch(payload, options)` stores the job in the configured backend. Any process with the same definition and a worker can run it.
14
14
  - **Worker** — one per queue name, started by `queueConnector()` (or `startWorkers()`); routes each job to the handler with its name.
15
15
  - **Shutdown** — workers stop taking jobs, active jobs finish (up to `workers.shutdownTimeout`), then connections close.
16
16
 
@@ -20,6 +20,7 @@ Durable background jobs. Jobs are stored in **Redis** (required), retried on fai
20
20
 
21
21
  ## Skills index
22
22
 
23
+ - `local-queues` — SQLite/memory configuration, leases, crash recovery, cancellation.
23
24
  - `configure-queue` — `src/config/queue.ts`, `queueConnector()`, workers on/off, shutdown.
24
25
  - `define-jobs` — `defineJob`, `dispatch` options, retries, progress, `find`.
25
26
  - `manage-failed-jobs` — `failedJobs`, `retryFailedJob`, the bull-board dashboard.