@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,273 @@
1
+ # Patterns
2
+
3
+ ## Table of Contents
4
+
5
+ - [Process Step Jobs](#process-step-jobs)
6
+ - [Idempotent Jobs](#idempotent-jobs)
7
+ - [Throttle Jobs](#throttle-jobs)
8
+ - [Manual Rate Limiting](#manual-rate-limiting)
9
+ - [Static Rate Limiting](#static-rate-limiting)
10
+ - [Global Concurrency](#global-concurrency)
11
+ - [Failing Fast When Redis Is Down](#failing-fast-when-redis-is-down)
12
+ - [Redis Cluster](#redis-cluster)
13
+
14
+ ## Process Step Jobs
15
+
16
+ Break processor logic into resumable steps. Save step state in `job.data` so retries resume from the correct step.
17
+
18
+ ```ts
19
+ import { Worker } from "bullmq";
20
+
21
+ enum Step {
22
+ Initial,
23
+ Second,
24
+ Finish,
25
+ }
26
+
27
+ const worker = new Worker(
28
+ "queueName",
29
+ async (job) => {
30
+ let step = job.data.step;
31
+ while (step !== Step.Finish) {
32
+ switch (step) {
33
+ case Step.Initial: {
34
+ await doInitialStepStuff();
35
+ await job.updateData({ step: Step.Second });
36
+ step = Step.Second;
37
+ break;
38
+ }
39
+ case Step.Second: {
40
+ await doSecondStepStuff();
41
+ await job.updateData({ step: Step.Finish });
42
+ step = Step.Finish;
43
+ return Step.Finish;
44
+ }
45
+ default:
46
+ throw new Error("invalid step");
47
+ }
48
+ }
49
+ },
50
+ { connection },
51
+ );
52
+ ```
53
+
54
+ ### Delaying Mid-Process
55
+
56
+ Use `moveToDelayed` + `DelayedError` to pause a job and resume later:
57
+
58
+ ```ts
59
+ import { DelayedError, Worker } from "bullmq";
60
+
61
+ const worker = new Worker(
62
+ "queueName",
63
+ async (job, token) => {
64
+ let step = job.data.step;
65
+ switch (step) {
66
+ case Step.Initial: {
67
+ await doStuff();
68
+ await job.moveToDelayed(Date.now() + 5000, token);
69
+ await job.updateData({ step: Step.Second });
70
+ throw new DelayedError(); // signals worker to release the job
71
+ }
72
+ case Step.Second: {
73
+ // resumes here after 5s delay
74
+ return doMoreStuff();
75
+ }
76
+ }
77
+ },
78
+ { connection },
79
+ );
80
+ ```
81
+
82
+ ### Waiting for Dynamic Children
83
+
84
+ Add children at runtime, then wait for them with `moveToWaitingChildren`:
85
+
86
+ ```ts
87
+ import { WaitingChildrenError, Worker } from "bullmq";
88
+
89
+ const worker = new Worker(
90
+ "parentQueue",
91
+ async (job, token) => {
92
+ let step = job.data.step;
93
+ switch (step) {
94
+ case Step.Initial: {
95
+ // Add child dynamically
96
+ await childQueue.add(
97
+ "child-1",
98
+ { foo: "bar" },
99
+ {
100
+ parent: { id: job.id!, queue: job.queueQualifiedName },
101
+ },
102
+ );
103
+ await job.updateData({ step: Step.WaitForChildren });
104
+ step = Step.WaitForChildren;
105
+ break;
106
+ }
107
+ case Step.WaitForChildren: {
108
+ const shouldWait = await job.moveToWaitingChildren(token!);
109
+ if (shouldWait) {
110
+ throw new WaitingChildrenError();
111
+ }
112
+ // All children done — continue
113
+ return "done";
114
+ }
115
+ }
116
+ },
117
+ { connection },
118
+ );
119
+ ```
120
+
121
+ ### Chaining Flows at Runtime
122
+
123
+ ```ts
124
+ import { FlowProducer, WaitingChildrenError, Worker } from "bullmq";
125
+
126
+ const flow = new FlowProducer({ connection });
127
+
128
+ const worker = new Worker(
129
+ "parentQueue",
130
+ async (job, token) => {
131
+ // Add a full flow as children
132
+ await flow.add({
133
+ name: "child-job",
134
+ queueName: "childQueue",
135
+ data: {},
136
+ children: [
137
+ { name: "gc1", data: {}, queueName: "grandchildQueue" },
138
+ { name: "gc2", data: {}, queueName: "grandchildQueue" },
139
+ ],
140
+ opts: {
141
+ parent: { id: job.id!, queue: job.queueQualifiedName },
142
+ },
143
+ });
144
+
145
+ const shouldWait = await job.moveToWaitingChildren(token!);
146
+ if (shouldWait) throw new WaitingChildrenError();
147
+ return "all children done";
148
+ },
149
+ { connection },
150
+ );
151
+ ```
152
+
153
+ **Note:** `DelayedError`, `WaitingChildrenError`, and `RateLimitError` do NOT increment `attemptsMade`. Use `maxStartedAttempts` to limit how many times a job can start processing.
154
+
155
+ ## Idempotent Jobs
156
+
157
+ Use `jobId` to prevent duplicate processing:
158
+
159
+ ```ts
160
+ await queue.add(
161
+ "process-order",
162
+ { orderId: 123 },
163
+ {
164
+ jobId: "order-123",
165
+ },
166
+ );
167
+ // Second add with same jobId is silently ignored
168
+ await queue.add(
169
+ "process-order",
170
+ { orderId: 123 },
171
+ {
172
+ jobId: "order-123",
173
+ },
174
+ );
175
+ ```
176
+
177
+ ## Throttle Jobs
178
+
179
+ Use deduplication throttle mode to limit how often a job type runs:
180
+
181
+ ```ts
182
+ await queue.add("sync", data, {
183
+ deduplication: { id: "sync-user-42", ttl: 60000 },
184
+ });
185
+ // Any add with same dedup ID within 60s is ignored
186
+ ```
187
+
188
+ ## Manual Rate Limiting
189
+
190
+ Handle external API rate limits (e.g., 429 responses):
191
+
192
+ ```ts
193
+ import { Worker } from "bullmq";
194
+
195
+ const worker = new Worker(
196
+ "api-calls",
197
+ async () => {
198
+ const [isRateLimited, duration] = await callExternalApi();
199
+ if (isRateLimited) {
200
+ await worker.rateLimit(duration);
201
+ throw Worker.RateLimitError();
202
+ }
203
+ },
204
+ {
205
+ connection,
206
+ limiter: { max: 1, duration: 500 }, // REQUIRED: must set limiter for rateLimit to work
207
+ },
208
+ );
209
+ ```
210
+
211
+ ## Static Rate Limiting
212
+
213
+ ```ts
214
+ const worker = new Worker("painter", async (job) => paintCar(job), {
215
+ limiter: {
216
+ max: 10, // max 10 jobs
217
+ duration: 1000, // per 1 second
218
+ },
219
+ });
220
+ ```
221
+
222
+ The rate limiter is **global** across all workers for the queue.
223
+
224
+ ## Global Concurrency
225
+
226
+ Limit to 1 job processing at a time across all workers:
227
+
228
+ ```ts
229
+ await queue.setGlobalConcurrency(1);
230
+ ```
231
+
232
+ ## Failing Fast When Redis Is Down
233
+
234
+ For producer services (HTTP endpoints), disable offline queue so calls fail immediately:
235
+
236
+ ```ts
237
+ import { Redis } from "ioredis";
238
+
239
+ const connection = new Redis({
240
+ maxRetriesPerRequest: 1,
241
+ enableOfflineQueue: false,
242
+ });
243
+
244
+ const queue = new Queue("q", { connection });
245
+
246
+ try {
247
+ await queue.add("job", data);
248
+ } catch (err) {
249
+ // Redis is down — return 503 to the client
250
+ }
251
+ ```
252
+
253
+ ## Redis Cluster
254
+
255
+ ```ts
256
+ import { Cluster } from "ioredis";
257
+
258
+ const connection = new Cluster(
259
+ [
260
+ { host: "node1", port: 6380 },
261
+ { host: "node2", port: 6380 },
262
+ ],
263
+ {
264
+ natMap: {
265
+ /* ... */
266
+ },
267
+ },
268
+ );
269
+
270
+ const queue = new Queue("q", { connection, prefix: "{myprefix}" });
271
+ ```
272
+
273
+ Use `{hashTag}` prefix to ensure all keys for a queue land on the same slot.
@@ -0,0 +1,308 @@
1
+ # Production Guide
2
+
3
+ ## Table of Contents
4
+
5
+ - [Redis Configuration](#redis-configuration)
6
+ - [Connection Strategy](#connection-strategy)
7
+ - [Graceful Shutdown](#graceful-shutdown)
8
+ - [Retry Strategies](#retry-strategies)
9
+ - [Stalled Jobs](#stalled-jobs)
10
+ - [Sandboxed Processors](#sandboxed-processors)
11
+ - [Concurrency](#concurrency)
12
+ - [Auto-Job Removal](#auto-job-removal)
13
+ - [Error Handling](#error-handling)
14
+ - [Monitoring with Prometheus](#monitoring-with-prometheus)
15
+ - [Data Security](#data-security)
16
+ - [Troubleshooting Checklist](#troubleshooting-checklist)
17
+
18
+ ## Redis Configuration
19
+
20
+ ### Persistence
21
+
22
+ Enable AOF (Append Only File) for durability. 1-second fsync is sufficient for most apps:
23
+
24
+ ```
25
+ # redis.conf
26
+ appendonly yes
27
+ appendfsync everysec
28
+ ```
29
+
30
+ ### Memory Policy
31
+
32
+ **Critical:** BullMQ will malfunction if Redis evicts keys.
33
+
34
+ ```
35
+ maxmemory-policy noeviction
36
+ ```
37
+
38
+ ## Connection Strategy
39
+
40
+ Different needs for producers vs consumers:
41
+
42
+ ### Producers (Queue class) — fail fast
43
+
44
+ ```ts
45
+ import { Redis } from "ioredis";
46
+
47
+ const producerConn = new Redis({
48
+ host: "redis.example.com",
49
+ port: 6379,
50
+ maxRetriesPerRequest: 1, // fail quickly on disconnect
51
+ enableOfflineQueue: false, // don't queue commands when offline
52
+ });
53
+
54
+ const queue = new Queue("q", { connection: producerConn });
55
+ ```
56
+
57
+ ### Consumers (Worker class) — wait indefinitely
58
+
59
+ ```ts
60
+ const workerConn = new Redis({
61
+ host: "redis.example.com",
62
+ port: 6379,
63
+ maxRetriesPerRequest: null, // REQUIRED for workers — retry forever
64
+ // enableOfflineQueue: true // default, keeps queuing commands
65
+ });
66
+
67
+ const worker = new Worker("q", processor, { connection: workerConn });
68
+ ```
69
+
70
+ ### Retry Strategy
71
+
72
+ BullMQ default (for internally created connections):
73
+
74
+ ```ts
75
+ retryStrategy: (times) => Math.max(Math.min(Math.exp(times), 20000), 1000);
76
+ // Exponential backoff: min 1s, max 20s
77
+ ```
78
+
79
+ ## Graceful Shutdown
80
+
81
+ ```ts
82
+ const gracefulShutdown = async (signal: string) => {
83
+ console.log(`Received ${signal}, closing worker...`);
84
+ await worker.close();
85
+ process.exit(0);
86
+ };
87
+
88
+ process.on("SIGINT", () => gracefulShutdown("SIGINT"));
89
+ process.on("SIGTERM", () => gracefulShutdown("SIGTERM"));
90
+ ```
91
+
92
+ If the worker doesn't close before the process is killed, in-progress jobs become **stalled** and are re-processed after ~30 seconds by another worker.
93
+
94
+ ## Retry Strategies
95
+
96
+ ### Fixed Backoff
97
+
98
+ Retry after a constant delay:
99
+
100
+ ```ts
101
+ await queue.add("job", data, {
102
+ attempts: 3,
103
+ backoff: { type: "fixed", delay: 1000 }, // retry every 1s
104
+ // With jitter (0-1, randomizes between delay*jitter and delay):
105
+ // backoff: { type: "fixed", delay: 1000, jitter: 0.5 },
106
+ });
107
+ ```
108
+
109
+ ### Exponential Backoff
110
+
111
+ Retry after `2^(attempts-1) * delay` ms:
112
+
113
+ ```ts
114
+ await queue.add("job", data, {
115
+ attempts: 5,
116
+ backoff: { type: "exponential", delay: 1000 },
117
+ // Attempt 1: 1s, Attempt 2: 2s, Attempt 3: 4s, Attempt 4: 8s, Attempt 5: 16s
118
+ });
119
+ ```
120
+
121
+ ### Custom Backoff
122
+
123
+ Define on the worker:
124
+
125
+ ```ts
126
+ const worker = new Worker("q", processor, {
127
+ settings: {
128
+ backoffStrategy: (attemptsMade, type, err, job) => {
129
+ if (type === "linear") return attemptsMade * 1000;
130
+ if (type === "custom-exp") return Math.pow(2, attemptsMade) * 500;
131
+ throw new Error("unknown type");
132
+ },
133
+ },
134
+ });
135
+
136
+ // Use in job options
137
+ await queue.add("job", data, {
138
+ attempts: 5,
139
+ backoff: { type: "linear" },
140
+ });
141
+ ```
142
+
143
+ **Special return values from `backoffStrategy`:**
144
+
145
+ - `0` → move to end of waiting list (or back to prioritized if priority > 0)
146
+ - `-1` → don't retry, move to failed immediately
147
+
148
+ ### Default Job Options
149
+
150
+ Set retry defaults for all jobs in a queue:
151
+
152
+ ```ts
153
+ const queue = new Queue("q", {
154
+ defaultJobOptions: {
155
+ attempts: 3,
156
+ backoff: { type: "exponential", delay: 1000 },
157
+ removeOnComplete: { count: 200 },
158
+ removeOnFail: { count: 1000 },
159
+ },
160
+ });
161
+ ```
162
+
163
+ ## Stalled Jobs
164
+
165
+ A job becomes stalled when the worker holding it doesn't renew its lock within `stalledInterval` (default: 30s). Causes:
166
+
167
+ - CPU-intensive synchronous code blocking the event loop
168
+ - Process crash or kill without graceful shutdown
169
+
170
+ Stalled jobs are automatically moved back to waiting. After `maxStalledCount` (default: 1), they move to failed.
171
+
172
+ **Prevention:**
173
+
174
+ - Avoid blocking the event loop — use `async`/`await`, break CPU work into chunks
175
+ - Use sandboxed processors for CPU-intensive tasks
176
+ - Increase `stalledInterval` if jobs legitimately need long uninterrupted processing
177
+
178
+ ## Sandboxed Processors
179
+
180
+ Run processors in separate child processes for CPU-intensive work:
181
+
182
+ ```ts
183
+ // processor.ts (separate file)
184
+ import { SandboxedJob } from "bullmq";
185
+
186
+ export default async function (job: SandboxedJob) {
187
+ // CPU-intensive work — runs in a child process
188
+ return heavyComputation(job.data);
189
+ }
190
+
191
+ // main.ts
192
+ const worker = new Worker("q", "./processor.ts", { connection });
193
+ ```
194
+
195
+ Benefits: isolates CPU-heavy work, prevents stalled jobs, true parallelism with `concurrency > 1`.
196
+
197
+ ## Concurrency
198
+
199
+ ### Local Concurrency (single worker)
200
+
201
+ ```ts
202
+ const worker = new Worker("q", processor, { concurrency: 50 });
203
+ ```
204
+
205
+ Only effective for I/O-bound (async) work. For CPU-bound work, use sandboxed processors.
206
+
207
+ ### Multiple Workers (recommended for high availability)
208
+
209
+ Run workers across multiple processes/machines. Combine with local concurrency:
210
+
211
+ ```ts
212
+ // Each machine runs:
213
+ const worker = new Worker("q", processor, { concurrency: 10 });
214
+ // 5 machines × 10 concurrency = 50 jobs processed concurrently
215
+ ```
216
+
217
+ ### Global Concurrency
218
+
219
+ Limit total concurrent processing across ALL workers:
220
+
221
+ ```ts
222
+ await queue.setGlobalConcurrency(1); // only 1 job at a time, globally
223
+ ```
224
+
225
+ ### Global Rate Limit
226
+
227
+ ```ts
228
+ const queue = new Queue("q");
229
+ // Must configure on the queue, not the worker
230
+ await queue.setGlobalConcurrency(5); // combined with worker limiter
231
+
232
+ // Check if rate limited
233
+ const ttl = await queue.getRateLimitTtl(100);
234
+ if (ttl > 0) console.log("Queue is rate limited");
235
+
236
+ // Reset rate limit
237
+ await queue.removeRateLimitKey();
238
+ ```
239
+
240
+ ## Auto-Job Removal
241
+
242
+ By default, completed and failed jobs are kept forever. Always configure removal:
243
+
244
+ ```ts
245
+ const queue = new Queue("q", {
246
+ defaultJobOptions: {
247
+ removeOnComplete: { count: 100, age: 3600 }, // keep 100 or 1 hour
248
+ removeOnFail: { count: 5000 }, // keep last 5000 failures
249
+ },
250
+ });
251
+ ```
252
+
253
+ ## Error Handling
254
+
255
+ Always attach error handlers to prevent unhandled exceptions:
256
+
257
+ ```ts
258
+ worker.on("error", (err) => {
259
+ logger.error(err, "Worker error");
260
+ });
261
+
262
+ queue.on("error", (err) => {
263
+ logger.error(err, "Queue error");
264
+ });
265
+
266
+ // Global safety net
267
+ process.on("uncaughtException", (err) => {
268
+ logger.error(err, "Uncaught exception");
269
+ });
270
+
271
+ process.on("unhandledRejection", (reason, promise) => {
272
+ logger.error({ promise, reason }, "Unhandled rejection");
273
+ });
274
+ ```
275
+
276
+ **Important:** processor functions must throw `Error` objects (not strings or other types).
277
+
278
+ ## Monitoring with Prometheus
279
+
280
+ ```ts
281
+ import { Queue, MetricsTime } from "bullmq";
282
+
283
+ const queue = new Queue("q", {
284
+ metrics: {
285
+ maxDataPoints: MetricsTime.ONE_WEEK * 2, // collect 2 weeks of data
286
+ },
287
+ });
288
+
289
+ // Retrieve metrics
290
+ const completedMetrics = await queue.getMetrics("completed");
291
+ // { meta: { count, prevCount }, data: number[] }
292
+
293
+ const failedMetrics = await queue.getMetrics("failed");
294
+ ```
295
+
296
+ ## Data Security
297
+
298
+ Job data is stored in **plain text** in Redis. Never store sensitive data (passwords, tokens, PII) in job payloads. If unavoidable, encrypt sensitive fields before adding to the queue.
299
+
300
+ ## Troubleshooting Checklist
301
+
302
+ 1. **Jobs not processing?** Check worker is connected to the same queue name with matching `prefix`.
303
+ 2. **Stalled jobs?** CPU blocking the event loop — use sandboxed processors or break up work.
304
+ 3. **Missing error handler?** Worker may silently stop processing — always attach `worker.on("error", ...)`.
305
+ 4. **Redis memory growing?** Configure `removeOnComplete`/`removeOnFail`, check `maxmemory-policy`.
306
+ 5. **Delayed jobs not firing?** Ensure Redis is reachable and no clock skew issues.
307
+ 6. **Rate limiting not working?** `limiter` must be set on the worker options for `rateLimit()` to function.
308
+ 7. **ioredis keyPrefix?** Don't use it — use BullMQ's `prefix` option instead.
@@ -0,0 +1,58 @@
1
+ ---
2
+ name: composition-patterns
3
+ description: React composition patterns that scale. Use when refactoring components with boolean prop proliferation, building flexible component libraries, or designing reusable APIs. Triggers on tasks involving compound components, render props, context providers, or component architecture. Includes React 19 API changes.
4
+ ---
5
+
6
+ # React Composition Patterns
7
+
8
+ Composition patterns for building flexible, maintainable React components. Avoid
9
+ boolean prop proliferation by using compound components, lifting state, and
10
+ composing internals. These patterns make codebases easier for both humans and AI
11
+ agents to work with as they scale.
12
+
13
+ ## When NOT to Use
14
+
15
+ Skip these patterns when: fewer than 3 props, simple variants, or single-use components.
16
+
17
+ ## When to Apply
18
+
19
+ Reference these guidelines when:
20
+
21
+ - Refactoring components with many boolean props
22
+ - Building reusable component libraries
23
+ - Designing flexible component APIs
24
+ - Reviewing component architecture
25
+ - Working with compound components or context providers
26
+
27
+ ## Rule Categories by Priority
28
+
29
+ | Priority | Category | Impact | Prefix |
30
+ | -------- | ----------------------- | ------ | --------------- |
31
+ | 1 | Component Architecture | HIGH | `architecture-` |
32
+ | 2 | State Management | MEDIUM | `state-` |
33
+ | 3 | Implementation Patterns | MEDIUM | `patterns-` |
34
+ | 4 | React 19 APIs | MEDIUM | `react19-` |
35
+
36
+ ## Quick Reference
37
+
38
+ ### 1. Component Architecture (HIGH)
39
+
40
+ - **Avoid boolean props** — Don't add boolean props like `isThread`, `isEditing`, `isDMThread` to customize behavior. Each boolean doubles possible states. Use composition instead — see [references/architecture-avoid-boolean-props.md](references/architecture-avoid-boolean-props.md)
41
+ - **Compound components** — Structure complex components with shared context so each subcomponent accesses state via context, not props — see [references/architecture-compound-components.md](references/architecture-compound-components.md)
42
+
43
+ ### 2. State Management (MEDIUM)
44
+
45
+ - **Decouple implementation** — Provider is the only place that knows how state is managed — see [references/state-decouple-implementation.md](references/state-decouple-implementation.md)
46
+ - **Context interface** — Define generic interface with `state`, `actions`, `meta` for dependency injection — see [references/state-context-interface.md](references/state-context-interface.md)
47
+ - **Lift state** — Move state into provider components for sibling access — see [references/state-lift-state.md](references/state-lift-state.md)
48
+
49
+ ### 3. Implementation Patterns (MEDIUM)
50
+
51
+ - **Explicit variants** — Create explicit variant components instead of boolean modes — see [references/patterns-explicit-variants.md](references/patterns-explicit-variants.md)
52
+ - **Children over render props** — Use `children` for composition instead of `renderX` props — see [references/patterns-children-over-render-props.md](references/patterns-children-over-render-props.md)
53
+
54
+ ### 4. React 19 APIs (MEDIUM)
55
+
56
+ > **React 19+ only.** Skip this section if using React 18 or earlier.
57
+
58
+ - **No forwardRef** — Don't use `forwardRef`; pass `ref` as a regular prop. Use `use()` instead of `useContext()` — see [references/react19-no-forwardref.md](references/react19-no-forwardref.md)