/bunqueue
Use bunqueue job queue library - Queue, Worker, Bunqueue (simple mode), FlowProducer, cron, DLQ, embedded and TCP modes
$ npx -y skills add egeominotti/bunqueue --skill bunqueue --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
- Slash command
/bunqueue
Context preview
The summary Claude sees to decide when to auto-load this skill.
Use bunqueue job queue library - Queue, Worker, Bunqueue (simple mode), FlowProducer, cron, DLQ, embedded and TCP modes
SKILL.md
bunqueue.SKILL.mdname: bunqueue
description: Use bunqueue job queue library - Queue, Worker, Bunqueue (simple mode), FlowProducer, cron, DLQ, embedded and TCP modes
disable-model-invocation: false
user-invocable: true
allowed-tools: Read, Grep, Glob, Bash, Edit, Write
bunqueue - Job Queue for Bun
You are helping a developer use **bunqueue**, a high-performance job queue for Bun with SQLite persistence.
Installation
bun add bunqueue
Quick Decision: Which Mode?
- **Embedded mode**: Single process, no server needed. Best for most apps.
- **TCP mode**: Separate server process. Best for distributed systems with multiple producers/consumers.
- **Simple Mode (`Bunqueue`)**: All-in-one wrapper. Best for getting started fast.
Simple Mode (Recommended Start)
Simple Mode gives you a Queue and a Worker in a single object. Add jobs, process them, add middleware, schedule crons — all from one place. Use `Bunqueue` when producer and consumer are in the same process. For distributed systems, use `Queue` + `Worker` separately.
For full API details, see [reference.md](reference.md)
Architecture
new Bunqueue('emails', opts)
│
├── this.queue = new Queue('emails', ...)
├── this.worker = new Worker('emails', ...)
│
└── Subsystems (all optional):
├── RetryEngine — jitter, fibonacci, exponential, custom
├── CircuitBreaker — pauses worker after N failures
├── BatchAccumulator — groups N jobs into one call
├── TriggerManager — "on complete → create job B"
├── TtlChecker — rejects expired jobs
├── PriorityAger — boosts old jobs' priority
├── CancellationManager — AbortController per job
└── DedupDebounceMerger — deduplication & debounce defaultsProcessing pipeline per job: `Job → Circuit Breaker → TTL check → AbortController → Retry → Middleware → Processor`
Basic Usage
import { Bunqueue } from 'bunqueue/client';
const app = new Bunqueue('emails', {
embedded: true,
processor: async (job) => {
console.log(`Sending to ${job.data.to}`);
return { sent: true };
},
});
await app.add('send', { to: 'alice@example.com' });Routes (Named Handlers)
const app = new Bunqueue('notifications', {
embedded: true,
routes: {
'send-email': async (job) => {
await sendEmail(job.data.to);
return { channel: 'email' };
},
'send-sms': async (job) => {
await sendSMS(job.data.to);
return { channel: 'sms' };
},
},
});
await app.add('send-email', { to: 'alice' });
await app.add('send-sms', { to: 'bob' });> Use one of `processor`, `routes`, or `batch`. Passing multiple or none throws an error.
Middleware (Onion Model)
// Timing middleware
app.use(async (job, next) => {
const start = Date.now();
const result = await next();
console.log(`${job.name}: ${Date.now() - start}ms`);
return result;
});
// Error recovery middleware
app.use(async (job, next) => {
try {
return await next();
} catch (err) {
return { recovered: true, error: err.message };
}
});Execution order: mw1 → mw2 → processor → mw2 → mw1. Zero overhead when no middleware.
Batch Processing
const app = new Bunqueue('db-inserts', {
embedded: true,
batch: {
size: 50, // flush every 50 jobs
timeout: 2000, // or every 2 seconds
processor: async (jobs) => {
const rows = jobs.map(j => j.data.row);
await db.insertMany('table', rows);
return jobs.map(() => ({ inserted: true }));
},
},
});Advanced Retry (5 Strategies)
const app = new Bunqueue('api-calls', {
embedded: true,
processor: async (job) => {
const res = await fetch(job.data.url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return { status: res.status };
},
retry: {
maxAttempts: 5,
delay: 1000,
strategy: 'jitter', // 'fixed' | 'exponential' | 'jitter' | 'fibonacci' | 'custom'
retryIf: (error) => error.message.includes('503'),
},
});Strategies: `fixed` (constant delay), `exponential` (delay × 2^attempt), `jitter` (exponential × random 0.5-1.0), `fibonacci` (delay × fib(attempt)), `custom` (customBackoff(attempt, error) → ms). This is in-process retry — the job stays active.
Graceful Cancellation
const app = new Bunqueue('encoding', {
embedded: true,
processor: async (job) => {
const signal = app.getSignal(job.id);
for (const chunk of chunks) {
if (signal?.aborted) throw new Error('Cancelled');
await encode(chunk);
}
return { done: true };
},
});
const job = await app.add('video', { file: 'big.mp4' });
app.cancel(job.id); // cancel immediately
app.cancel(job.id, 5000); // cancel after 5s grace periodWorks with fetch too: `await fetch(url, { signal })`.
Circuit Breaker
Pauses the worker after too many consecutive failures: `CLOSED → OPEN (paused) → HALF-OPEN → CLOSED`
const app = new Bunqueue('payments', {
embedded: true,
processor: async (job) => paymentGateway.charge(job.data),
circuitBreaker: {
threshold: 5, // open after 5 failures
resetTimeout: 30000, // try again after 30s
onOpen: () => alert('Gateway down!'),
onClose: () => alert('Gateway recovered'),
},
});
app.getCircuitState(); // 'closed' | 'open' | 'half-open'
app.resetCircuit(); // force close + resume workerEvent Triggers
const app = new Bunqueue('orders', {
embedded: true,
routes: {
'place-order': async (job) => ({ orderId: job.data.id, total: 99 }),
'send-receipt': async (job) => ({ sent: true }),
'fraud-alert': async (job) => ({ alerted: true }),
},
});
app.trigger({ on: 'place-order', create: 'send-receipt', data: (result, job) => ({ id: job.data.id }) });
app.trigger({ on: 'place-order', create: 'fraud-alert', data: (r) => ({ amount: r.totalRead more
name: bunqueue description: Use bunqueue job queue library - Queue, Worker, Bunqueue (simple mode), FlowProducer, cron, DLQ, embedded and TCP modes disable-model-invocation: false user-invocable: true allowed-tools: Read, Grep, Glob, Bash, Edit, Write
bunqueue - Job Queue for Bun
You are helping a developer use **bunqueue**, a high-performance job queue for Bun with SQLite persistence.
Installation
bun add bunqueue
Quick Decision: Which Mode?
- **Embedded mode**: Single process, no server needed. Best for most apps.
- **TCP mode**: Separate server process. Best for distributed systems with multiple producers/consumers.
- **Simple Mode (`Bunqueue`)**: All-in-one wrapper. Best for getting started fast.
Simple Mode (Recommended Start)
Simple Mode gives you a Queue and a Worker in a single object. Add jobs, process them, add middleware, schedule crons — all from one place. Use `Bunqueue` when producer and consumer are in the same process. For distributed systems, use `Queue` + `Worker` separately.
For full API details, see [reference.md](reference.md)
Architecture
new Bunqueue('emails', opts)
│
├── this.queue = new Queue('emails', ...)
├── this.worker = new Worker('emails', ...)
│
└── Subsystems (all optional):
├── RetryEngine — jitter, fibonacci, exponential, custom
├── CircuitBreaker — pauses worker after N failures
├── BatchAccumulator — groups N jobs into one call
├── TriggerManager — "on complete → create job B"
├── TtlChecker — rejects expired jobs
├── PriorityAger — boosts old jobs' priority
├── CancellationManager — AbortController per job
└── DedupDebounceMerger — deduplication & debounce defaultsProcessing pipeline per job: `Job → Circuit Breaker → TTL check → AbortController → Retry → Middleware → Processor`
Basic Usage
import { Bunqueue } from 'bunqueue/client';
const app = new Bunqueue('emails', {
embedded: true,
processor: async (job) => {
console.log(`Sending to ${job.data.to}`);
return { sent: true };
},
});
await app.add('send', { to: 'alice@example.com' });Routes (Named Handlers)
const app = new Bunqueue('notifications', {
embedded: true,
routes: {
'send-email': async (job) => {
await sendEmail(job.data.to);
return { channel: 'email' };
},
'send-sms': async (job) => {
await sendSMS(job.data.to);
return { channel: 'sms' };
},
},
});
await app.add('send-email', { to: 'alice' });
await app.add('send-sms', { to: 'bob' });> Use one of `processor`, `routes`, or `batch`. Passing multiple or none throws an error.
Middleware (Onion Model)
// Timing middleware
app.use(async (job, next) => {
const start = Date.now();
const result = await next();
console.log(`${job.name}: ${Date.now() - start}ms`);
return result;
});
// Error recovery middleware
app.use(async (job, next) => {
try {
return await next();
} catch (err) {
return { recovered: true, error: err.message };
}
});Execution order: mw1 → mw2 → processor → mw2 → mw1. Zero overhead when no middleware.
Batch Processing
const app = new Bunqueue('db-inserts', {
embedded: true,
batch: {
size: 50, // flush every 50 jobs
timeout: 2000, // or every 2 seconds
processor: async (jobs) => {
const rows = jobs.map(j => j.data.row);
await db.insertMany('table', rows);
return jobs.map(() => ({ inserted: true }));
},
},
});Advanced Retry (5 Strategies)
const app = new Bunqueue('api-calls', {
embedded: true,
processor: async (job) => {
const res = await fetch(job.data.url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return { status: res.status };
},
retry: {
maxAttempts: 5,
delay: 1000,
strategy: 'jitter', // 'fixed' | 'exponential' | 'jitter' | 'fibonacci' | 'custom'
retryIf: (error) => error.message.includes('503'),
},
});Strategies: `fixed` (constant delay), `exponential` (delay × 2^attempt), `jitter` (exponential × random 0.5-1.0), `fibonacci` (delay × fib(attempt)), `custom` (customBackoff(attempt, error) → ms). This is in-process retry — the job stays active.
Graceful Cancellation
const app = new Bunqueue('encoding', {
embedded: true,
processor: async (job) => {
const signal = app.getSignal(job.id);
for (const chunk of chunks) {
if (signal?.aborted) throw new Error('Cancelled');
await encode(chunk);
}
return { done: true };
},
});
const job = await app.add('video', { file: 'big.mp4' });
app.cancel(job.id); // cancel immediately
app.cancel(job.id, 5000); // cancel after 5s grace periodWorks with fetch too: `await fetch(url, { signal })`.
Circuit Breaker
Pauses the worker after too many consecutive failures: `CLOSED → OPEN (paused) → HALF-OPEN → CLOSED`
const app = new Bunqueue('payments', {
embedded: true,
processor: async (job) => paymentGateway.charge(job.data),
circuitBreaker: {
threshold: 5, // open after 5 failures
resetTimeout: 30000, // try again after 30s
onOpen: () => alert('Gateway down!'),
onClose: () => alert('Gateway recovered'),
},
});
app.getCircuitState(); // 'closed' | 'open' | 'half-open'
app.resetCircuit(); // force close + resume workerEvent Triggers
const app = new Bunqueue('orders', {
embedded: true,
routes: {
'place-order': async (job) => ({ orderId: job.data.id, total: 99 }),
'send-receipt': async (job) => ({ sent: true }),
'fraud-alert': async (job) => ({ alerted: true }),
},
});
app.trigger({ on: 'place-order', create: 'send-receipt', data: (result, job) => ({ id: job.data.id }) });
app.trigger({ on: 'place-order', create: 'fraud-alert', data: (r) => ({ amount: r.total⚡ High-performance job queue for Bun. SQLite persistence, DLQ, cron jobs, S3 backups. Built for AI agents and automation
Repo: egeominotti/bunqueue

