@jgamaraalv/ts-dev-kit 1.0.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 (117) hide show
  1. package/.claude-plugin/marketplace.json +24 -0
  2. package/.claude-plugin/plugin.json +24 -0
  3. package/CHANGELOG.md +24 -0
  4. package/LICENSE +21 -0
  5. package/README.md +128 -0
  6. package/agents/accessibility-pro.md +139 -0
  7. package/agents/api-builder.md +110 -0
  8. package/agents/code-reviewer.md +190 -0
  9. package/agents/database-expert.md +138 -0
  10. package/agents/debugger.md +241 -0
  11. package/agents/docker-expert.md +51 -0
  12. package/agents/multi-agent-coordinator.md +378 -0
  13. package/agents/nextjs-expert.md +136 -0
  14. package/agents/performance-engineer.md +138 -0
  15. package/agents/playwright-expert.md +126 -0
  16. package/agents/react-specialist.md +97 -0
  17. package/agents/security-scanner.md +105 -0
  18. package/agents/test-generator.md +221 -0
  19. package/agents/typescript-pro.md +253 -0
  20. package/agents/ux-optimizer.md +93 -0
  21. package/docs/rules/orchestration.md.template +126 -0
  22. package/package.json +28 -0
  23. package/skills/bullmq/SKILL.md +225 -0
  24. package/skills/bullmq/references/flows-and-schedulers.md +186 -0
  25. package/skills/bullmq/references/job-types-and-options.md +163 -0
  26. package/skills/bullmq/references/patterns.md +273 -0
  27. package/skills/bullmq/references/production.md +308 -0
  28. package/skills/composition-patterns/SKILL.md +58 -0
  29. package/skills/composition-patterns/references/architecture-avoid-boolean-props.md +87 -0
  30. package/skills/composition-patterns/references/architecture-compound-components.md +107 -0
  31. package/skills/composition-patterns/references/patterns-children-over-render-props.md +77 -0
  32. package/skills/composition-patterns/references/patterns-explicit-variants.md +87 -0
  33. package/skills/composition-patterns/references/react19-no-forwardref.md +37 -0
  34. package/skills/composition-patterns/references/state-context-interface.md +194 -0
  35. package/skills/composition-patterns/references/state-decouple-implementation.md +96 -0
  36. package/skills/composition-patterns/references/state-lift-state.md +126 -0
  37. package/skills/conventional-commits/SKILL.md +148 -0
  38. package/skills/docker/SKILL.md +55 -0
  39. package/skills/docker/references/compose-configs.md +95 -0
  40. package/skills/docker/references/monorepo-dockerfile.md +111 -0
  41. package/skills/drizzle-pg/SKILL.md +202 -0
  42. package/skills/drizzle-pg/references/advanced.md +299 -0
  43. package/skills/drizzle-pg/references/migrations.md +214 -0
  44. package/skills/drizzle-pg/references/queries.md +321 -0
  45. package/skills/drizzle-pg/references/relations.md +272 -0
  46. package/skills/drizzle-pg/references/schema-pg.md +256 -0
  47. package/skills/drizzle-pg/references/sql-operator.md +215 -0
  48. package/skills/fastify-best-practices/SKILL.md +143 -0
  49. package/skills/fastify-best-practices/references/hooks-and-lifecycle.md +122 -0
  50. package/skills/fastify-best-practices/references/plugins-and-encapsulation.md +137 -0
  51. package/skills/fastify-best-practices/references/request-reply-errors.md +189 -0
  52. package/skills/fastify-best-practices/references/routes-and-handlers.md +134 -0
  53. package/skills/fastify-best-practices/references/server-and-options.md +127 -0
  54. package/skills/fastify-best-practices/references/typescript-and-logging.md +223 -0
  55. package/skills/fastify-best-practices/references/validation-and-serialization.md +190 -0
  56. package/skills/ioredis/SKILL.md +51 -0
  57. package/skills/ioredis/references/advanced-patterns.md +312 -0
  58. package/skills/ioredis/references/cluster-sentinel.md +280 -0
  59. package/skills/ioredis/references/connection-options.md +187 -0
  60. package/skills/ioredis/references/core-api.md +179 -0
  61. package/skills/nextjs-best-practices/SKILL.md +194 -0
  62. package/skills/nextjs-best-practices/references/async-patterns.md +84 -0
  63. package/skills/nextjs-best-practices/references/bundling.md +192 -0
  64. package/skills/nextjs-best-practices/references/data-patterns.md +310 -0
  65. package/skills/nextjs-best-practices/references/debug-tricks.md +127 -0
  66. package/skills/nextjs-best-practices/references/directives.md +74 -0
  67. package/skills/nextjs-best-practices/references/error-handling.md +237 -0
  68. package/skills/nextjs-best-practices/references/file-conventions.md +152 -0
  69. package/skills/nextjs-best-practices/references/font.md +175 -0
  70. package/skills/nextjs-best-practices/references/functions.md +116 -0
  71. package/skills/nextjs-best-practices/references/hydration-error.md +86 -0
  72. package/skills/nextjs-best-practices/references/image.md +184 -0
  73. package/skills/nextjs-best-practices/references/metadata.md +305 -0
  74. package/skills/nextjs-best-practices/references/parallel-routes.md +299 -0
  75. package/skills/nextjs-best-practices/references/route-handlers.md +154 -0
  76. package/skills/nextjs-best-practices/references/rsc-boundaries.md +168 -0
  77. package/skills/nextjs-best-practices/references/runtime-selection.md +40 -0
  78. package/skills/nextjs-best-practices/references/scripts.md +148 -0
  79. package/skills/nextjs-best-practices/references/self-hosting.md +210 -0
  80. package/skills/nextjs-best-practices/references/suspense-boundaries.md +67 -0
  81. package/skills/owasp-security-review/SKILL.md +98 -0
  82. package/skills/owasp-security-review/references/a01-broken-access-control.md +78 -0
  83. package/skills/owasp-security-review/references/a02-security-misconfiguration.md +81 -0
  84. package/skills/owasp-security-review/references/a03-supply-chain-failures.md +65 -0
  85. package/skills/owasp-security-review/references/a04-cryptographic-failures.md +82 -0
  86. package/skills/owasp-security-review/references/a05-injection.md +106 -0
  87. package/skills/owasp-security-review/references/a06-insecure-design.md +76 -0
  88. package/skills/owasp-security-review/references/a07-authentication-failures.md +83 -0
  89. package/skills/owasp-security-review/references/a08-integrity-failures.md +72 -0
  90. package/skills/owasp-security-review/references/a09-logging-alerting-failures.md +76 -0
  91. package/skills/owasp-security-review/references/a10-exceptional-conditions.md +131 -0
  92. package/skills/postgresql/SKILL.md +50 -0
  93. package/skills/postgresql/references/ddl-schema.md +300 -0
  94. package/skills/postgresql/references/indexes.md +257 -0
  95. package/skills/postgresql/references/jsonb.md +261 -0
  96. package/skills/postgresql/references/performance.md +291 -0
  97. package/skills/postgresql/references/psql-cli.md +153 -0
  98. package/skills/postgresql/references/queries.md +287 -0
  99. package/skills/postgresql/references/transactions.md +280 -0
  100. package/skills/react-best-practices/SKILL.md +110 -0
  101. package/skills/react-best-practices/references/advanced-patterns.md +91 -0
  102. package/skills/react-best-practices/references/async-patterns.md +233 -0
  103. package/skills/react-best-practices/references/bundle-optimization.md +201 -0
  104. package/skills/react-best-practices/references/client-patterns.md +178 -0
  105. package/skills/react-best-practices/references/js-performance.md +210 -0
  106. package/skills/react-best-practices/references/rendering-performance.md +209 -0
  107. package/skills/react-best-practices/references/rerender-optimization.md +316 -0
  108. package/skills/react-best-practices/references/server-performance.md +274 -0
  109. package/skills/service-worker/SKILL.md +195 -0
  110. package/skills/service-worker/references/api-reference.md +114 -0
  111. package/skills/service-worker/references/caching-strategies.md +202 -0
  112. package/skills/service-worker/references/push-and-sync.md +261 -0
  113. package/skills/typescript-conventions/SKILL.md +51 -0
  114. package/skills/ui-ux-guidelines/SKILL.md +105 -0
  115. package/skills/ui-ux-guidelines/references/accessibility-and-interaction.md +74 -0
  116. package/skills/ui-ux-guidelines/references/forms-content-checklist.md +126 -0
  117. package/skills/ui-ux-guidelines/references/layout-typography-animation.md +95 -0
