Files
WRNexusJSDoc/app/pages/packages/queue.wrn
T
ClintchizandClaude Opus 5.5 b536b57447 docs: update portal for WRNexusJS 0.8.72
- Bump every @wrnexus/* dependency to its latest release (ui 0.8.88,
  styles 0.8.26, cli 0.8.72, core 0.8.16, i18n 0.8.12, ...) and refresh
  bun.lock and package-lock.json. graphql/native 0.8.8 were republished,
  so their stale lockfile integrity entries were re-resolved.
- wrnexus.config.ts: drop `functions` and `compatibility`, both removed
  from the framework.
- Regenerate package, portal and discovery docs from the installed
  packages, including the new 0.8.72 release page.
- Package pages now install each package's own released version
  (ui 0.8.88, core 0.8.16) instead of stamping the CLI version on all.
- llms.txt / llms-full.txt: the generator kept every earlier run's CLI
  reference and appended another; it now replaces it, so the guide holds
  one current reference and no longer advertises the removed
  `wrnexus compatibility` command.
- docs.test.ts: compileWireFile was renamed compileWrnFile; tests follow
  the per-package install versions and the removed command.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 16:46:32 +05:30

207 lines
25 KiB
Plaintext

page wrnexusqueue {
seo {
title = "@wrnexus/queue"
description = "Background jobs with delay, concurrency, retry, and repetition."
}
view {
<div class="docs-shell">
<a href="#main" class="skip-link">Skip to content</a>
<header class="topbar">
<a class="brand" href="/"><span>W</span> WRNexusJS</a>
<nav aria-label="Primary"><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a></nav>
<div class="topbar-actions"><a class="preview-pill" href="/access">Private preview · v0.8.72</a><button data-wire-theme-toggle class="theme-button" aria-label="Toggle color theme" title="Toggle color theme">◐</button></div>
</header>
<div class="mobile-doc-nav"><details><summary>Browse documentation</summary><nav><a href="/getting-started">Get started</a><a href="/packages">Packages</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/language">Language</a><a href="/architecture">Architecture</a><a href="/tutorial">Tutorial</a><a href="/guides/project-structure">Guides</a><a href="/examples">Examples</a><a href="/search">Search</a></nav></details></div>
<main id="main" class="portal-main docs-layout docs-layout--navigation"><aside class="docs-section-menu" aria-label="Documentation sections"><nav>
<section><strong>Get started</strong><a href="/getting-started">Quick start</a><a href="/tutorial">Tutorial</a><a href="/guides/full-stack-auth-demo">Auth dashboard demo</a><a href="/guides/configuration-and-profiles">Configuration</a></section>
<section><strong>Build</strong><a href="/guides/pages-and-components">Pages and components</a><a href="/guides/routing">Routing</a><a href="/guides/server-data">Server data</a><a href="/guides/forms-and-validation">Forms and validation</a><a href="/guides/database">Database</a></section>
<section><strong>Auth and security</strong><a href="/guides/authentication">Authentication</a><a href="/guides/authorization">Authorization</a><a href="/guides/security">Application security</a><a href="/security">Security policy</a></section>
<section><strong>Reference</strong><a href="/packages">Packages overview</a><a href="/packages/cli">CLI</a><a href="/language">.wrn language</a><a href="https://component.wrnexusjs.dev/">Components</a><a href="/examples">Examples</a></section>
<section class="package-menu"><strong>Packages</strong><a href="/packages/ai">@wrnexus/ai</a><a href="/packages/auth">@wrnexus/auth</a><a href="/packages/authz">@wrnexus/authz</a><a href="/packages/benchmark">@wrnexus/benchmark</a><a href="/packages/cache">@wrnexus/cache</a><a href="/packages/captcha">@wrnexus/captcha</a><a href="/packages/cli">@wrnexus/cli</a><a href="/packages/compiler">@wrnexus/compiler</a><a href="/packages/content">@wrnexus/content</a><a href="/packages/core">@wrnexus/core</a><a href="/packages/csr">@wrnexus/csr</a><a href="/packages/db">@wrnexus/db</a><a href="/packages/dev-server">@wrnexus/dev-server</a><a href="/packages/dev-toolbar">@wrnexus/dev-toolbar</a><a href="/packages/encryption">@wrnexus/encryption</a><a href="/packages/graphql">@wrnexus/graphql</a><a href="/packages/helpers">@wrnexus/helpers</a><a href="/packages/i18n">@wrnexus/i18n</a><a href="/packages/identity">@wrnexus/identity</a><a href="/packages/image">@wrnexus/image</a><a href="/packages/jwt">@wrnexus/jwt</a><a href="/packages/language-server">@wrnexus/language-server</a><a href="/packages/mcp">@wrnexus/mcp</a><a href="/packages/mobile">@wrnexus/mobile</a><a href="/packages/native">@wrnexus/native</a><a href="/packages/oauth">@wrnexus/oauth</a><a href="/packages/observability">@wrnexus/observability</a><a href="/packages/playground">@wrnexus/playground</a><a href="/packages/plugin">@wrnexus/plugin</a><a href="/packages/pubsub">@wrnexus/pubsub</a><a href="/packages/pwa">@wrnexus/pwa</a><a href="/packages/queue">@wrnexus/queue</a><a href="/packages/reactive">@wrnexus/reactive</a><a href="/packages/realtime">@wrnexus/realtime</a><a href="/packages/router">@wrnexus/router</a><a href="/packages/security">@wrnexus/security</a><a href="/packages/ssr">@wrnexus/ssr</a><a href="/packages/store">@wrnexus/store</a><a href="/packages/styles">@wrnexus/styles</a><a href="/packages/syntax">@wrnexus/syntax</a><a href="/packages/test">@wrnexus/test</a><a href="/packages/tracking">@wrnexus/tracking</a><a href="/packages/typecheck">@wrnexus/typecheck</a><a href="/packages/ui">@wrnexus/ui</a><a href="/packages/uploader">@wrnexus/uploader</a><a href="/packages/validation">@wrnexus/validation</a></section>
</nav></aside><div class="docs-reading-column">
<article class="documentation prose standalone package-document"><nav class="breadcrumbs" aria-label="Breadcrumb"><a href="/">Home</a><span>/</span><a href="/packages">Packages</a><span>/</span><span aria-current="page">@wrnexus/queue</span></nav><section class="doc-intro"><span class="eyebrow">Data · Package reference</span><h1>@wrnexus/queue</h1><p>Background jobs with delay, concurrency, retry, and repetition.</p><div class="doc-meta"><span>v0.8.14</span><span>Private registry</span><span>Data</span></div><section id="access" class="access-callout"><h2>Install the package</h2><p>After WorkRoot approves private registry access, install the package:</p><pre><code>bun add @wrnexus/queue@0.8.14</code><button type="button" class="copy-button" aria-label="Copy installation command">Copy</button></pre><p><a href="/access">Request preview access</a>. Never put registry tokens in source control.</p></section></section><section id="guide"><blockquote>A background job queue with delays, retries + exponential backoff, recurring jobs, and concurrent workers.</blockquote>
<p>Part of the <strong>WRNexusJS</strong> framework — an SSR-first, Bun-native full-stack web framework.</p>
<h3 id="overview">Overview</h3>
<p><code>@wrnexus/queue</code> is a server-side durable job queue. You register named workers, enqueue jobs (optionally delayed or recurring), and let the queue poll and run them on a timer — with per-job retry limits and doubling backoff between attempts. <code>defineQueue</code> persists to <code>.wrnexus/queue.sqlite</code> by default, while low-level <code>createQueue</code> remains an intentionally in-memory primitive. Reach for it when you need to defer work (emails, webhooks, cleanup) off the request path without a heavyweight external broker. Tests can drive it deterministically via <code>drain()</code>.</p>
<pre data-language="bash"><code>bun add @wrnexus/queue</code></pre>
<blockquote>Private package — the machine must be authenticated to the <code>wrnexus</code> npm org</blockquote>
<blockquote>(a read token in <code>~/.npmrc</code>). Requires <strong>Bun</strong> (Node is not supported).</blockquote>
<h3 id="api">API</h3>
<p>The package exports a single factory plus its supporting types.</p>
<h4 id="createqueue-options-queue"><code>createQueue(options?): Queue</code></h4>
<p>Creates a new queue instance.</p>
<pre data-language="ts"><code>function createQueue(options?: QueueOptions): Queue;</code></pre>
<h4 id="queueoptions"><code>QueueOptions</code></h4>
<div class="table-wrap"><table>
<thead><tr><th>Option</th><th>Type</th><th>Default</th><th>Description</th></tr></thead>
<tbody><tr><td><code>maxAttempts</code></td><td><code>number</code></td><td><code>3</code></td><td>Default max attempts per job before it is dead-lettered.</td></tr><tr><td><code>backoffMs</code></td><td><code>number</code></td><td><code>1000</code></td><td>Base retry backoff in ms; doubles per attempt.</td></tr><tr><td><code>pollMs</code></td><td><code>number</code></td><td><code>250</code></td><td>Poll interval used once <code>start()</code> is called (ms).</td></tr><tr><td><code>onFailed</code></td><td><code>(job: Job, error: unknown) =&gt; void</code></td><td>—</td><td>Called when a job exhausts its attempts.</td></tr><tr><td><code>concurrency</code></td><td><code>number</code></td><td>unlimited</td><td>Maximum jobs claimed by one <code>drain()</code> call.</td></tr><tr><td><code>capacity</code></td><td><code>number</code></td><td>unlimited</td><td>Optional maximum queued jobs; omitted queues never scan merely to enforce a hidden cap.</td></tr><tr><td><code>now</code></td><td><code>() =&gt; number</code></td><td><code>Date.now</code></td><td>Clock injection for deterministic tests.</td></tr></tbody></table></div>
<h4 id="queue"><code>Queue</code></h4>
<p>The object returned by <code>createQueue</code>.</p>
<div class="table-wrap"><table>
<thead><tr><th>Method</th><th>Signature</th><th>Description</th></tr></thead>
<tbody><tr><td><code>add</code></td><td><code>add&lt;T&gt;(name, data: T, options?: AddOptions): Promise&lt;Job&lt;T&gt;&gt;</code></td><td>Enqueue a job under a worker name. Returns the created job.</td></tr><tr><td><code>process</code></td><td><code>process&lt;T&gt;(name, handler: JobHandler&lt;T&gt;): void</code></td><td>Register the worker that runs jobs of the given name.</td></tr><tr><td><code>drain</code></td><td><code>drain(now?: number): Promise&lt;number&gt;</code></td><td>Run every job whose <code>runAt ≤ now</code>, once. Returns how many ran.</td></tr><tr><td><code>start</code></td><td><code>start(): void</code></td><td>Begin polling every <code>pollMs</code>. No-op if already started.</td></tr><tr><td><code>stop</code></td><td><code>stop(): void</code></td><td>Stop the poll timer.</td></tr><tr><td><code>shutdown</code></td><td><code>shutdown(&#123; force? &#125;): Promise&lt;void&gt;</code></td><td>Stop accepting jobs and await active work; force aborts it.</td></tr><tr><td><code>size</code></td><td><code>size(): number</code></td><td>Number of jobs currently queued.</td></tr><tr><td><code>get/list</code></td><td><code>get(id)</code> / <code>list(name?)</code></td><td>Inspect defensive copies of pending jobs.</td></tr><tr><td><code>cancel</code></td><td><code>cancel(id): boolean</code></td><td>Remove queued work or abort an active handler.</td></tr><tr><td><code>failed</code></td><td><code>failed(): Job[]</code></td><td>Inspect exhausted jobs in the dead-letter collection.</td></tr><tr><td><code>retry</code></td><td><code>retry(id): Promise&lt;boolean&gt;</code></td><td>Reset and requeue a dead-lettered job.</td></tr></tbody></table></div>
<h4 id="addoptions"><code>AddOptions</code></h4>
<div class="table-wrap"><table>
<thead><tr><th>Option</th><th>Type</th><th>Description</th></tr></thead>
<tbody><tr><td><code>delayMs</code></td><td><code>number</code></td><td>Delay before the job becomes runnable (ms).</td></tr><tr><td><code>maxAttempts</code></td><td><code>number</code></td><td>Max attempts before dead-lettering. Defaults to the queue's <code>maxAttempts</code>.</td></tr><tr><td><code>repeat</code></td><td><code>number</code></td><td>Re-enqueue this job this many ms after each successful run (recurring).</td></tr><tr><td><code>priority</code></td><td><code>number</code></td><td>Higher values are selected first among due jobs.</td></tr><tr><td><code>idempotencyKey</code></td><td><code>string</code></td><td>Return the matching pending job instead of enqueueing a duplicate.</td></tr></tbody></table></div>
<h4 id="jobhandler-t"><code>JobHandler&lt;T&gt;</code></h4>
<pre data-language="ts"><code>type JobHandler&lt;T = unknown&gt; = (
job: Job&lt;T&gt;,
context: &#123; signal: AbortSignal &#125;,
) =&gt; void | Promise&lt;void&gt;;</code></pre>
<h4 id="job-t"><code>Job&lt;T&gt;</code></h4>
<pre data-language="ts"><code>interface Job&lt;T = unknown&gt; &#123;
id: string; // e.g. &quot;job_1&quot;
name: string;
data: T;
attempts: number;
maxAttempts: number;
runAt: number; // epoch ms; job runs when now ≥ runAt
repeat?: number; // if set, re-enqueue this many ms after each success
&#125;</code></pre>
<h3 id="usage">Usage</h3>
<p>Application queues belong in <code>app/queues/*.ts</code> and default-export <code>defineQueue(...)</code>. Configure persistence once in <code>wrnexus.config.ts</code>:</p>
<pre data-language="ts"><code>export default &#123;
queue: &#123; storage: &quot;database&quot;, databaseName: &quot;default&quot; &#125;,
// Or omit queue entirely for .wrnexus/queue.sqlite.
&#125;;</code></pre>
<p>Use <code>storage: &quot;memory&quot;</code> only for disposable work. A selected database must exist; WRNexusJS fails startup/first use with a clear error instead of silently falling back. Each job may declare <code>success(data, job)</code> and <code>failed(data, error, job)</code> lifecycle hooks.</p>
<p>Register workers, enqueue jobs, then start the poller:</p>
<pre data-language="ts"><code>import &#123; createQueue &#125; from &quot;@wrnexus/queue&quot;;
const queue = createQueue(&#123; maxAttempts: 3, backoffMs: 1000 &#125;);
// Register a worker for the &quot;email&quot; job name.
queue.process&lt;&#123; to: string &#125;&gt;(&quot;email&quot;, async (job) =&gt; &#123;
await send(job.data.to);
&#125;);
// Enqueue a delayed job with up to 3 attempts.
await queue.add(&quot;email&quot;, &#123; to: &quot;a@b.com&quot; &#125;, &#123; delayMs: 5000, maxAttempts: 3 &#125;);
queue.start(); // begin polling; queue.stop() to halt</code></pre>
<h4 id="application-queues-with-definequeue">Application queues with <code>defineQueue</code></h4>
<p>WRNexusJS applications can place typed queue modules in <code>app/queues</code>. The dev and production servers discover them, start them after runtime initialization, and shut them down gracefully:</p>
<pre data-language="ts"><code>import &#123; defineQueue &#125; from &quot;@wrnexus/queue&quot;;
export default defineQueue(&#123;
name: &quot;email&quot;,
jobs: &#123;
send: &#123;
maxAttempts: 3,
idempotency: (&#123; messageId &#125;: &#123; messageId: number &#125;) =&gt; `message:$&#123;messageId&#125;`,
run: async (&#123; messageId &#125;) =&gt; sendMessage(messageId),
&#125;,
&#125;,
&#125;);</code></pre>
<p>Callers get a typed producer and inspection helpers:</p>
<pre data-language="ts"><code>const job = await email.send.add(&#123; messageId: 42 &#125;);
await email.send.status(job.id);
await email.send.cancel(job.id);
await email.send.retry(job.id);</code></pre>
<p><code>createDurableQueue</code> also exposes <code>start()</code>, <code>stop()</code>, <code>isRunning()</code>, <code>health()</code> and <code>get()</code> so applications do not need to build their own polling middleware.</p>
<h4 id="sqlite">SQLite</h4>
<p>Use <code>sqliteQueueStore(db)</code> from <code>@wrnexus/queue/sqlite</code> with a WRNexusJS-compatible SQLite client. <code>installSqliteQueueSchema(db)</code> installs the queue table and its due-job and idempotency indexes.</p>
<p>Use <code>context.signal</code> in network/database calls so forced shutdown and active cancellation finish promptly. For process termination, prefer <code>await queue.shutdown()</code>; use <code>&#123; force: true &#125;</code> only after your grace period.</p>
<h4 id="durable-queue">Durable queue</h4>
<p><code>createDurableQueue(&#123; store &#125;)</code> retains jobs until their handler succeeds and supports atomic leases when a driver implements <code>QueueStore.claim</code>. It exposes the same cancellation/shutdown behavior plus <code>list</code>, <code>failed</code>, and <code>retry</code>. The included <code>memoryQueueStore()</code> is useful for tests; production Redis/SQL drivers should make <code>claim()</code> atomic to prevent two workers executing one job.</p>
<h4 id="recurring-jobs">Recurring jobs</h4>
<p>Pass <code>repeat</code> to re-enqueue a job a fixed interval after each successful run:</p>
<pre data-language="ts"><code>queue.process(&quot;heartbeat&quot;, async () =&gt; ping());
await queue.add(&quot;heartbeat&quot;, &#123;&#125;, &#123; repeat: 60_000 &#125;); // runs ~every minute</code></pre>
<h4 id="handling-permanent-failures">Handling permanent failures</h4>
<p>When a job's <code>attempts</code> reaches <code>maxAttempts</code>, it is dropped and <code>onFailed</code> fires instead of retrying:</p>
<pre data-language="ts"><code>const queue = createQueue(&#123;
onFailed: (job, error) =&gt; &#123;
console.error(`job $&#123;job.id&#125; ($&#123;job.name&#125;) gave up`, error);
&#125;,
&#125;);</code></pre>
<h4 id="deterministic-testing">Deterministic testing</h4>
<p>Instead of <code>start()</code>, inject a clock and drive the queue with <code>drain()</code>:</p>
<pre data-language="ts"><code>let clock = 0;
const queue = createQueue(&#123; now: () =&gt; clock &#125;);
queue.process(&quot;task&quot;, async () =&gt; &#123;
/* ... */
&#125;);
await queue.add(&quot;task&quot;, &#123;&#125;, &#123; delayMs: 5000 &#125;);
clock = 5000;
const ran = await queue.drain(); // =&gt; 1</code></pre>
<h4 id="durable-workflows-and-approvals">Durable workflows and approvals</h4>
<p><code>createWorkflowEngine(store)</code> executes dependency-ordered steps and persists every transition, result, progress update, failure, cancellation, and approval record. Approval steps pause safely and can resume after a process restart because the snapshot lives in the supplied <code>WorkflowStore</code>.</p>
<pre data-language="ts"><code>const workflow = defineDurableWorkflow(&#123;
name: &quot;publish-report&quot;,
steps: [
&#123; name: &quot;build&quot;, run: buildReport &#125;,
&#123; name: &quot;approve&quot;, dependsOn: [&quot;build&quot;], approval: true, run: (report) =&gt; report &#125;,
&#123; name: &quot;publish&quot;, dependsOn: [&quot;approve&quot;], run: publishReport &#125;,
],
&#125;);
const run = await engine.start(workflow, input);
await engine.approve(workflow, run.id, &quot;approve&quot;, currentUser.id);</code></pre>
<p>Use <code>memoryWorkflowStore()</code> for tests. Production stores implement the small <code>get</code>, <code>put</code>, and <code>list</code> contract using the same transactional database or durable service as the application.</p>
<h3 id="retry-backoff-behavior">Retry &amp; backoff behavior</h3>
<ul>
<li>On a thrown handler error, the job is retried while <code>attempts &lt; maxAttempts</code>.</li>
<li>The next <code>runAt</code> is set to <code>now + backoffMs * 2^(attempts - 1)</code> (exponential</li>
<p>backoff): with <code>backoffMs: 1000</code> the delays are 1s, 2s, 4s, …</p>
<li>A job whose worker name has no registered handler stays queued until one is</li>
<p>registered (it is not counted as runnable by <code>drain</code>).</p>
<li><code>drain</code> is re-entrant-safe: overlapping calls are skipped while one is running.</li>
</ul>
<h3 id="requirements-notes">Requirements / Notes</h3>
<ul>
<li><strong>Bun-only</strong> runtime (Node is not supported), consistent with the rest of the</li>
<p>WRNexusJS framework. The queue itself relies only on standard timers (<code>setInterval</code>/<code>clearInterval</code>) and has no runtime dependencies.</p>
<li>The default store is in-process, so queued jobs do not survive a restart; a</li>
<p>pluggable driver is intended for backing it with Redis/SQL for durability.</p>
<li>Works alongside <code>@wrnexus/core</code> for offloading work from the request path.</li>
</ul></section><section id="api" class="api"><h2>Complete TypeScript API</h2><p>Generated from the exact installed package declarations.</p><pre data-language="typescript"><code>export &#123; A as AddOptions, D as DefineQueueOptions, a as DefinedJob, b as DefinedJobOptions, c as DefinedJobs, d as DefinedQueue, e as DurableQueue, f as DurableQueueOptions, J as Job, g as JobContext, h as JobDefinition, i as JobHandler, j as JobStateMachineOptions, k as JobStatus, O as OutboxEntry, l as OutboxWriter, P as POSTGRES_QUEUE_SCHEMA, Q as Queue, m as QueueAdminOptions, n as QueueDashboardSnapshot, o as QueueEvent, p as QueueEventType, q as QueueHealth, r as QueueJobDefinitions, s as QueueJobRecord, t as QueueJobState, u as QueueOptions, v as QueueScheduler, w as QueueStorage, x as QueueStorageConfig, y as QueueStore, R as RedisQueueClient, S as SagaStep, z as ScheduledJob, B as SqlQueueClient, SqliteQueueClient, C as SubjectJob, E as SubjectQueue, W as WorkerDefinition, F as WorkflowDefinition, G as WorkflowEngine, H as WorkflowRunContext, I as WorkflowSnapshot, K as WorkflowStatus, L as WorkflowStep, M as WorkflowStore, N as addBatch, T as configureQueueStorage, U as configuredQueueStore, V as createDurableQueue, X as createQueue, Y as createQueueAdminHandler, Z as createQueueScheduler, _ as createWorkflowEngine, $ as cronToInterval, a0 as databaseQueueStore, a1 as defineDurableWorkflow, a2 as defineJob, a3 as defineJobStateMachine, a4 as defineQueue, a5 as defineSaga, a6 as defineWorker, a7 as defineWorkflow, a8 as drainOutbox, a9 as installDatabaseQueueSchema, aa as installOutboxSchema, installSqliteQueueSchema, ab as memoryQueueStore, ac as memoryWorkflowStore, ad as postgresQueueStore, ae as queueDashboardSnapshot, af as queueStorageConfig, ag as redisQueueStore, ah as renderQueueDashboard, ai as runQueueDaemon, aj as runWorker, sqliteQueueSchema, sqliteQueueStore, ak as subjectQueue, al as testQueue, am as withOutbox &#125; from './sqlite.js';
import '@wrnexus/core';
import '@wrnexus/db';
import '@wrnexus/rpc';
</code></pre></section><section id="examples" class="examples"><h2>Examples</h2><p>Copy-ready examples from the installed package documentation.</p><div class="example-grid"><article class="example-card"><h3>Configure persistence once in wrnexus.config.ts</h3><pre data-language="ts"><code>export default &#123;
queue: &#123; storage: &quot;database&quot;, databaseName: &quot;default&quot; &#125;,
// Or omit queue entirely for .wrnexus/queue.sqlite.
&#125;;</code></pre></article><article class="example-card"><h3>Register workers, enqueue jobs, then start the poller</h3><pre data-language="ts"><code>import &#123; createQueue &#125; from &quot;@wrnexus/queue&quot;;
const queue = createQueue(&#123; maxAttempts: 3, backoffMs: 1000 &#125;);
// Register a worker for the &quot;email&quot; job name.
queue.process&lt;&#123; to: string &#125;&gt;(&quot;email&quot;, async (job) =&gt; &#123;
await send(job.data.to);
&#125;);
// Enqueue a delayed job with up to 3 attempts.
await queue.add(&quot;email&quot;, &#123; to: &quot;a@b.com&quot; &#125;, &#123; delayMs: 5000, maxAttempts: 3 &#125;);
queue.start(); // begin polling; queue.stop() to halt</code></pre></article><article class="example-card"><h3>Application queues with defineQueue</h3><pre data-language="ts"><code>import &#123; defineQueue &#125; from &quot;@wrnexus/queue&quot;;
export default defineQueue(&#123;
name: &quot;email&quot;,
jobs: &#123;
send: &#123;
maxAttempts: 3,
idempotency: (&#123; messageId &#125;: &#123; messageId: number &#125;) =&gt; `message:$&#123;messageId&#125;`,
run: async (&#123; messageId &#125;) =&gt; sendMessage(messageId),
&#125;,
&#125;,
&#125;);</code></pre></article><article class="example-card"><h3>Application queues with defineQueue</h3><pre data-language="ts"><code>const job = await email.send.add(&#123; messageId: 42 &#125;);
await email.send.status(job.id);
await email.send.cancel(job.id);
await email.send.retry(job.id);</code></pre></article><article class="example-card"><h3>Recurring jobs</h3><pre data-language="ts"><code>queue.process(&quot;heartbeat&quot;, async () =&gt; ping());
await queue.add(&quot;heartbeat&quot;, &#123;&#125;, &#123; repeat: 60_000 &#125;); // runs ~every minute</code></pre></article><article class="example-card"><h3>Handling permanent failures</h3><pre data-language="ts"><code>const queue = createQueue(&#123;
onFailed: (job, error) =&gt; &#123;
console.error(`job $&#123;job.id&#125; ($&#123;job.name&#125;) gave up`, error);
&#125;,
&#125;);</code></pre></article></div></section></article></div>
<aside class="on-this-page"><h2>On this page</h2><nav><a class="toc-level-2" href="#guide">Guide</a><a class="toc-level-3" href="#overview">Overview</a><a class="toc-level-3" href="#api">API</a><a class="toc-level-4" href="#createqueue-options-queue">createQueue(options?): Queue</a><a class="toc-level-4" href="#queueoptions">QueueOptions</a><a class="toc-level-4" href="#queue">Queue</a><a class="toc-level-4" href="#addoptions">AddOptions</a><a class="toc-level-4" href="#jobhandler-t">JobHandler&lt;T&gt;</a><a class="toc-level-4" href="#job-t">Job&lt;T&gt;</a><a class="toc-level-3" href="#usage">Usage</a><a class="toc-level-4" href="#application-queues-with-definequeue">Application queues with defineQueue</a><a class="toc-level-4" href="#sqlite">SQLite</a><a class="toc-level-4" href="#durable-queue">Durable queue</a><a class="toc-level-4" href="#recurring-jobs">Recurring jobs</a><a class="toc-level-4" href="#handling-permanent-failures">Handling permanent failures</a><a class="toc-level-4" href="#deterministic-testing">Deterministic testing</a><a class="toc-level-4" href="#durable-workflows-and-approvals">Durable workflows and approvals</a><a class="toc-level-3" href="#retry-backoff-behavior">Retry &amp; backoff behavior</a><a class="toc-level-3" href="#requirements-notes">Requirements / Notes</a><a class="toc-level-2" href="#api">Complete API</a><a class="toc-level-2" href="#examples">Examples</a></nav></aside>
</main>
<footer><div class="footer-brand"><span class="footer-mark" aria-hidden="true">W</span><p><strong>WRNexusJS 0.8.72</strong><span>Complete API documentation generated from installed package declarations.</span></p></div><nav aria-label="Footer"><a href="/packages">All packages</a><a href="/getting-started">Get started</a><a href="/security">Security</a><a href="/support">Support</a><a href="/llms.txt">AI guide</a></nav><p class="footer-meta">Private Developer Preview · Bun-native</p></footer>
</div>
}
}