@@ -0,0 +1,225 @@
1
+ ---
2
+ name: bullmq
3
+ description: "BullMQ queue system reference for Redis-backed job queues, workers, flows, and schedulers. Use when: (1) creating queues and workers with BullMQ, (2) adding jobs (delayed, prioritized, repeatable, deduplicated), (3) setting up FlowProducer parent-child job hierarchies, (4) configuring retry strategies, rate limiting, or concurrency, (5) implementing job schedulers with cron/interval patterns, (6) preparing BullMQ for production (graceful shutdown, Redis config, monitoring), or (7) debugging stalled jobs or connection issues"
4
+ ---
5
+
6
+ # BullMQ
7
+
8
+ Redis-backed queue system for Node.js. Four core classes: `Queue`, `Worker`, `QueueEvents`, `FlowProducer`.
9
+
10
+ ## Table of Contents
11
+
12
+ - [Install](#install)
13
+ - [Quick Start](#quick-start)
14
+ - [Connections](#connections)
15
+ - [Queue](#queue)
16
+ - [Worker](#worker)
17
+ - [TypeScript Generics](#typescript-generics)
18
+ - [Events](#events)
19
+ - [Job Lifecycle States](#job-lifecycle-states)
20
+ - [Advanced Topics](#advanced-topics)
21
+
22
+ ## Install
23
+
24
+ `yarn add bullmq` — requires Redis 5.0+ with `maxmemory-policy=noeviction`.
25
+
26
+ ## Quick Start
27
+
28
+ ```ts
29
+ import { Queue, Worker, QueueEvents } from "bullmq";
30
+
31
+ // --- Producer ---
32
+ const queue = new Queue("my-queue", {
33
+ connection: { host: "localhost", port: 6379 },
34
+ });
35
+
36
+ await queue.add("job-name", { foo: "bar" });
37
+
38
+ // --- Consumer ---
39
+ const worker = new Worker(
40
+ "my-queue",
41
+ async (job) => {
42
+ // process job
43
+ await job.updateProgress(50);
44
+ return { result: "done" };
45
+ },
46
+ { connection: { host: "localhost", port: 6379 } },
47
+ );
48
+
49
+ worker.on("completed", (job, returnvalue) => {
50
+ console.log(`${job.id} completed with`, returnvalue);
51
+ });
52
+
53
+ worker.on("failed", (job, err) => {
54
+ console.error(`${job.id} failed with`, err.message);
55
+ });
56
+
57
+ // IMPORTANT: always attach an error handler
58
+ worker.on("error", (err) => {
59
+ console.error(err);
60
+ });
61
+
62
+ // --- Global event listener (all workers) ---
63
+ const queueEvents = new QueueEvents("my-queue", {
64
+ connection: { host: "localhost", port: 6379 },
65
+ });
66
+
67
+ queueEvents.on("completed", ({ jobId, returnvalue }) => {
68
+ console.log(`Job ${jobId} completed`);
69
+ });
70
+
71
+ queueEvents.on("failed", ({ jobId, failedReason }) => {
72
+ console.error(`Job ${jobId} failed: ${failedReason}`);
73
+ });
74
+ ```
75
+
76
+ ## Connections
77
+
78
+ BullMQ uses ioredis internally. Pass `connection` options or an existing ioredis instance.
79
+
80
+ ```ts
81
+ import { Queue, Worker } from "bullmq";
82
+ import { Redis } from "ioredis";
83
+
84
+ // Option 1: connection config (new connection per instance)
85
+ const queue = new Queue("q", {
86
+ connection: { host: "redis.example.com", port: 6379 },
87
+ });
88
+
89
+ // Option 2: reuse ioredis instance (Queue and multiple Queues can share)
90
+ const connection = new Redis();
91
+ const q1 = new Queue("q1", { connection });
92
+ const q2 = new Queue("q2", { connection });
93
+
94
+ // Option 3: reuse for Workers (BullMQ internally duplicates for blocking)
95
+ const workerConn = new Redis({ maxRetriesPerRequest: null });
96
+ const w1 = new Worker("q1", async (job) => {}, { connection: workerConn });
97
+ ```
98
+
99
+ **Critical rules:**
100
+
101
+ - Workers REQUIRE `maxRetriesPerRequest: null` on the ioredis instance. BullMQ enforces this and will warn/throw if not set.
102
+ - Do NOT use ioredis `keyPrefix` option — use BullMQ's `prefix` option instead.
103
+ - `QueueEvents` cannot share connections (uses blocking Redis commands).
104
+ - Redis MUST have `maxmemory-policy=noeviction`.
105
+
106
+ ## Queue
107
+
108
+ ```ts
109
+ const queue = new Queue("paint", { connection });
110
+
111
+ // Add a job
112
+ await queue.add("job-name", { color: "red" });
113
+
114
+ // Add with options
115
+ await queue.add(
116
+ "job-name",
117
+ { color: "blue" },
118
+ {
119
+ delay: 5000, // wait 5s before processing
120
+ priority: 1, // lower = higher priority (0 is highest, max 2^21)
121
+ attempts: 3, // retry up to 3 times
122
+ backoff: { type: "exponential", delay: 1000 },
123
+ removeOnComplete: true, // or { count: 100 } to keep last 100
124
+ removeOnFail: 1000, // keep last 1000 failed jobs
125
+ },
126
+ );
127
+
128
+ // Add bulk
129
+ await queue.addBulk([
130
+ { name: "job1", data: { x: 1 } },
131
+ { name: "job2", data: { x: 2 }, opts: { priority: 1 } },
132
+ ]);
133
+
134
+ // Queue operations
135
+ await queue.pause();
136
+ await queue.resume();
137
+ await queue.obliterate({ force: true }); // remove all data
138
+ await queue.close();
139
+ ```
140
+
141
+ ## Worker
142
+
143
+ ```ts
144
+ const worker = new Worker<MyData, MyReturn>(
145
+ "paint",
146
+ async (job) => {
147
+ await job.updateProgress(42);
148
+ return { cost: 100 };
149
+ },
150
+ {
151
+ connection,
152
+ concurrency: 5, // process 5 jobs concurrently
153
+ autorun: false, // don't start immediately
154
+ },
155
+ );
156
+
157
+ worker.run(); // start when ready
158
+
159
+ // Update concurrency at runtime
160
+ worker.concurrency = 10;
161
+ ```
162
+
163
+ **Processor receives 3 args:** `(job, token?, signal?)` — signal is an `AbortSignal` for cancellation support.
164
+
165
+ ## TypeScript Generics
166
+
167
+ ```ts
168
+ interface JobData {
169
+ color: string;
170
+ }
171
+ interface JobReturn {
172
+ cost: number;
173
+ }
174
+
175
+ const queue = new Queue<JobData, JobReturn>("paint");
176
+ const worker = new Worker<JobData, JobReturn>("paint", async (job) => {
177
+ // job.data is typed as JobData
178
+ return { cost: 100 }; // must match JobReturn
179
+ });
180
+ ```
181
+
182
+ ## Events
183
+
184
+ **Worker events** (local to that worker instance):
185
+
186
+ | Event | Callback signature |
187
+ | ----------- | ------------------------------------ |
188
+ | `completed` | `(job, returnvalue)` |
189
+ | `failed` | `(job \| undefined, error, prev)` |
190
+ | `progress` | `(job, progress: number \| object)` |
191
+ | `drained` | `()` — queue is empty |
192
+ | `error` | `(error)` — MUST attach this handler |
193
+
194
+ **QueueEvents** (global, all workers, uses Redis Streams):
195
+
196
+ | Event | Callback signature |
197
+ | -------------- | ------------------------------------------------- |
198
+ | `completed` | `({ jobId, returnvalue })` |
199
+ | `failed` | `({ jobId, failedReason })` |
200
+ | `progress` | `({ jobId, data })` |
201
+ | `waiting` | `({ jobId })` |
202
+ | `active` | `({ jobId, prev })` |
203
+ | `delayed` | `({ jobId, delay })` |
204
+ | `deduplicated` | `({ jobId, deduplicationId, deduplicatedJobId })` |
205
+
206
+ Event stream is auto-trimmed (~10,000 events). Configure via `streams.events.maxLen`.
207
+
208
+ ## Job Lifecycle States
209
+
210
+ ```
211
+ add() → wait / prioritized / delayed
212
+
213
+ active → completed
214
+
215
+ failed → (retry) → wait/delayed
216
+ ```
217
+
218
+ With FlowProducer: jobs can also be in `waiting-children` state until all children complete.
219
+
220
+ ## Advanced Topics
221
+
222
+ - **Job types and options** (delayed, prioritized, deduplication, repeatable): See [references/job-types-and-options.md](references/job-types-and-options.md)
223
+ - **Flows and schedulers** (FlowProducer, parent-child, job schedulers, cron): See [references/flows-and-schedulers.md](references/flows-and-schedulers.md)
224
+ - **Patterns** (step jobs, idempotent, throttle, manual rate-limit): See [references/patterns.md](references/patterns.md)
225
+ - **Production** (shutdown, Redis config, retries, backoff, monitoring): See [references/production.md](references/production.md)
@@ -0,0 +1,186 @@
1
+ # Flows and Job Schedulers
2
+
3
+ ## Table of Contents
4
+
5
+ - [FlowProducer — Parent-Child Relationships](#flowproducer--parent-child-relationships)
6
+ - [Job Schedulers (v5.16.0+)](#job-schedulers-v5160)
7
+
8
+ ## FlowProducer — Parent-Child Relationships
9
+
10
+ A parent job waits in `waiting-children` state until all its children complete. Children can be in different queues.
11
+
12
+ ```ts
13
+ import { FlowProducer } from "bullmq";
14
+
15
+ const flowProducer = new FlowProducer({ connection });
16
+
17
+ const flow = await flowProducer.add({
18
+ name: "renovate-interior",
19
+ queueName: "renovate",
20
+ children: [
21
+ { name: "paint", data: { place: "ceiling" }, queueName: "steps" },
22
+ { name: "paint", data: { place: "walls" }, queueName: "steps" },
23
+ { name: "fix", data: { place: "floor" }, queueName: "steps" },
24
+ ],
25
+ });
26
+ ```
27
+
28
+ This atomically adds 4 jobs. The parent (`renovate`) processes only after all 3 children complete.
29
+
30
+ ### Accessing Children Results
31
+
32
+ ```ts
33
+ const renovateWorker = new Worker("renovate", async (job) => {
34
+ const childrenValues = await job.getChildrenValues();
35
+ // { "bull:steps:paint:1": 2500, "bull:steps:fix:3": 1750 }
36
+ const total = Object.values(childrenValues).reduce((a, b) => a + b, 0);
37
+ return total;
38
+ });
39
+ ```
40
+
41
+ ### Serial Chains (Deep Hierarchies)
42
+
43
+ Nest children to create sequential execution:
44
+
45
+ ```ts
46
+ await flowProducer.add({
47
+ name: "car",
48
+ data: { step: "engine" },
49
+ queueName: "assembly",
50
+ children: [
51
+ {
52
+ name: "car",
53
+ data: { step: "wheels" },
54
+ queueName: "assembly",
55
+ children: [{ name: "car", data: { step: "chassis" }, queueName: "assembly" }],
56
+ },
57
+ ],
58
+ });
59
+ // Processing order: chassis → wheels → engine
60
+ ```
61
+
62
+ ### Flow Getters
63
+
64
+ ```ts
65
+ // Get direct children
66
+ const deps = await job.getDependencies();
67
+
68
+ // Paginated by type
69
+ const { processed, nextProcessedCursor } = await job.getDependencies({
70
+ processed: { count: 5, cursor: 0 },
71
+ });
72
+
73
+ // Count by type
74
+ const { failed, ignored, processed, unprocessed } = await job.getDependenciesCount();
75
+
76
+ // Check state
77
+ const state = await job.getState(); // "waiting-children"
78
+ ```
79
+
80
+ ### Queue Options for Flows
81
+
82
+ ```ts
83
+ await flowProducer.add(flowDefinition, {
84
+ queuesOptions: {
85
+ assembly: {
86
+ defaultJobOptions: { removeOnComplete: true },
87
+ },
88
+ },
89
+ });
90
+ ```
91
+
92
+ ### Flow Job Removal
93
+
94
+ - Removing a **parent** removes all its children.
95
+ - Removing a **child** removes the parent's dependency on it. If it was the last child, the parent completes.
96
+ - If any job to be removed is **locked** (being processed), the removal throws an exception.
97
+
98
+ ```ts
99
+ await job.remove();
100
+ // or
101
+ await queue.remove(jobId);
102
+ ```
103
+
104
+ ### Add Flows in Bulk
105
+
106
+ ```ts
107
+ await flowProducer.addBulk([
108
+ { name: "flow1", queueName: "q", children: [...] },
109
+ { name: "flow2", queueName: "q", children: [...] },
110
+ ]);
111
+ ```
112
+
113
+ ## Job Schedulers (v5.16.0+)
114
+
115
+ Job Schedulers replace legacy "repeatable jobs". They act as factories producing jobs on a schedule.
116
+
117
+ ### Fixed Interval
118
+
119
+ ```ts
120
+ const firstJob = await queue.upsertJobScheduler("my-scheduler", {
121
+ every: 1000, // every 1 second
122
+ });
123
+ ```
124
+
125
+ ### Cron Pattern
126
+
127
+ ```ts
128
+ await queue.upsertJobScheduler(
129
+ "daily-report",
130
+ { pattern: "0 15 3 * * *" }, // daily at 3:15 AM
131
+ {
132
+ name: "generate-report",
133
+ data: { type: "daily" },
134
+ opts: { attempts: 5, backoff: 3, removeOnFail: 1000 },
135
+ },
136
+ );
137
+ ```
138
+
139
+ ### Key Behaviors
140
+
141
+ - **Upsert semantics**: calling `upsertJobScheduler` with the same ID updates the existing scheduler (no duplicates).
142
+ - **Production rate**: a new job is only produced when the previous one starts processing. If the queue is busy, jobs may arrive less frequently than the interval.
143
+ - **Job ID**: jobs get auto-generated IDs — you cannot use custom job IDs. Use `name` to discriminate.
144
+ - There is always one associated job in "delayed" status.
145
+
146
+ ### Manage Schedulers
147
+
148
+ ```ts
149
+ // List all schedulers
150
+ const schedulers = await queue.getJobSchedulers(0, 100);
151
+
152
+ // Remove a scheduler
153
+ await queue.removeJobScheduler("my-scheduler");
154
+ ```
155
+
156
+ ### Repeat Strategies
157
+
158
+ Built-in: `every` (fixed interval) and `pattern` (cron). You can define custom strategies:
159
+
160
+ ```ts
161
+ const queue = new Queue("q", {
162
+ settings: {
163
+ repeatStrategy: (millis, opts) => {
164
+ // Return next execution timestamp
165
+ return Date.now() + customDelay;
166
+ },
167
+ },
168
+ });
169
+ ```
170
+
171
+ ### Repeat Options
172
+
173
+ ```ts
174
+ await queue.upsertJobScheduler("scheduler-id", {
175
+ pattern: "0 * * * *", // cron expression
176
+ // or
177
+ every: 5000, // fixed ms interval
178
+
179
+ // Optional:
180
+ limit: 100, // max times to repeat
181
+ startDate: new Date("2025-01-01"),
182
+ endDate: new Date("2025-12-31"),
183
+ tz: "America/Sao_Paulo", // timezone for cron
184
+ immediately: true, // produce first job immediately
185
+ });
186
+ ```
@@ -0,0 +1,163 @@
1
+ # Job Types and Options
2
+
3
+ ## Table of Contents
4
+
5
+ - [Delayed Jobs](#delayed-jobs)
6
+ - [Prioritized Jobs](#prioritized-jobs)
7
+ - [FIFO and LIFO](#fifo-and-lifo)
8
+ - [Custom Job IDs](#custom-job-ids)
9
+ - [Job Data](#job-data)
10
+ - [Deduplication](#deduplication)
11
+ - [Repeatable Jobs (Legacy)](#repeatable-jobs-legacy)
12
+ - [Auto-Removal Options](#auto-removal-options)
13
+ - [Returning Job Data](#returning-job-data)
14
+
15
+ ## Delayed Jobs
16
+
17
+ ```ts
18
+ await queue.add("email", { to: "user@example.com" }, { delay: 60000 }); // 1 min delay
19
+ ```
20
+
21
+ Jobs wait in the "delayed" set, then move to "wait" (or "prioritized") when the delay expires. No `QueueScheduler` needed since BullMQ 2.0.
22
+
23
+ ## Prioritized Jobs
24
+
25
+ Priority range: `0` (highest) to `2^21` (lowest). Default is `0`. Follows Unix nice convention — higher number = lower priority.
26
+
27
+ ```ts
28
+ await queue.add("urgent", data, { priority: 1 });
29
+ await queue.add("normal", data, { priority: 100 });
30
+ await queue.add("low", data, { priority: 2097152 }); // 2^21
31
+ ```
32
+
33
+ Prioritized jobs go into a dedicated set and are picked before regular "wait" jobs.
34
+
35
+ ## FIFO and LIFO
36
+
37
+ Default is FIFO. Use `{ lifo: true }` to place jobs at the head of the waiting list.
38
+
39
+ ## Custom Job IDs
40
+
41
+ ```ts
42
+ await queue.add("job", data, { jobId: "my-unique-id" });
43
+ ```
44
+
45
+ If a job with the same ID exists and is not yet removed, the `add` call is ignored. Useful for idempotency. Do NOT use colons `:` in job IDs (used as internal separator).
46
+
47
+ ## Job Data
48
+
49
+ Keep job data small — it's stored in Redis. For large payloads, store data externally and pass a reference.
50
+
51
+ ```ts
52
+ // Update data during processing
53
+ await job.updateData({ ...job.data, step: "next" });
54
+
55
+ // Get current data
56
+ const data = job.data;
57
+ ```
58
+
59
+ ## Deduplication
60
+
61
+ Three modes, all use a `deduplication` option with an `id`:
62
+
63
+ ### Simple Mode — deduplicate until job completes/fails
64
+
65
+ ```ts
66
+ await queue.add("process", data, {
67
+ deduplication: { id: "unique-key" },
68
+ });
69
+ ```
70
+
71
+ Subsequent adds with same dedup ID are ignored while the job is active.
72
+
73
+ ### Throttle Mode — deduplicate for a TTL period
74
+
75
+ ```ts
76
+ await queue.add("process", data, {
77
+ deduplication: { id: "unique-key", ttl: 5000 },
78
+ });
79
+ ```
80
+
81
+ Ignores duplicate adds for 5 seconds regardless of job state.
82
+
83
+ ### Debounce Mode — replace previous job, reset TTL
84
+
85
+ ```ts
86
+ await queue.add("process", data, {
87
+ deduplication: { id: "unique-key", ttl: 5000, extend: true, replace: true },
88
+ delay: 5000,
89
+ });
90
+ ```
91
+
92
+ Replaces the previous pending job with new data. Only the last added job is processed.
93
+
94
+ ### Deduplication API
95
+
96
+ ```ts
97
+ // Check which job holds the dedup key
98
+ const jobId = await queue.getDeduplicationJobId("unique-key");
99
+
100
+ // Remove dedup key manually
101
+ await queue.removeDeduplicationKey("unique-key");
102
+ // Or from the job itself
103
+ const removed = await job.removeDeduplicationKey();
104
+ ```
105
+
106
+ Listen for dedup events:
107
+
108
+ ```ts
109
+ queueEvents.on("deduplicated", ({ jobId, deduplicationId, deduplicatedJobId }) => {
110
+ console.log(`Job ${deduplicatedJobId} was deduplicated`);
111
+ });
112
+ ```
113
+
114
+ ## Repeatable Jobs (Legacy)
115
+
116
+ Prefer Job Schedulers (`upsertJobScheduler`) for new code. Legacy `repeat` option still works:
117
+
118
+ ```ts
119
+ await queue.add("report", data, {
120
+ repeat: { every: 60000 }, // every 60s
121
+ // or
122
+ repeat: { pattern: "0 * * * *" }, // cron: every hour
123
+ });
124
+ ```
125
+
126
+ ## Auto-Removal Options
127
+
128
+ Control how many completed/failed jobs to keep:
129
+
130
+ ```ts
131
+ await queue.add("job", data, {
132
+ // removeOnComplete: true, // Option A: remove immediately
133
+ // removeOnComplete: { count: 100 }, // Option B: keep last 100
134
+ removeOnComplete: { age: 3600 }, // Option C: keep for 1 hour
135
+ removeOnFail: { count: 500 }, // keep last 500 failed
136
+ });
137
+ ```
138
+
139
+ Set defaults for all jobs:
140
+
141
+ ```ts
142
+ const queue = new Queue("q", {
143
+ defaultJobOptions: {
144
+ removeOnComplete: { count: 200 },
145
+ removeOnFail: { count: 1000 },
146
+ },
147
+ });
148
+ ```
149
+
150
+ ## Returning Job Data
151
+
152
+ The value returned from the processor is stored as the job's `returnvalue`:
153
+
154
+ ```ts
155
+ // In the worker
156
+ async (job) => {
157
+ return { processedAt: Date.now() };
158
+ };
159
+
160
+ // Retrieve later
161
+ const job = await Job.fromId(queue, jobId);
162
+ console.log(job.returnvalue); // { processedAt: ... }
163
+ ```