@warlock.js/queue 5.31.0 → 5.32.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.
- package/CHANGELOG.md +6 -0
- package/cjs/index.cjs +3 -2
- package/cjs/index.cjs.map +1 -1
- package/esm/config.d.mts +4 -6
- package/esm/config.d.mts.map +1 -1
- package/esm/dashboard-boot.d.mts +2 -4
- package/esm/dashboard-boot.d.mts.map +1 -1
- package/esm/dashboard.d.mts +8 -7
- package/esm/dashboard.d.mts.map +1 -1
- package/esm/dashboard.mjs +1 -1
- package/esm/dashboard.mjs.map +1 -1
- package/esm/define-job.d.mts +2 -2
- package/esm/define-job.d.mts.map +1 -1
- package/esm/define-job.mjs +1 -1
- package/esm/dispatch-interceptor.d.mts +3 -5
- package/esm/dispatch-interceptor.d.mts.map +1 -1
- package/esm/duration.d.mts +1 -3
- package/esm/duration.d.mts.map +1 -1
- package/esm/errors.d.mts +6 -7
- package/esm/errors.d.mts.map +1 -1
- package/esm/failed-jobs.d.mts +8 -7
- package/esm/failed-jobs.d.mts.map +1 -1
- package/esm/failed-jobs.mjs +2 -1
- package/esm/failed-jobs.mjs.map +1 -1
- package/esm/job-registry.d.mts +3 -5
- package/esm/job-registry.d.mts.map +1 -1
- package/esm/queue-connector.d.mts +3 -5
- package/esm/queue-connector.d.mts.map +1 -1
- package/esm/queue-context.d.mts +8 -7
- package/esm/queue-context.d.mts.map +1 -1
- package/esm/queue-dashboard-unguarded.error.d.mts +1 -2
- package/esm/queue-dashboard-unguarded.error.d.mts.map +1 -1
- package/esm/queue-manager.d.mts +5 -7
- package/esm/queue-manager.d.mts.map +1 -1
- package/esm/types.d.mts +76 -46
- package/esm/types.d.mts.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,12 @@ All notable changes to `@warlock.js/queue` are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). `@warlock.js/*` packages are released in lockstep — every package shares the same version number, so a version below may list only the changes that affected this package.
|
|
6
6
|
|
|
7
|
+
## 5.32.0 - 2026-10-18
|
|
8
|
+
|
|
9
|
+
### Changed
|
|
10
|
+
|
|
11
|
+
- Pin the package development compiler to TypeScript 7.0.2 and use explicit Node types and source roots with supported module resolution.
|
|
12
|
+
|
|
7
13
|
## 5.31.0 - 2026-10-18
|
|
8
14
|
|
|
9
15
|
### Added
|
package/cjs/index.cjs
CHANGED
|
@@ -404,7 +404,7 @@ async function closeWorker(worker, timeout) {
|
|
|
404
404
|
async function queueDashboard(server, options = {}) {
|
|
405
405
|
const basePath = options.basePath ?? "/admin/queues";
|
|
406
406
|
const { createBullBoard, BullMQAdapter, FastifyAdapter } = await loadBullBoard();
|
|
407
|
-
const queueNames = options.queues ?? [
|
|
407
|
+
const queueNames = options.queues ?? [.../* @__PURE__ */ new Set([defaultQueueName(), ...registeredJobs().map((job) => queueOf(job))])];
|
|
408
408
|
const serverAdapter = new FastifyAdapter();
|
|
409
409
|
serverAdapter.setBasePath(basePath);
|
|
410
410
|
createBullBoard({
|
|
@@ -646,7 +646,8 @@ async function toSnapshot(job, state) {
|
|
|
646
646
|
* failed unrecoverably. Each entry can be retried.
|
|
647
647
|
*/
|
|
648
648
|
async function failedJobs(options = {}) {
|
|
649
|
-
|
|
649
|
+
const queueName = options.queue ?? defaultQueueName();
|
|
650
|
+
return (await getQueue(queueName).getFailed(options.start ?? 0, options.end ?? 99)).map((job) => toFailedJob(job));
|
|
650
651
|
}
|
|
651
652
|
/**
|
|
652
653
|
* Retry one failed job by id: it goes back to waiting with its attempts reset.
|
package/cjs/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["UnrecoverableError","Queue","Worker"],"sources":["../../../../../../../packages/queue/src/errors.ts","../../../../../../../packages/queue/src/config.ts","../../../../../../../packages/queue/src/dashboard-middleware-adapter.ts","../../../../../../../packages/queue/src/dashboard-guard-plugin.ts","../../../../../../../packages/queue/src/job-registry.ts","../../../../../../../packages/queue/src/queue-context.ts","../../../../../../../packages/queue/src/process-job.ts","../../../../../../../packages/queue/src/queue-manager.ts","../../../../../../../packages/queue/src/dashboard.ts","../../../../../../../packages/queue/src/queue-dashboard-unguarded.error.ts","../../../../../../../packages/queue/src/dashboard-boot.ts","../../../../../../../packages/queue/src/dispatch-interceptor.ts","../../../../../../../packages/queue/src/duration.ts","../../../../../../../packages/queue/src/define-job.ts","../../../../../../../packages/queue/src/failed-jobs.ts","../../../../../../../packages/queue/src/queue-connector.ts"],"sourcesContent":["/**\n * Thrown when the queue is used before `setQueueConfig` (or the queue\n * connector) supplied a configuration.\n */\nexport class QueueNotConfiguredError extends Error {\n public constructor() {\n super(\n \"@warlock.js/queue is not configured. Add src/config/queue.ts exporting a QueueConfig \" +\n \"and register queueConnector() in warlock.config.ts > connectors, \" +\n \"or call setQueueConfig() yourself.\",\n );\n this.name = \"QueueNotConfiguredError\";\n }\n}\n\n/**\n * Thrown for a malformed duration such as `\"10 minutes\"`.\n */\nexport class InvalidDurationError extends Error {\n public constructor(value: unknown) {\n super(\n `Invalid duration ${JSON.stringify(value)}: expected milliseconds as a non-negative number ` +\n `or a string like \"500ms\", \"30s\", \"10m\", \"2h\", \"1d\".`,\n );\n this.name = \"InvalidDurationError\";\n }\n}\n\n/**\n * Thrown by `defineJob` for an invalid definition.\n */\nexport class InvalidJobDefinitionError extends Error {\n public constructor(message: string) {\n super(message);\n this.name = \"InvalidJobDefinitionError\";\n }\n}\n\n/**\n * Thrown by `retryFailedJob` when no failed job has the given id.\n */\nexport class FailedJobNotFoundError extends Error {\n public constructor(id: string, queue: string) {\n super(`No failed job with id \"${id}\" on queue \"${queue}\".`);\n this.name = \"FailedJobNotFoundError\";\n }\n}\n\n/**\n * Thrown by `queueDashboard` when an optional bull-board package is not\n * installed.\n */\nexport class QueueDashboardDependencyError extends Error {\n public constructor(missing: string) {\n super(\n `The queue dashboard needs the optional package \"${missing}\", which is not installed.\\n` +\n \"Install both bull-board packages:\\n\\n\" +\n \" npm install @bull-board/api @bull-board/fastify\\n\",\n );\n this.name = \"QueueDashboardDependencyError\";\n }\n}\n\n/**\n * Throw from a job handler (or a queue context's `restore`) to fail the job\n * permanently — it is not retried, whatever `attempts` allows.\n */\nexport class UnrecoverableJobError extends Error {\n public constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"UnrecoverableJobError\";\n }\n}\n","import { QueueNotConfiguredError } from \"./errors\";\nimport type { QueueConfig } from \"./types\";\n\nlet activeConfig: QueueConfig | undefined;\n\n/**\n * Set the active queue configuration. In a Warlock app the queue connector\n * calls this at boot with `src/config/queue.ts`; scripts and tests may call\n * it directly. Replaces (does not merge) any previous configuration.\n */\nexport function setQueueConfig(config: QueueConfig): void {\n activeConfig = config;\n}\n\n/**\n * The active queue configuration. Throws {@link QueueNotConfiguredError}\n * when none was set.\n */\nexport function getQueueConfig(): QueueConfig {\n if (!activeConfig) {\n throw new QueueNotConfiguredError();\n }\n\n return activeConfig;\n}\n\n/** Forget the active configuration. */\nexport function resetQueueConfig(): void {\n activeConfig = undefined;\n}\n\n/** The queue a job runs on when it names none. */\nexport function defaultQueueName(): string {\n return activeConfig?.defaultQueue ?? \"default\";\n}\n","import type { Middleware } from \"@warlock.js/core\";\nimport type { FastifyReply, FastifyRequest } from \"fastify\";\n\n/**\n * Run a Warlock middleware list against a raw Fastify request/reply pair.\n *\n * Bull-board's `FastifyAdapter` hands back a plain Fastify plugin, not a\n * Warlock route — there is no `Route`, no validation pipeline, and none of\n * `createRequestStore`'s context wiring (CSP, tracing, `@warlock.js/context`\n * stores). Reusing that full pipeline here would pull the whole request\n * machinery into a place it was never meant to run. Instead this builds the\n * minimal `Request`/`Response` pair — enough for guard-style middleware\n * (`authMiddleware`, `ipFilterMiddleware`, a custom check) to read the\n * request and short-circuit with a response, which covers every one of\n * bull-board's routes because they all sit behind the same hook.\n *\n * `@warlock.js/core` is imported dynamically so this module never drags\n * core's runtime graph into a process that never mounts the dashboard.\n *\n * @returns `true` when a middleware sent a response and the caller must not\n * continue (bull-board's handler must not run); `false` to continue.\n */\nexport async function runDashboardMiddleware(\n middlewareList: Middleware[],\n fastifyRequest: FastifyRequest,\n fastifyReply: FastifyReply,\n): Promise<boolean> {\n if (middlewareList.length === 0) {\n return false;\n }\n\n const { Request, Response } = await import(\"@warlock.js/core\");\n\n const request = new Request();\n const response = new Response();\n\n response.setResponse(fastifyReply);\n request.response = response;\n response.request = request;\n request.setRequest(fastifyRequest);\n\n for (const middlewareFunction of middlewareList) {\n const result = await middlewareFunction({ request, response });\n\n if (result) {\n return true;\n }\n }\n\n return false;\n}\n","import type { Middleware } from \"@warlock.js/core\";\nimport type { FastifyInstance, FastifyPluginCallback } from \"fastify\";\nimport { runDashboardMiddleware } from \"./dashboard-middleware-adapter\";\n\n/**\n * Wrap bull-board's Fastify plugin in a scope that runs `middlewareList`\n * on every request before bull-board's own routes see it.\n *\n * Bull-board's `FastifyAdapter.registerPlugin()` returns a plain Fastify\n * plugin with no hook for Warlock middleware to run through, and Fastify\n * only lets an `onRequest` hook be added to a plugin scope — never spliced\n * into a plugin someone else wrote. So this builds ONE plugin that adds the\n * hook to its own scope and then registers bull-board's plugin as a child of\n * that scope; Fastify's encapsulation runs the hook for every route the\n * child registers, which is every dashboard route.\n */\nexport function buildDashboardGuardPlugin(\n middlewareList: Middleware[],\n bullBoardPlugin: unknown,\n): FastifyPluginCallback {\n return function dashboardGuardPlugin(instance: FastifyInstance, _options, done) {\n if (middlewareList.length > 0) {\n instance.addHook(\"onRequest\", async (request, reply) => {\n const handled = await runDashboardMiddleware(middlewareList, request, reply);\n\n if (handled) {\n // Returning the reply is Fastify's own way for an `onRequest` hook to\n // END the lifecycle, so bull-board's handler never runs. Relying\n // instead on Fastify noticing the reply was already sent leaves the\n // outcome to write ORDERING: a guard answering asynchronously can lose\n // that race and let the dashboard render to an unauthenticated caller.\n // The adapter returns this boolean precisely so the decision is\n // explicit rather than emergent.\n return reply;\n }\n });\n }\n\n instance.register(bullBoardPlugin as never);\n done();\n };\n}\n","import { defaultQueueName } from \"./config\";\nimport type { JobDefinition } from \"./types\";\n\n/** A registered definition, payload/result erased for storage. */\nexport type RegisteredJob = JobDefinition<unknown, unknown>;\n\ntype RegistryListener = (job: RegisteredJob) => void;\n\nconst jobs = new Map<string, RegisteredJob>();\nconst listeners = new Set<RegistryListener>();\n\n/**\n * Register a definition under its name.\n *\n * Re-registering a name REPLACES the previous definition: in development a\n * job module is re-evaluated on every reload, and refusing the second\n * evaluation would break the reload. Job names must therefore be unique\n * across the app — two different modules using one name leave only the\n * later handler active.\n */\nexport function registerJob(job: RegisteredJob): void {\n jobs.set(job.name, job);\n\n for (const listener of listeners) {\n listener(job);\n }\n}\n\n/** The definition registered under `name`, if any. */\nexport function findRegisteredJob(name: string): RegisteredJob | undefined {\n return jobs.get(name);\n}\n\n/** Every registered definition. */\nexport function registeredJobs(): RegisteredJob[] {\n return [...jobs.values()];\n}\n\n/** The queue a definition runs on, resolved against the active config. */\nexport function queueOf(job: Pick<RegisteredJob, \"queue\">): string {\n return job.queue ?? defaultQueueName();\n}\n\n/** Be told whenever a job is registered. Returns an unsubscribe function. */\nexport function onJobRegistered(listener: RegistryListener): () => void {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n}\n","/**\n * Carries ambient state (tenant, request id, …) from the code that\n * dispatches a job into the worker that runs it.\n */\nexport type QueueContext<T> = {\n /** Read the ambient state at dispatch time; `undefined` means nothing to carry. */\n capture: () => T | undefined;\n /** Run `run` with the captured state re-established. */\n restore: (captured: T, run: () => Promise<unknown>) => Promise<unknown>;\n};\n\n/**\n * Define a queue context. `restore`'s `captured` is inferred from `capture`.\n *\n * @example\n * setQueueContext(\n * defineQueueContext({\n * capture: () => tenantStorage.getStore(),\n * restore: (tenant, run) => tenantStorage.run(tenant, run),\n * }),\n * );\n */\nexport function defineQueueContext<T>(context: QueueContext<T>): QueueContext<T> {\n return context;\n}\n\nlet activeContext: QueueContext<any> | undefined;\n\n/** Register the queue context used by every job dispatch and run. */\nexport function setQueueContext(context: QueueContext<any>): void {\n activeContext = context;\n}\n\n/** The registered queue context, if any. */\nexport function getQueueContext(): QueueContext<any> | undefined {\n return activeContext;\n}\n\n/** Forget the registered queue context. */\nexport function resetQueueContext(): void {\n activeContext = undefined;\n}\n\n/** The job-data envelope written when a context was captured. */\nexport type QueueEnvelope = {\n __warlock: { v: 1; context?: unknown };\n payload: unknown;\n};\n\n/** Wrap a payload with the captured context; no context leaves it bare. */\nexport function wrapPayload(payload: unknown, context: unknown): unknown {\n if (context === undefined) return payload;\n\n return { __warlock: { v: 1, context }, payload } satisfies QueueEnvelope;\n}\n\n/** Split job data into payload and captured context; bare data passes through. */\nexport function unwrapPayload(data: unknown): { payload: unknown; context: unknown } {\n const envelope = data as Partial<QueueEnvelope> | null;\n\n if (\n envelope !== null &&\n typeof envelope === \"object\" &&\n typeof envelope.__warlock === \"object\" &&\n envelope.__warlock !== null &&\n envelope.__warlock.v === 1 &&\n \"payload\" in envelope\n ) {\n return { payload: envelope.payload, context: envelope.__warlock.context };\n }\n\n return { payload: data, context: undefined };\n}\n","import { type Job, UnrecoverableError } from \"bullmq\";\nimport { UnrecoverableJobError } from \"./errors\";\nimport { findRegisteredJob } from \"./job-registry\";\nimport { getQueueContext, unwrapPayload } from \"./queue-context\";\nimport type { JobContext } from \"./types\";\n\n/**\n * The single BullMQ processor every worker runs: route the job to the\n * handler registered under its name.\n *\n * A name with no handler in this process fails with `UnrecoverableError` —\n * retrying cannot make a missing definition appear, so it must not burn\n * through its attempts.\n */\nexport async function processJob(job: Job): Promise<unknown> {\n const definition = findRegisteredJob(job.name);\n\n if (!definition) {\n throw new UnrecoverableError(\n `No job named \"${job.name}\" is defined in this process. ` +\n \"Make sure the module that calls defineJob() is imported by the worker process.\",\n );\n }\n\n const { payload, context: captured } = unwrapPayload(job.data);\n\n const context: JobContext = {\n id: String(job.id),\n name: job.name,\n queue: job.queueName,\n attempt: job.attemptsMade + 1,\n maxAttempts: job.opts.attempts ?? 1,\n progress: (value) => job.updateProgress(value),\n log: async (line) => {\n await job.log(line);\n },\n context: captured,\n };\n\n const run = async () => definition.handle(payload, context);\n const queueContext = getQueueContext();\n\n try {\n return captured !== undefined && queueContext\n ? await queueContext.restore(captured, run)\n : await run();\n } catch (error) {\n if (error instanceof UnrecoverableJobError) {\n throw new UnrecoverableError(error.message);\n }\n\n throw error;\n }\n}\n","import { log } from \"@warlock.js/logger\";\nimport { Queue, Worker } from \"bullmq\";\nimport { getQueueConfig } from \"./config\";\nimport { onJobRegistered, queueOf, registeredJobs } from \"./job-registry\";\nimport { processJob } from \"./process-job\";\n\nconst DEFAULT_SHUTDOWN_TIMEOUT = 30_000;\n\nconst queues = new Map<string, Queue>();\nconst workers = new Map<string, Worker>();\nlet stopListening: (() => void) | undefined;\n\n/**\n * The BullMQ queue for `name`, created on first use with the configured\n * connection and prefix.\n */\nexport function getQueue(name: string): Queue {\n let queue = queues.get(name);\n\n if (!queue) {\n const config = getQueueConfig();\n\n queue = new Queue(name, {\n connection: config.connection,\n prefix: config.prefix ?? \"warlock\",\n });\n\n queue.on(\"error\", (error) => {\n log.error(\"queue\", \"connection\", error);\n });\n\n queues.set(name, queue);\n }\n\n return queue;\n}\n\n/**\n * Start one worker per queue that has a registered job, and keep starting\n * workers for queues whose first job is defined later.\n *\n * A no-op returning `[]` when `workers.enabled` is `false`, or when this\n * process does not serve the `worker` role (`Application.hasRole`) — a\n * process started with `--role=web`, for example, produces jobs but never\n * runs them in-process. Calling it again while workers run starts only the\n * missing ones.\n *\n * @returns the queue names that now have a worker in this process.\n */\nexport async function startWorkers(): Promise<string[]> {\n const { Application } = await import(\"@warlock.js/core\");\n\n if (!Application.hasRole(\"worker\")) {\n log.info(\n \"queue\",\n \"workers\",\n `queue: workers not started (role: ${[...Application.roles].join(\",\")})`,\n );\n return [];\n }\n\n const config = getQueueConfig();\n\n if (config.workers?.enabled === false) {\n return [];\n }\n\n for (const job of registeredJobs()) {\n ensureWorker(queueOf(job));\n }\n\n stopListening ??= onJobRegistered((job) => {\n ensureWorker(queueOf(job));\n });\n\n await Promise.all([...workers.values()].map((worker) => worker.waitUntilReady()));\n\n return [...workers.keys()];\n}\n\n/** The queue names with a running worker in this process. */\nexport function runningWorkers(): string[] {\n return [...workers.keys()];\n}\n\nfunction ensureWorker(queueName: string): void {\n if (workers.has(queueName)) {\n return;\n }\n\n const config = getQueueConfig();\n\n const worker = new Worker(queueName, processJob, {\n connection: config.connection,\n prefix: config.prefix ?? \"warlock\",\n concurrency: config.workers?.concurrency ?? 1,\n });\n\n worker.on(\"error\", (error) => {\n log.error(\"queue\", \"worker\", error);\n });\n\n worker.on(\"failed\", (job, error) => {\n log.error(\n \"queue\",\n \"job.failed\",\n `${job?.name ?? \"unknown\"} (${job?.id ?? \"?\"}): ${error.message}`,\n );\n });\n\n workers.set(queueName, worker);\n}\n\nexport type CloseQueueOptions = {\n /**\n * How long to wait for active jobs before force-closing workers, in\n * milliseconds. Default: `workers.shutdownTimeout`, else `30000`.\n */\n timeout?: number;\n};\n\n/**\n * Graceful shutdown: stop workers taking new jobs and wait for active ones\n * (bounded by `timeout`, then force-close), then close every queue\n * connection. Safe to call when nothing was started, and more than once.\n */\nexport async function closeQueue(options: CloseQueueOptions = {}): Promise<void> {\n stopListening?.();\n stopListening = undefined;\n\n const timeout = options.timeout ?? configuredShutdownTimeout();\n const closingWorkers = [...workers.values()];\n workers.clear();\n\n await Promise.all(closingWorkers.map((worker) => closeWorker(worker, timeout)));\n\n const closingQueues = [...queues.values()];\n queues.clear();\n\n await Promise.all(closingQueues.map((queue) => queue.close()));\n}\n\nfunction configuredShutdownTimeout(): number {\n try {\n return getQueueConfig().workers?.shutdownTimeout ?? DEFAULT_SHUTDOWN_TIMEOUT;\n } catch {\n return DEFAULT_SHUTDOWN_TIMEOUT;\n }\n}\n\nasync function closeWorker(worker: Worker, timeout: number): Promise<void> {\n let timer: NodeJS.Timeout | undefined;\n\n const timedOut = new Promise<\"timeout\">((resolve) => {\n timer = setTimeout(() => resolve(\"timeout\"), timeout);\n });\n\n // A close already in progress cannot be upgraded to a forced one (BullMQ\n // returns the pending promise), so the timeout path drops the connections\n // instead and lets the graceful close settle in the background.\n const closing = worker.close().then(() => \"closed\" as const);\n closing.catch(() => undefined);\n\n const outcome = await Promise.race([closing, timedOut]);\n\n clearTimeout(timer);\n\n if (outcome === \"timeout\") {\n log.warn(\n \"queue\",\n \"shutdown\",\n `Worker for \"${worker.name}\" still had active jobs after ${timeout}ms; disconnecting. ` +\n \"Those jobs are retried once their lock expires.\",\n );\n\n await worker.disconnect();\n }\n}\n","import type { Middleware } from \"@warlock.js/core\";\nimport { defaultQueueName } from \"./config\";\nimport { buildDashboardGuardPlugin } from \"./dashboard-guard-plugin\";\nimport { QueueDashboardDependencyError } from \"./errors\";\nimport { queueOf, registeredJobs } from \"./job-registry\";\nimport { getQueue } from \"./queue-manager\";\n\n/**\n * The part of a Fastify instance the dashboard needs. Warlock's HTTP server\n * (`getHttpServer()` from `@warlock.js/core`) satisfies it.\n */\nexport type DashboardServer = {\n register(plugin: never, options: { prefix: string }): unknown;\n};\n\nexport type QueueDashboardOptions = {\n /** URL prefix the dashboard is mounted on. Default `\"/admin/queues\"`. */\n basePath?: string;\n /** Queues to show. Default: every queue with a defined job, plus the default queue. */\n queues?: string[];\n /**\n * Run before every dashboard route. Applied via a wrapping Fastify plugin\n * scope, since bull-board's own plugin has no hook to splice Warlock\n * middleware into — see `dashboard-guard-plugin.ts`.\n */\n middleware?: Middleware[];\n};\n\ntype BullBoardModules = {\n createBullBoard: (options: { queues: unknown[]; serverAdapter: unknown }) => unknown;\n BullMQAdapter: new (queue: unknown) => unknown;\n FastifyAdapter: new () => {\n setBasePath(path: string): unknown;\n registerPlugin(): unknown;\n };\n};\n\n/**\n * Mount the bull-board failed/active/delayed job UI on Warlock's Fastify\n * server. Requires the optional packages `@bull-board/api` and\n * `@bull-board/fastify`; they are loaded only when this is called, and a\n * missing one throws {@link QueueDashboardDependencyError}.\n *\n * Call it before the HTTP server starts listening, and put it behind your\n * own authentication — the dashboard can retry and delete jobs.\n *\n * @example\n * import { getHttpServer } from \"@warlock.js/core\";\n * await queueDashboard(getHttpServer(), { basePath: \"/admin/queues\" });\n */\nexport async function queueDashboard(\n server: DashboardServer,\n options: QueueDashboardOptions = {},\n): Promise<void> {\n const basePath = options.basePath ?? \"/admin/queues\";\n const { createBullBoard, BullMQAdapter, FastifyAdapter } = await loadBullBoard();\n const queueNames = options.queues ?? [\n ...new Set([defaultQueueName(), ...registeredJobs().map((job) => queueOf(job))]),\n ];\n\n const serverAdapter = new FastifyAdapter();\n serverAdapter.setBasePath(basePath);\n\n createBullBoard({\n queues: queueNames.map((name) => new BullMQAdapter(getQueue(name))),\n serverAdapter,\n });\n\n const guardedPlugin = buildDashboardGuardPlugin(\n options.middleware ?? [],\n serverAdapter.registerPlugin(),\n );\n\n await server.register(guardedPlugin as never, { prefix: basePath });\n}\n\n/**\n * Load the optional bull-board packages. Exported for tests of the missing\n * dependency path; `importer` defaults to a real dynamic import.\n */\nexport async function loadBullBoard(\n importer: (specifier: string) => Promise<Record<string, unknown>> = (specifier) =>\n import(specifier),\n): Promise<BullBoardModules> {\n const api = await importOptional(importer, \"@bull-board/api\");\n const adapter = await importOptional(importer, \"@bull-board/api/bullMQAdapter\");\n const fastify = await importOptional(importer, \"@bull-board/fastify\");\n\n return {\n createBullBoard: api.createBullBoard as BullBoardModules[\"createBullBoard\"],\n BullMQAdapter: adapter.BullMQAdapter as BullBoardModules[\"BullMQAdapter\"],\n FastifyAdapter: fastify.FastifyAdapter as BullBoardModules[\"FastifyAdapter\"],\n };\n}\n\nasync function importOptional(\n importer: (specifier: string) => Promise<Record<string, unknown>>,\n specifier: string,\n): Promise<Record<string, unknown>> {\n try {\n return await importer(specifier);\n } catch (error) {\n if (isModuleNotFound(error)) {\n throw new QueueDashboardDependencyError(specifier.split(\"/\").slice(0, 2).join(\"/\"));\n }\n\n throw error;\n }\n}\n\nfunction isModuleNotFound(error: unknown): boolean {\n const code = (error as { code?: unknown } | undefined)?.code;\n\n return code === \"ERR_MODULE_NOT_FOUND\" || code === \"MODULE_NOT_FOUND\";\n}\n","/**\n * Thrown at boot when `queue.dashboard.enabled` is `true` in production with\n * no guard middleware. The dashboard can retry and delete jobs; mounting it\n * on the open internet without a guard is a production incident waiting to\n * happen, so this fails the boot instead of shipping the hole.\n */\nexport class QueueDashboardUnguardedError extends Error {\n public constructor() {\n super(\n \"queue.dashboard.enabled is true in production with no middleware. The dashboard can \" +\n \"retry and delete jobs, so it must be guarded before it is exposed.\\n\\n\" +\n \"Add a guard middleware:\\n\\n\" +\n ' import { middleware } from \"@warlock.js/core\";\\n' +\n ' import { authMiddleware } from \"@warlock.js/auth\";\\n\\n' +\n \" const queueConfig: QueueConfig = {\\n\" +\n \" // ...\\n\" +\n \" dashboard: {\\n\" +\n \" enabled: true,\\n\" +\n ' middleware: [authMiddleware(\"admin\")],\\n' +\n \" },\\n\" +\n \" };\\n\",\n );\n this.name = \"QueueDashboardUnguardedError\";\n }\n}\n","import { log } from \"@warlock.js/logger\";\nimport { type DashboardServer, queueDashboard } from \"./dashboard\";\nimport { QueueDashboardUnguardedError } from \"./queue-dashboard-unguarded.error\";\nimport type { QueueConfig } from \"./types\";\n\n/** Default path the dashboard mounts on when `dashboard.path` is not set. */\nexport const DEFAULT_DASHBOARD_PATH = \"/admin/queues\";\n\n/**\n * Mount the bull-board dashboard for `queue.dashboard` in `config`, applying\n * the safety rule: `enabled` in production with no middleware throws\n * {@link QueueDashboardUnguardedError} instead of booting exposed; outside\n * production with no middleware it logs one warning and mounts anyway.\n *\n * Called by `queueConnector()` at boot, once the HTTP server exists but\n * before it starts listening — see `queue-connector.ts`. Exported so it can\n * be unit-tested without going through the whole connector lifecycle.\n */\nexport async function mountQueueDashboard(\n server: DashboardServer | undefined,\n config: QueueConfig,\n): Promise<void> {\n const dashboard = config.dashboard;\n\n if (!dashboard?.enabled) {\n return;\n }\n\n const middlewareList = dashboard.middleware ?? [];\n\n if (middlewareList.length === 0) {\n if (process.env.NODE_ENV === \"production\") {\n throw new QueueDashboardUnguardedError();\n }\n\n log.warn(\n \"queue\",\n \"dashboard\",\n \"queue.dashboard.enabled is true with no middleware. The dashboard can retry and delete \" +\n \"jobs — add a guard middleware before this ships to production.\",\n );\n }\n\n if (!server) {\n log.warn(\n \"queue\",\n \"dashboard\",\n \"queue.dashboard.enabled is true but no HTTP server was found; the dashboard was not mounted.\",\n );\n\n return;\n }\n\n await queueDashboard(server, {\n basePath: dashboard.path ?? DEFAULT_DASHBOARD_PATH,\n middleware: middlewareList,\n });\n}\n","import type { DispatchOptions, DispatchedJob, JobSnapshot } from \"./types\";\n\n/**\n * A stand-in for the broker, used by test fakes.\n *\n * When one is installed, `job.dispatch()` hands the job to it instead of\n * adding it to BullMQ, and `job.find()` asks it instead of Redis. Nothing\n * connects to Redis while it is installed.\n *\n * `@warlock.js/core/tests` installs one as `queueFake` in the test\n * environment; this package exposes the seam so core needs no dependency on\n * it. The slot lives on `globalThis` so every copy of this package in a\n * process (a Vitest worker can load two) sees the same one.\n */\nexport type DispatchInterceptor = {\n dispatch(job: {\n name: string;\n queue: string;\n payload: unknown;\n options: DispatchOptions;\n }): DispatchedJob | Promise<DispatchedJob>;\n find?(job: {\n name: string;\n queue: string;\n id: string;\n }): JobSnapshot | undefined | Promise<JobSnapshot | undefined>;\n};\n\nconst INTERCEPTOR_KEY = Symbol.for(\"warlock.queue.dispatchInterceptor\");\n\ntype InterceptorHolder = { [INTERCEPTOR_KEY]?: DispatchInterceptor };\n\n/** Install an interceptor, or remove it with `undefined`. */\nexport function setDispatchInterceptor(interceptor: DispatchInterceptor | undefined): void {\n (globalThis as InterceptorHolder)[INTERCEPTOR_KEY] = interceptor;\n}\n\n/** The installed interceptor, if any. */\nexport function getDispatchInterceptor(): DispatchInterceptor | undefined {\n return (globalThis as InterceptorHolder)[INTERCEPTOR_KEY];\n}\n","import { InvalidDurationError } from \"./errors\";\nimport type { Duration } from \"./types\";\n\nconst UNIT_MILLISECONDS: Record<string, number> = {\n ms: 1,\n s: 1_000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n};\n\nconst DURATION_PATTERN = /^(\\d+(?:\\.\\d+)?)(ms|s|m|h|d)$/;\n\n/**\n * Convert a {@link Duration} to milliseconds. Numbers are already\n * milliseconds. Anything else is rejected loudly rather than guessed at.\n */\nexport function toMilliseconds(value: Duration | string): number {\n if (typeof value === \"number\") {\n if (!Number.isFinite(value) || value < 0) {\n throw new InvalidDurationError(value);\n }\n\n return Math.round(value);\n }\n\n const match = typeof value === \"string\" ? DURATION_PATTERN.exec(value.trim()) : null;\n\n if (!match) {\n throw new InvalidDurationError(value);\n }\n\n return Math.round(Number(match[1]) * UNIT_MILLISECONDS[match[2]!]!);\n}\n","import type { Job, JobsOptions } from \"bullmq\";\nimport { getQueueConfig } from \"./config\";\nimport { getDispatchInterceptor } from \"./dispatch-interceptor\";\nimport { toMilliseconds } from \"./duration\";\nimport { InvalidJobDefinitionError } from \"./errors\";\nimport { getQueueContext, unwrapPayload, wrapPayload } from \"./queue-context\";\nimport { queueOf, registerJob, type RegisteredJob } from \"./job-registry\";\nimport { getQueue } from \"./queue-manager\";\nimport type {\n DispatchOptions,\n JobBackoff,\n JobDefinition,\n JobOptions,\n JobSnapshot,\n JobState,\n QueueJob,\n} from \"./types\";\n\n/**\n * Define a background job.\n *\n * The definition is registered by name so any worker in the process can run\n * it; the returned object dispatches it with a typed payload.\n *\n * @example\n * export const sendInvoice = defineJob({\n * name: \"invoices.send\",\n * attempts: 5,\n * backoff: { type: \"exponential\", delay: 2000 },\n * async handle(payload: { invoiceId: string }, ctx) {\n * await ctx.progress(50);\n * },\n * });\n *\n * await sendInvoice.dispatch({ invoiceId: \"42\" }, { delay: \"10m\", priority: 1 });\n */\nexport function defineJob<TPayload, TResult = unknown>(\n definition: JobDefinition<TPayload, TResult>,\n): QueueJob<TPayload, TResult> {\n assertValidDefinition(definition);\n registerJob(definition as RegisteredJob);\n\n return {\n name: definition.name,\n get queue() {\n return queueOf(definition);\n },\n async dispatch(payload, options = {}) {\n const queueName = queueOf(definition);\n const interceptor = getDispatchInterceptor();\n\n // A test fake stands in for the broker: record, never touch Redis.\n if (interceptor) {\n return interceptor.dispatch({ name: definition.name, queue: queueName, payload, options });\n }\n\n // Capture synchronously, in the caller's async context.\n const captured = definition.context === false ? undefined : getQueueContext()?.capture();\n const job = await getQueue(queueName).add(\n definition.name,\n wrapPayload(payload, captured),\n toBullJobOptions(definition, options),\n );\n\n return { id: String(job.id), name: definition.name, queue: queueName };\n },\n async find(id) {\n const interceptor = getDispatchInterceptor();\n\n if (interceptor) {\n const found = await interceptor.find?.({\n name: definition.name,\n queue: queueOf(definition),\n id,\n });\n\n return found as JobSnapshot<TPayload, TResult> | undefined;\n }\n\n const queue = getQueue(queueOf(definition));\n const initialJob = await queue.getJob(id);\n\n if (!initialJob || initialJob.name !== definition.name) {\n return undefined;\n }\n\n // Read the state first, then (re)fetch the job. BullMQ writes a job's\n // result/attemptsMade/finishedOn fields *before* it becomes visible\n // under a new state, so re-reading the job after the state is known\n // guarantees those fields are consistent with the reported state\n // (rather than reflecting a moment before the job finished).\n const state = (await initialJob.getState()) as JobState;\n const job = (await queue.getJob(id)) ?? initialJob;\n\n return toSnapshot<TPayload, TResult>(job, state);\n },\n };\n}\n\nfunction assertValidDefinition(definition: JobDefinition<unknown, unknown>): void {\n if (typeof definition.name !== \"string\" || definition.name.trim() === \"\") {\n throw new InvalidJobDefinitionError(\"defineJob() requires a non-empty `name`.\");\n }\n\n if (typeof definition.handle !== \"function\") {\n throw new InvalidJobDefinitionError(\n `defineJob(\"${definition.name}\") requires a \\`handle(payload, ctx)\\` function.`,\n );\n }\n\n if (\n definition.attempts !== undefined &&\n !(Number.isInteger(definition.attempts) && definition.attempts >= 1)\n ) {\n throw new InvalidJobDefinitionError(\n `defineJob(\"${definition.name}\"): \\`attempts\\` must be an integer >= 1, got ${definition.attempts}.`,\n );\n }\n}\n\n/**\n * Merge app defaults < job definition < dispatch options into BullMQ's shape.\n */\nfunction toBullJobOptions(definition: JobOptions, options: DispatchOptions): JobsOptions {\n const defaults = getQueueConfig().defaultJobOptions ?? {};\n const attempts = options.attempts ?? definition.attempts ?? defaults.attempts;\n const backoff = options.backoff ?? definition.backoff ?? defaults.backoff;\n const removeOnComplete = definition.removeOnComplete ?? defaults.removeOnComplete;\n const removeOnFail = definition.removeOnFail ?? defaults.removeOnFail;\n\n const bullOptions: JobsOptions = {};\n\n if (attempts !== undefined) bullOptions.attempts = attempts;\n if (backoff !== undefined) bullOptions.backoff = toBullBackoff(backoff);\n if (removeOnComplete !== undefined) bullOptions.removeOnComplete = removeOnComplete;\n if (removeOnFail !== undefined) bullOptions.removeOnFail = removeOnFail;\n if (options.delay !== undefined) bullOptions.delay = toMilliseconds(options.delay);\n if (options.priority !== undefined) bullOptions.priority = options.priority;\n if (options.jobId !== undefined) bullOptions.jobId = options.jobId;\n\n return bullOptions;\n}\n\nfunction toBullBackoff(backoff: JobBackoff): JobsOptions[\"backoff\"] {\n return typeof backoff === \"number\" ? { type: \"fixed\", delay: backoff } : backoff;\n}\n\n/**\n * A plain view of a BullMQ job.\n *\n * @param job The job to read fields from.\n * @param state The job's state; pass a state read *before* `job` was\n * fetched (or re-fetched) so the returned snapshot's fields are consistent\n * with it. If omitted, the state is read from `job` directly.\n */\nexport async function toSnapshot<TPayload, TResult>(\n job: Job,\n state?: JobState,\n): Promise<JobSnapshot<TPayload, TResult>> {\n const resolvedState = state ?? ((await job.getState()) as JobState);\n\n return {\n id: String(job.id),\n name: job.name,\n queue: job.queueName,\n state: resolvedState,\n payload: unwrapPayload(job.data).payload as TPayload,\n progress: job.progress as JobSnapshot[\"progress\"],\n attemptsMade: job.attemptsMade,\n result: job.returnvalue as TResult | undefined,\n failedReason: job.failedReason || undefined,\n createdAt: new Date(job.timestamp),\n finishedAt: job.finishedOn ? new Date(job.finishedOn) : undefined,\n };\n}\n","import { unwrapPayload } from \"./queue-context\";\nimport type { Job } from \"bullmq\";\nimport { defaultQueueName } from \"./config\";\nimport { FailedJobNotFoundError } from \"./errors\";\nimport { getQueue } from \"./queue-manager\";\nimport type { FailedJob } from \"./types\";\n\nexport type FailedJobsOptions = {\n /** Queue to read. Default: the default queue. */\n queue?: string;\n /** First index (newest first). Default `0`. */\n start?: number;\n /** Last index, inclusive. Default `99`. */\n end?: number;\n};\n\n/**\n * List failed jobs, newest first — jobs that used up every attempt, or\n * failed unrecoverably. Each entry can be retried.\n */\nexport async function failedJobs(options: FailedJobsOptions = {}): Promise<FailedJob[]> {\n const queueName = options.queue ?? defaultQueueName();\n const jobs = await getQueue(queueName).getFailed(options.start ?? 0, options.end ?? 99);\n\n return jobs.map((job) => toFailedJob(job));\n}\n\n/**\n * Retry one failed job by id: it goes back to waiting with its attempts reset.\n * Throws {@link FailedJobNotFoundError} when no FAILED job has that id.\n */\nexport async function retryFailedJob(id: string, options: { queue?: string } = {}): Promise<void> {\n const queueName = options.queue ?? defaultQueueName();\n const job = await getQueue(queueName).getJob(id);\n\n if (!job || !(await job.isFailed())) {\n throw new FailedJobNotFoundError(id, queueName);\n }\n\n await job.retry(\"failed\");\n}\n\nfunction toFailedJob(job: Job): FailedJob {\n return {\n id: String(job.id),\n name: job.name,\n queue: job.queueName,\n payload: unwrapPayload(job.data).payload,\n attemptsMade: job.attemptsMade,\n failedReason: job.failedReason,\n stacktrace: job.stacktrace ?? [],\n failedAt: job.finishedOn ? new Date(job.finishedOn) : undefined,\n retry: () => job.retry(\"failed\"),\n };\n}\n","/**\n * The queue's connector for `warlock.config.ts > connectors`.\n *\n * Deliberately a plain object with TYPE-ONLY imports from core: the config\n * file that constructs it must not drag core's runtime graph (or BullMQ) in\n * at config-load time. Core is imported lazily inside `start()`, where the\n * app has already loaded it.\n */\nimport type { Connector, ConnectorLifecyclePhase } from \"@warlock.js/core\";\nimport { log } from \"@warlock.js/logger\";\nimport { resetQueueConfig, setQueueConfig } from \"./config\";\nimport { mountQueueDashboard } from \"./dashboard-boot\";\nimport { closeQueue, startWorkers } from \"./queue-manager\";\nimport type { QueueConfig } from \"./types\";\n\n/**\n * Boots after every built-in connector (`ConnectorPriority.AI` is `10`), so\n * the logger is up and anything a job handler needs is already connected;\n * shuts down before them for the same reason.\n */\nexport const QUEUE_CONNECTOR_PRIORITY = 11;\n\nconst WATCHED_FILES = [\"src/config/queue.ts\"];\n\nexport type QueueConnectorOptions = {\n /**\n * Supply the configuration directly instead of reading the `queue` config\n * key (`src/config/queue.ts`).\n */\n config?: QueueConfig;\n};\n\n/**\n * Construct the queue connector.\n *\n * Runs in the `late` lifecycle phase — after app code is imported — so every\n * `defineJob` in the app has registered before workers start. At start it\n * reads the `queue` config and starts in-process workers unless\n * `workers.enabled` is `false`; at shutdown it closes workers (waiting for\n * active jobs, bounded by `workers.shutdownTimeout`) and then the queues.\n *\n * @example\n * // warlock.config.ts\n * import { queueConnector } from \"@warlock.js/queue\";\n *\n * export default defineConfig({ connectors: [queueConnector()] });\n */\nexport function queueConnector(options: QueueConnectorOptions = {}): Connector {\n let active = false;\n\n const connector: Connector = {\n name: \"queue\",\n priority: QUEUE_CONNECTOR_PRIORITY,\n // Core's `ConnectorLifecyclePhase.Late`; the value is spelled out so this\n // module stays free of a runtime import of core.\n lifecyclePhase: \"late\" as ConnectorLifecyclePhase,\n isActive: () => active,\n /**\n * Mounts the dashboard, when configured, here rather than in `start()`:\n * `boot()` runs for every late-phase connector, in priority order, before\n * any of them `start()`s — so by the time this runs, the HTTP connector\n * (priority 5, before queue's 11) has already built its Fastify instance\n * and registered its own plugins, but has not yet called `listen()`.\n * Fastify refuses new plugin registrations after `listen()`, so this is\n * the only point in the boot sequence where mounting is possible.\n */\n async boot() {\n const queueConfig = options.config ?? (await readQueueConfig());\n\n if (!queueConfig?.dashboard?.enabled) {\n return;\n }\n\n // The dashboard resolves queues through the active config, so it has to\n // be registered here rather than only in `start()`, which runs after\n // every late connector has booted. `start()` sets it again; the setter\n // is idempotent for the same object.\n setQueueConfig(queueConfig);\n\n const { getHttpServer } = await import(\"@warlock.js/core\");\n\n await mountQueueDashboard(getHttpServer(), queueConfig);\n },\n async start() {\n const queueConfig = options.config ?? (await readQueueConfig());\n\n if (!queueConfig) {\n log.warn(\n \"queue\",\n \"configured\",\n \"queueConnector() is registered but no `queue` config was found (src/config/queue.ts); queue not started\",\n );\n return;\n }\n\n setQueueConfig(queueConfig);\n const started = await startWorkers();\n active = true;\n\n log.info(\n \"queue\",\n \"configured\",\n started.length > 0\n ? `Queue workers running for: ${started.join(\", \")}`\n : \"Queue configured (no in-process workers)\",\n );\n },\n async restart() {\n await connector.shutdown();\n await connector.start();\n },\n async shutdown() {\n if (!active) {\n return;\n }\n\n await closeQueue();\n resetQueueConfig();\n active = false;\n },\n shouldRestart(changedFiles) {\n return changedFiles.some((file) => {\n const normalized = file.replace(/\\\\/g, \"/\");\n\n return WATCHED_FILES.some(\n (watched) => normalized === watched || normalized.endsWith(`/${watched}`),\n );\n });\n },\n };\n\n return connector;\n}\n\nasync function readQueueConfig(): Promise<QueueConfig | undefined> {\n const { config } = await import(\"@warlock.js/core\");\n\n return config.get<QueueConfig | undefined>(\"queue\");\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["UnrecoverableError","Queue","Worker"],"sources":["../../../../../../../packages/queue/src/errors.ts","../../../../../../../packages/queue/src/config.ts","../../../../../../../packages/queue/src/dashboard-middleware-adapter.ts","../../../../../../../packages/queue/src/dashboard-guard-plugin.ts","../../../../../../../packages/queue/src/job-registry.ts","../../../../../../../packages/queue/src/queue-context.ts","../../../../../../../packages/queue/src/process-job.ts","../../../../../../../packages/queue/src/queue-manager.ts","../../../../../../../packages/queue/src/dashboard.ts","../../../../../../../packages/queue/src/queue-dashboard-unguarded.error.ts","../../../../../../../packages/queue/src/dashboard-boot.ts","../../../../../../../packages/queue/src/dispatch-interceptor.ts","../../../../../../../packages/queue/src/duration.ts","../../../../../../../packages/queue/src/define-job.ts","../../../../../../../packages/queue/src/failed-jobs.ts","../../../../../../../packages/queue/src/queue-connector.ts"],"sourcesContent":["/**\n * Thrown when the queue is used before `setQueueConfig` (or the queue\n * connector) supplied a configuration.\n */\nexport class QueueNotConfiguredError extends Error {\n public constructor() {\n super(\n \"@warlock.js/queue is not configured. Add src/config/queue.ts exporting a QueueConfig \" +\n \"and register queueConnector() in warlock.config.ts > connectors, \" +\n \"or call setQueueConfig() yourself.\",\n );\n this.name = \"QueueNotConfiguredError\";\n }\n}\n\n/**\n * Thrown for a malformed duration such as `\"10 minutes\"`.\n */\nexport class InvalidDurationError extends Error {\n public constructor(value: unknown) {\n super(\n `Invalid duration ${JSON.stringify(value)}: expected milliseconds as a non-negative number ` +\n `or a string like \"500ms\", \"30s\", \"10m\", \"2h\", \"1d\".`,\n );\n this.name = \"InvalidDurationError\";\n }\n}\n\n/**\n * Thrown by `defineJob` for an invalid definition.\n */\nexport class InvalidJobDefinitionError extends Error {\n public constructor(message: string) {\n super(message);\n this.name = \"InvalidJobDefinitionError\";\n }\n}\n\n/**\n * Thrown by `retryFailedJob` when no failed job has the given id.\n */\nexport class FailedJobNotFoundError extends Error {\n public constructor(id: string, queue: string) {\n super(`No failed job with id \"${id}\" on queue \"${queue}\".`);\n this.name = \"FailedJobNotFoundError\";\n }\n}\n\n/**\n * Thrown by `queueDashboard` when an optional bull-board package is not\n * installed.\n */\nexport class QueueDashboardDependencyError extends Error {\n public constructor(missing: string) {\n super(\n `The queue dashboard needs the optional package \"${missing}\", which is not installed.\\n` +\n \"Install both bull-board packages:\\n\\n\" +\n \" npm install @bull-board/api @bull-board/fastify\\n\",\n );\n this.name = \"QueueDashboardDependencyError\";\n }\n}\n\n/**\n * Throw from a job handler (or a queue context's `restore`) to fail the job\n * permanently — it is not retried, whatever `attempts` allows.\n */\nexport class UnrecoverableJobError extends Error {\n public constructor(message: string, options?: { cause?: unknown }) {\n super(message, options);\n this.name = \"UnrecoverableJobError\";\n }\n}\n","import { QueueNotConfiguredError } from \"./errors\";\nimport type { QueueConfig } from \"./types\";\n\nlet activeConfig: QueueConfig | undefined;\n\n/**\n * Set the active queue configuration. In a Warlock app the queue connector\n * calls this at boot with `src/config/queue.ts`; scripts and tests may call\n * it directly. Replaces (does not merge) any previous configuration.\n */\nexport function setQueueConfig(config: QueueConfig): void {\n activeConfig = config;\n}\n\n/**\n * The active queue configuration. Throws {@link QueueNotConfiguredError}\n * when none was set.\n */\nexport function getQueueConfig(): QueueConfig {\n if (!activeConfig) {\n throw new QueueNotConfiguredError();\n }\n\n return activeConfig;\n}\n\n/** Forget the active configuration. */\nexport function resetQueueConfig(): void {\n activeConfig = undefined;\n}\n\n/** The queue a job runs on when it names none. */\nexport function defaultQueueName(): string {\n return activeConfig?.defaultQueue ?? \"default\";\n}\n","import type { Middleware } from \"@warlock.js/core\";\nimport type { FastifyReply, FastifyRequest } from \"fastify\";\n\n/**\n * Run a Warlock middleware list against a raw Fastify request/reply pair.\n *\n * Bull-board's `FastifyAdapter` hands back a plain Fastify plugin, not a\n * Warlock route — there is no `Route`, no validation pipeline, and none of\n * `createRequestStore`'s context wiring (CSP, tracing, `@warlock.js/context`\n * stores). Reusing that full pipeline here would pull the whole request\n * machinery into a place it was never meant to run. Instead this builds the\n * minimal `Request`/`Response` pair — enough for guard-style middleware\n * (`authMiddleware`, `ipFilterMiddleware`, a custom check) to read the\n * request and short-circuit with a response, which covers every one of\n * bull-board's routes because they all sit behind the same hook.\n *\n * `@warlock.js/core` is imported dynamically so this module never drags\n * core's runtime graph into a process that never mounts the dashboard.\n *\n * @returns `true` when a middleware sent a response and the caller must not\n * continue (bull-board's handler must not run); `false` to continue.\n */\nexport async function runDashboardMiddleware(\n middlewareList: Middleware[],\n fastifyRequest: FastifyRequest,\n fastifyReply: FastifyReply,\n): Promise<boolean> {\n if (middlewareList.length === 0) {\n return false;\n }\n\n const { Request, Response } = await import(\"@warlock.js/core\");\n\n const request = new Request();\n const response = new Response();\n\n response.setResponse(fastifyReply);\n request.response = response;\n response.request = request;\n request.setRequest(fastifyRequest);\n\n for (const middlewareFunction of middlewareList) {\n const result = await middlewareFunction({ request, response });\n\n if (result) {\n return true;\n }\n }\n\n return false;\n}\n","import type { Middleware } from \"@warlock.js/core\";\nimport type { FastifyInstance, FastifyPluginCallback } from \"fastify\";\nimport { runDashboardMiddleware } from \"./dashboard-middleware-adapter\";\n\n/**\n * Wrap bull-board's Fastify plugin in a scope that runs `middlewareList`\n * on every request before bull-board's own routes see it.\n *\n * Bull-board's `FastifyAdapter.registerPlugin()` returns a plain Fastify\n * plugin with no hook for Warlock middleware to run through, and Fastify\n * only lets an `onRequest` hook be added to a plugin scope — never spliced\n * into a plugin someone else wrote. So this builds ONE plugin that adds the\n * hook to its own scope and then registers bull-board's plugin as a child of\n * that scope; Fastify's encapsulation runs the hook for every route the\n * child registers, which is every dashboard route.\n */\nexport function buildDashboardGuardPlugin(\n middlewareList: Middleware[],\n bullBoardPlugin: unknown,\n): FastifyPluginCallback {\n return function dashboardGuardPlugin(instance: FastifyInstance, _options, done) {\n if (middlewareList.length > 0) {\n instance.addHook(\"onRequest\", async (request, reply) => {\n const handled = await runDashboardMiddleware(middlewareList, request, reply);\n\n if (handled) {\n // Returning the reply is Fastify's own way for an `onRequest` hook to\n // END the lifecycle, so bull-board's handler never runs. Relying\n // instead on Fastify noticing the reply was already sent leaves the\n // outcome to write ORDERING: a guard answering asynchronously can lose\n // that race and let the dashboard render to an unauthenticated caller.\n // The adapter returns this boolean precisely so the decision is\n // explicit rather than emergent.\n return reply;\n }\n });\n }\n\n instance.register(bullBoardPlugin as never);\n done();\n };\n}\n","import { defaultQueueName } from \"./config\";\nimport type { JobDefinition } from \"./types\";\n\n/** A registered definition, payload/result erased for storage. */\nexport type RegisteredJob = JobDefinition<unknown, unknown>;\n\ntype RegistryListener = (job: RegisteredJob) => void;\n\nconst jobs = new Map<string, RegisteredJob>();\nconst listeners = new Set<RegistryListener>();\n\n/**\n * Register a definition under its name.\n *\n * Re-registering a name REPLACES the previous definition: in development a\n * job module is re-evaluated on every reload, and refusing the second\n * evaluation would break the reload. Job names must therefore be unique\n * across the app — two different modules using one name leave only the\n * later handler active.\n */\nexport function registerJob(job: RegisteredJob): void {\n jobs.set(job.name, job);\n\n for (const listener of listeners) {\n listener(job);\n }\n}\n\n/** The definition registered under `name`, if any. */\nexport function findRegisteredJob(name: string): RegisteredJob | undefined {\n return jobs.get(name);\n}\n\n/** Every registered definition. */\nexport function registeredJobs(): RegisteredJob[] {\n return [...jobs.values()];\n}\n\n/** The queue a definition runs on, resolved against the active config. */\nexport function queueOf(job: Pick<RegisteredJob, \"queue\">): string {\n return job.queue ?? defaultQueueName();\n}\n\n/** Be told whenever a job is registered. Returns an unsubscribe function. */\nexport function onJobRegistered(listener: RegistryListener): () => void {\n listeners.add(listener);\n\n return () => listeners.delete(listener);\n}\n","/**\n * Carries ambient state (tenant, request id, …) from the code that\n * dispatches a job into the worker that runs it.\n */\nexport type QueueContext<T> = {\n /** Read the ambient state at dispatch time; `undefined` means nothing to carry. */\n capture: () => T | undefined;\n /** Run `run` with the captured state re-established. */\n restore: (captured: T, run: () => Promise<unknown>) => Promise<unknown>;\n};\n\n/**\n * Define a queue context. `restore`'s `captured` is inferred from `capture`.\n *\n * @example\n * setQueueContext(\n * defineQueueContext({\n * capture: () => tenantStorage.getStore(),\n * restore: (tenant, run) => tenantStorage.run(tenant, run),\n * }),\n * );\n */\nexport function defineQueueContext<T>(context: QueueContext<T>): QueueContext<T> {\n return context;\n}\n\nlet activeContext: QueueContext<any> | undefined;\n\n/** Register the queue context used by every job dispatch and run. */\nexport function setQueueContext(context: QueueContext<any>): void {\n activeContext = context;\n}\n\n/** The registered queue context, if any. */\nexport function getQueueContext(): QueueContext<any> | undefined {\n return activeContext;\n}\n\n/** Forget the registered queue context. */\nexport function resetQueueContext(): void {\n activeContext = undefined;\n}\n\n/** The job-data envelope written when a context was captured. */\nexport type QueueEnvelope = {\n __warlock: { v: 1; context?: unknown };\n payload: unknown;\n};\n\n/** Wrap a payload with the captured context; no context leaves it bare. */\nexport function wrapPayload(payload: unknown, context: unknown): unknown {\n if (context === undefined) return payload;\n\n return { __warlock: { v: 1, context }, payload } satisfies QueueEnvelope;\n}\n\n/** Split job data into payload and captured context; bare data passes through. */\nexport function unwrapPayload(data: unknown): { payload: unknown; context: unknown } {\n const envelope = data as Partial<QueueEnvelope> | null;\n\n if (\n envelope !== null &&\n typeof envelope === \"object\" &&\n typeof envelope.__warlock === \"object\" &&\n envelope.__warlock !== null &&\n envelope.__warlock.v === 1 &&\n \"payload\" in envelope\n ) {\n return { payload: envelope.payload, context: envelope.__warlock.context };\n }\n\n return { payload: data, context: undefined };\n}\n","import { type Job, UnrecoverableError } from \"bullmq\";\nimport { UnrecoverableJobError } from \"./errors\";\nimport { findRegisteredJob } from \"./job-registry\";\nimport { getQueueContext, unwrapPayload } from \"./queue-context\";\nimport type { JobContext } from \"./types\";\n\n/**\n * The single BullMQ processor every worker runs: route the job to the\n * handler registered under its name.\n *\n * A name with no handler in this process fails with `UnrecoverableError` —\n * retrying cannot make a missing definition appear, so it must not burn\n * through its attempts.\n */\nexport async function processJob(job: Job): Promise<unknown> {\n const definition = findRegisteredJob(job.name);\n\n if (!definition) {\n throw new UnrecoverableError(\n `No job named \"${job.name}\" is defined in this process. ` +\n \"Make sure the module that calls defineJob() is imported by the worker process.\",\n );\n }\n\n const { payload, context: captured } = unwrapPayload(job.data);\n\n const context: JobContext = {\n id: String(job.id),\n name: job.name,\n queue: job.queueName,\n attempt: job.attemptsMade + 1,\n maxAttempts: job.opts.attempts ?? 1,\n progress: (value) => job.updateProgress(value),\n log: async (line) => {\n await job.log(line);\n },\n context: captured,\n };\n\n const run = async () => definition.handle(payload, context);\n const queueContext = getQueueContext();\n\n try {\n return captured !== undefined && queueContext\n ? await queueContext.restore(captured, run)\n : await run();\n } catch (error) {\n if (error instanceof UnrecoverableJobError) {\n throw new UnrecoverableError(error.message);\n }\n\n throw error;\n }\n}\n","import { log } from \"@warlock.js/logger\";\nimport { Queue, Worker } from \"bullmq\";\nimport { getQueueConfig } from \"./config\";\nimport { onJobRegistered, queueOf, registeredJobs } from \"./job-registry\";\nimport { processJob } from \"./process-job\";\n\nconst DEFAULT_SHUTDOWN_TIMEOUT = 30_000;\n\nconst queues = new Map<string, Queue>();\nconst workers = new Map<string, Worker>();\nlet stopListening: (() => void) | undefined;\n\n/**\n * The BullMQ queue for `name`, created on first use with the configured\n * connection and prefix.\n */\nexport function getQueue(name: string): Queue {\n let queue = queues.get(name);\n\n if (!queue) {\n const config = getQueueConfig();\n\n queue = new Queue(name, {\n connection: config.connection,\n prefix: config.prefix ?? \"warlock\",\n });\n\n queue.on(\"error\", (error) => {\n log.error(\"queue\", \"connection\", error);\n });\n\n queues.set(name, queue);\n }\n\n return queue;\n}\n\n/**\n * Start one worker per queue that has a registered job, and keep starting\n * workers for queues whose first job is defined later.\n *\n * A no-op returning `[]` when `workers.enabled` is `false`, or when this\n * process does not serve the `worker` role (`Application.hasRole`) — a\n * process started with `--role=web`, for example, produces jobs but never\n * runs them in-process. Calling it again while workers run starts only the\n * missing ones.\n *\n * @returns the queue names that now have a worker in this process.\n */\nexport async function startWorkers(): Promise<string[]> {\n const { Application } = await import(\"@warlock.js/core\");\n\n if (!Application.hasRole(\"worker\")) {\n log.info(\n \"queue\",\n \"workers\",\n `queue: workers not started (role: ${[...Application.roles].join(\",\")})`,\n );\n return [];\n }\n\n const config = getQueueConfig();\n\n if (config.workers?.enabled === false) {\n return [];\n }\n\n for (const job of registeredJobs()) {\n ensureWorker(queueOf(job));\n }\n\n stopListening ??= onJobRegistered((job) => {\n ensureWorker(queueOf(job));\n });\n\n await Promise.all([...workers.values()].map((worker) => worker.waitUntilReady()));\n\n return [...workers.keys()];\n}\n\n/** The queue names with a running worker in this process. */\nexport function runningWorkers(): string[] {\n return [...workers.keys()];\n}\n\nfunction ensureWorker(queueName: string): void {\n if (workers.has(queueName)) {\n return;\n }\n\n const config = getQueueConfig();\n\n const worker = new Worker(queueName, processJob, {\n connection: config.connection,\n prefix: config.prefix ?? \"warlock\",\n concurrency: config.workers?.concurrency ?? 1,\n });\n\n worker.on(\"error\", (error) => {\n log.error(\"queue\", \"worker\", error);\n });\n\n worker.on(\"failed\", (job, error) => {\n log.error(\n \"queue\",\n \"job.failed\",\n `${job?.name ?? \"unknown\"} (${job?.id ?? \"?\"}): ${error.message}`,\n );\n });\n\n workers.set(queueName, worker);\n}\n\nexport type CloseQueueOptions = {\n /**\n * How long to wait for active jobs before force-closing workers, in\n * milliseconds. Default: `workers.shutdownTimeout`, else `30000`.\n */\n timeout?: number;\n};\n\n/**\n * Graceful shutdown: stop workers taking new jobs and wait for active ones\n * (bounded by `timeout`, then force-close), then close every queue\n * connection. Safe to call when nothing was started, and more than once.\n */\nexport async function closeQueue(options: CloseQueueOptions = {}): Promise<void> {\n stopListening?.();\n stopListening = undefined;\n\n const timeout = options.timeout ?? configuredShutdownTimeout();\n const closingWorkers = [...workers.values()];\n workers.clear();\n\n await Promise.all(closingWorkers.map((worker) => closeWorker(worker, timeout)));\n\n const closingQueues = [...queues.values()];\n queues.clear();\n\n await Promise.all(closingQueues.map((queue) => queue.close()));\n}\n\nfunction configuredShutdownTimeout(): number {\n try {\n return getQueueConfig().workers?.shutdownTimeout ?? DEFAULT_SHUTDOWN_TIMEOUT;\n } catch {\n return DEFAULT_SHUTDOWN_TIMEOUT;\n }\n}\n\nasync function closeWorker(worker: Worker, timeout: number): Promise<void> {\n let timer: NodeJS.Timeout | undefined;\n\n const timedOut = new Promise<\"timeout\">((resolve) => {\n timer = setTimeout(() => resolve(\"timeout\"), timeout);\n });\n\n // A close already in progress cannot be upgraded to a forced one (BullMQ\n // returns the pending promise), so the timeout path drops the connections\n // instead and lets the graceful close settle in the background.\n const closing = worker.close().then(() => \"closed\" as const);\n closing.catch(() => undefined);\n\n const outcome = await Promise.race([closing, timedOut]);\n\n clearTimeout(timer);\n\n if (outcome === \"timeout\") {\n log.warn(\n \"queue\",\n \"shutdown\",\n `Worker for \"${worker.name}\" still had active jobs after ${timeout}ms; disconnecting. ` +\n \"Those jobs are retried once their lock expires.\",\n );\n\n await worker.disconnect();\n }\n}\n","import type { Middleware } from \"@warlock.js/core\";\nimport { defaultQueueName } from \"./config\";\nimport { buildDashboardGuardPlugin } from \"./dashboard-guard-plugin\";\nimport { QueueDashboardDependencyError } from \"./errors\";\nimport { queueOf, registeredJobs } from \"./job-registry\";\nimport { getQueue } from \"./queue-manager\";\n\n/**\n * The part of a Fastify instance the dashboard needs. Warlock's HTTP server\n * (`getHttpServer()` from `@warlock.js/core`) satisfies it.\n */\nexport type DashboardServer = {\n register(plugin: never, options: { prefix: string }): unknown;\n};\n\nexport type QueueDashboardOptions = {\n /** URL prefix the dashboard is mounted on. Default `\"/admin/queues\"`. */\n basePath?: string;\n /** Queues to show. Default: every queue with a defined job, plus the default queue. */\n queues?: string[];\n /**\n * Run before every dashboard route. Applied via a wrapping Fastify plugin\n * scope, since bull-board's own plugin has no hook to splice Warlock\n * middleware into — see `dashboard-guard-plugin.ts`.\n */\n middleware?: Middleware[];\n};\n\ntype BullBoardModules = {\n createBullBoard: (options: { queues: unknown[]; serverAdapter: unknown }) => unknown;\n BullMQAdapter: new (queue: unknown) => unknown;\n FastifyAdapter: new () => {\n setBasePath(path: string): unknown;\n registerPlugin(): unknown;\n };\n};\n\n/**\n * Mount the bull-board failed/active/delayed job UI on Warlock's Fastify\n * server. Requires the optional packages `@bull-board/api` and\n * `@bull-board/fastify`; they are loaded only when this is called, and a\n * missing one throws {@link QueueDashboardDependencyError}.\n *\n * Call it before the HTTP server starts listening, and put it behind your\n * own authentication — the dashboard can retry and delete jobs.\n *\n * @example\n * import { getHttpServer } from \"@warlock.js/core\";\n * await queueDashboard(getHttpServer(), { basePath: \"/admin/queues\" });\n */\nexport async function queueDashboard(\n server: DashboardServer,\n options: QueueDashboardOptions = {},\n): Promise<void> {\n const basePath = options.basePath ?? \"/admin/queues\";\n const { createBullBoard, BullMQAdapter, FastifyAdapter } = await loadBullBoard();\n const queueNames = options.queues ?? [\n ...new Set([defaultQueueName(), ...registeredJobs().map((job) => queueOf(job))]),\n ];\n\n const serverAdapter = new FastifyAdapter();\n serverAdapter.setBasePath(basePath);\n\n createBullBoard({\n queues: queueNames.map((name) => new BullMQAdapter(getQueue(name))),\n serverAdapter,\n });\n\n const guardedPlugin = buildDashboardGuardPlugin(\n options.middleware ?? [],\n serverAdapter.registerPlugin(),\n );\n\n await server.register(guardedPlugin as never, { prefix: basePath });\n}\n\n/**\n * Load the optional bull-board packages. Exported for tests of the missing\n * dependency path; `importer` defaults to a real dynamic import.\n */\nexport async function loadBullBoard(\n importer: (specifier: string) => Promise<Record<string, unknown>> = (specifier) =>\n import(specifier),\n): Promise<BullBoardModules> {\n const api = await importOptional(importer, \"@bull-board/api\");\n const adapter = await importOptional(importer, \"@bull-board/api/bullMQAdapter\");\n const fastify = await importOptional(importer, \"@bull-board/fastify\");\n\n return {\n createBullBoard: api.createBullBoard as BullBoardModules[\"createBullBoard\"],\n BullMQAdapter: adapter.BullMQAdapter as BullBoardModules[\"BullMQAdapter\"],\n FastifyAdapter: fastify.FastifyAdapter as BullBoardModules[\"FastifyAdapter\"],\n };\n}\n\nasync function importOptional(\n importer: (specifier: string) => Promise<Record<string, unknown>>,\n specifier: string,\n): Promise<Record<string, unknown>> {\n try {\n return await importer(specifier);\n } catch (error) {\n if (isModuleNotFound(error)) {\n throw new QueueDashboardDependencyError(specifier.split(\"/\").slice(0, 2).join(\"/\"));\n }\n\n throw error;\n }\n}\n\nfunction isModuleNotFound(error: unknown): boolean {\n const code = (error as { code?: unknown } | undefined)?.code;\n\n return code === \"ERR_MODULE_NOT_FOUND\" || code === \"MODULE_NOT_FOUND\";\n}\n","/**\n * Thrown at boot when `queue.dashboard.enabled` is `true` in production with\n * no guard middleware. The dashboard can retry and delete jobs; mounting it\n * on the open internet without a guard is a production incident waiting to\n * happen, so this fails the boot instead of shipping the hole.\n */\nexport class QueueDashboardUnguardedError extends Error {\n public constructor() {\n super(\n \"queue.dashboard.enabled is true in production with no middleware. The dashboard can \" +\n \"retry and delete jobs, so it must be guarded before it is exposed.\\n\\n\" +\n \"Add a guard middleware:\\n\\n\" +\n ' import { middleware } from \"@warlock.js/core\";\\n' +\n ' import { authMiddleware } from \"@warlock.js/auth\";\\n\\n' +\n \" const queueConfig: QueueConfig = {\\n\" +\n \" // ...\\n\" +\n \" dashboard: {\\n\" +\n \" enabled: true,\\n\" +\n ' middleware: [authMiddleware(\"admin\")],\\n' +\n \" },\\n\" +\n \" };\\n\",\n );\n this.name = \"QueueDashboardUnguardedError\";\n }\n}\n","import { log } from \"@warlock.js/logger\";\nimport { type DashboardServer, queueDashboard } from \"./dashboard\";\nimport { QueueDashboardUnguardedError } from \"./queue-dashboard-unguarded.error\";\nimport type { QueueConfig } from \"./types\";\n\n/** Default path the dashboard mounts on when `dashboard.path` is not set. */\nexport const DEFAULT_DASHBOARD_PATH = \"/admin/queues\";\n\n/**\n * Mount the bull-board dashboard for `queue.dashboard` in `config`, applying\n * the safety rule: `enabled` in production with no middleware throws\n * {@link QueueDashboardUnguardedError} instead of booting exposed; outside\n * production with no middleware it logs one warning and mounts anyway.\n *\n * Called by `queueConnector()` at boot, once the HTTP server exists but\n * before it starts listening — see `queue-connector.ts`. Exported so it can\n * be unit-tested without going through the whole connector lifecycle.\n */\nexport async function mountQueueDashboard(\n server: DashboardServer | undefined,\n config: QueueConfig,\n): Promise<void> {\n const dashboard = config.dashboard;\n\n if (!dashboard?.enabled) {\n return;\n }\n\n const middlewareList = dashboard.middleware ?? [];\n\n if (middlewareList.length === 0) {\n if (process.env.NODE_ENV === \"production\") {\n throw new QueueDashboardUnguardedError();\n }\n\n log.warn(\n \"queue\",\n \"dashboard\",\n \"queue.dashboard.enabled is true with no middleware. The dashboard can retry and delete \" +\n \"jobs — add a guard middleware before this ships to production.\",\n );\n }\n\n if (!server) {\n log.warn(\n \"queue\",\n \"dashboard\",\n \"queue.dashboard.enabled is true but no HTTP server was found; the dashboard was not mounted.\",\n );\n\n return;\n }\n\n await queueDashboard(server, {\n basePath: dashboard.path ?? DEFAULT_DASHBOARD_PATH,\n middleware: middlewareList,\n });\n}\n","import type { DispatchOptions, DispatchedJob, JobSnapshot } from \"./types\";\n\n/**\n * A stand-in for the broker, used by test fakes.\n *\n * When one is installed, `job.dispatch()` hands the job to it instead of\n * adding it to BullMQ, and `job.find()` asks it instead of Redis. Nothing\n * connects to Redis while it is installed.\n *\n * `@warlock.js/core/tests` installs one as `queueFake` in the test\n * environment; this package exposes the seam so core needs no dependency on\n * it. The slot lives on `globalThis` so every copy of this package in a\n * process (a Vitest worker can load two) sees the same one.\n */\nexport type DispatchInterceptor = {\n dispatch(job: {\n name: string;\n queue: string;\n payload: unknown;\n options: DispatchOptions;\n }): DispatchedJob | Promise<DispatchedJob>;\n find?(job: {\n name: string;\n queue: string;\n id: string;\n }): JobSnapshot | undefined | Promise<JobSnapshot | undefined>;\n};\n\nconst INTERCEPTOR_KEY = Symbol.for(\"warlock.queue.dispatchInterceptor\");\n\ntype InterceptorHolder = { [INTERCEPTOR_KEY]?: DispatchInterceptor };\n\n/** Install an interceptor, or remove it with `undefined`. */\nexport function setDispatchInterceptor(interceptor: DispatchInterceptor | undefined): void {\n (globalThis as InterceptorHolder)[INTERCEPTOR_KEY] = interceptor;\n}\n\n/** The installed interceptor, if any. */\nexport function getDispatchInterceptor(): DispatchInterceptor | undefined {\n return (globalThis as InterceptorHolder)[INTERCEPTOR_KEY];\n}\n","import { InvalidDurationError } from \"./errors\";\nimport type { Duration } from \"./types\";\n\nconst UNIT_MILLISECONDS: Record<string, number> = {\n ms: 1,\n s: 1_000,\n m: 60_000,\n h: 3_600_000,\n d: 86_400_000,\n};\n\nconst DURATION_PATTERN = /^(\\d+(?:\\.\\d+)?)(ms|s|m|h|d)$/;\n\n/**\n * Convert a {@link Duration} to milliseconds. Numbers are already\n * milliseconds. Anything else is rejected loudly rather than guessed at.\n */\nexport function toMilliseconds(value: Duration | string): number {\n if (typeof value === \"number\") {\n if (!Number.isFinite(value) || value < 0) {\n throw new InvalidDurationError(value);\n }\n\n return Math.round(value);\n }\n\n const match = typeof value === \"string\" ? DURATION_PATTERN.exec(value.trim()) : null;\n\n if (!match) {\n throw new InvalidDurationError(value);\n }\n\n return Math.round(Number(match[1]) * UNIT_MILLISECONDS[match[2]!]!);\n}\n","import type { Job, JobsOptions } from \"bullmq\";\nimport { getQueueConfig } from \"./config\";\nimport { getDispatchInterceptor } from \"./dispatch-interceptor\";\nimport { toMilliseconds } from \"./duration\";\nimport { InvalidJobDefinitionError } from \"./errors\";\nimport { getQueueContext, unwrapPayload, wrapPayload } from \"./queue-context\";\nimport { queueOf, registerJob, type RegisteredJob } from \"./job-registry\";\nimport { getQueue } from \"./queue-manager\";\nimport type {\n DispatchOptions,\n JobBackoff,\n JobDefinition,\n JobOptions,\n JobSnapshot,\n JobState,\n QueueJob,\n} from \"./types\";\n\n/**\n * Define a background job.\n *\n * The definition is registered by name so any worker in the process can run\n * it; the returned object dispatches it with a typed payload.\n *\n * @example\n * export const sendInvoice = defineJob({\n * name: \"invoices.send\",\n * attempts: 5,\n * backoff: { type: \"exponential\", delay: 2000 },\n * async handle(payload: { invoiceId: string }, ctx) {\n * await ctx.progress(50);\n * },\n * });\n *\n * await sendInvoice.dispatch({ invoiceId: \"42\" }, { delay: \"10m\", priority: 1 });\n */\nexport function defineJob<TPayload, TResult = unknown>(\n definition: JobDefinition<TPayload, TResult>,\n): QueueJob<TPayload, TResult> {\n assertValidDefinition(definition);\n registerJob(definition as RegisteredJob);\n\n return {\n name: definition.name,\n get queue() {\n return queueOf(definition);\n },\n async dispatch(payload, options = {}) {\n const queueName = queueOf(definition);\n const interceptor = getDispatchInterceptor();\n\n // A test fake stands in for the broker: record, never touch Redis.\n if (interceptor) {\n return interceptor.dispatch({ name: definition.name, queue: queueName, payload, options });\n }\n\n // Capture synchronously, in the caller's async context.\n const captured = definition.context === false ? undefined : getQueueContext()?.capture();\n const job = await getQueue(queueName).add(\n definition.name,\n wrapPayload(payload, captured),\n toBullJobOptions(definition, options),\n );\n\n return { id: String(job.id), name: definition.name, queue: queueName };\n },\n async find(id) {\n const interceptor = getDispatchInterceptor();\n\n if (interceptor) {\n const found = await interceptor.find?.({\n name: definition.name,\n queue: queueOf(definition),\n id,\n });\n\n return found as JobSnapshot<TPayload, TResult> | undefined;\n }\n\n const queue = getQueue(queueOf(definition));\n const initialJob = await queue.getJob(id);\n\n if (!initialJob || initialJob.name !== definition.name) {\n return undefined;\n }\n\n // Read the state first, then (re)fetch the job. BullMQ writes a job's\n // result/attemptsMade/finishedOn fields *before* it becomes visible\n // under a new state, so re-reading the job after the state is known\n // guarantees those fields are consistent with the reported state\n // (rather than reflecting a moment before the job finished).\n const state = (await initialJob.getState()) as JobState;\n const job = (await queue.getJob(id)) ?? initialJob;\n\n return toSnapshot<TPayload, TResult>(job, state);\n },\n };\n}\n\nfunction assertValidDefinition(definition: JobDefinition<unknown, unknown>): void {\n if (typeof definition.name !== \"string\" || definition.name.trim() === \"\") {\n throw new InvalidJobDefinitionError(\"defineJob() requires a non-empty `name`.\");\n }\n\n if (typeof definition.handle !== \"function\") {\n throw new InvalidJobDefinitionError(\n `defineJob(\"${definition.name}\") requires a \\`handle(payload, ctx)\\` function.`,\n );\n }\n\n if (\n definition.attempts !== undefined &&\n !(Number.isInteger(definition.attempts) && definition.attempts >= 1)\n ) {\n throw new InvalidJobDefinitionError(\n `defineJob(\"${definition.name}\"): \\`attempts\\` must be an integer >= 1, got ${definition.attempts}.`,\n );\n }\n}\n\n/**\n * Merge app defaults < job definition < dispatch options into BullMQ's shape.\n */\nfunction toBullJobOptions(definition: JobOptions, options: DispatchOptions): JobsOptions {\n const defaults = getQueueConfig().defaultJobOptions ?? {};\n const attempts = options.attempts ?? definition.attempts ?? defaults.attempts;\n const backoff = options.backoff ?? definition.backoff ?? defaults.backoff;\n const removeOnComplete = definition.removeOnComplete ?? defaults.removeOnComplete;\n const removeOnFail = definition.removeOnFail ?? defaults.removeOnFail;\n\n const bullOptions: JobsOptions = {};\n\n if (attempts !== undefined) bullOptions.attempts = attempts;\n if (backoff !== undefined) bullOptions.backoff = toBullBackoff(backoff);\n if (removeOnComplete !== undefined) bullOptions.removeOnComplete = removeOnComplete;\n if (removeOnFail !== undefined) bullOptions.removeOnFail = removeOnFail;\n if (options.delay !== undefined) bullOptions.delay = toMilliseconds(options.delay);\n if (options.priority !== undefined) bullOptions.priority = options.priority;\n if (options.jobId !== undefined) bullOptions.jobId = options.jobId;\n\n return bullOptions;\n}\n\nfunction toBullBackoff(backoff: JobBackoff): JobsOptions[\"backoff\"] {\n return typeof backoff === \"number\" ? { type: \"fixed\", delay: backoff } : backoff;\n}\n\n/**\n * A plain view of a BullMQ job.\n *\n * @param job The job to read fields from.\n * @param state The job's state; pass a state read *before* `job` was\n * fetched (or re-fetched) so the returned snapshot's fields are consistent\n * with it. If omitted, the state is read from `job` directly.\n */\nexport async function toSnapshot<TPayload, TResult>(\n job: Job,\n state?: JobState,\n): Promise<JobSnapshot<TPayload, TResult>> {\n const resolvedState = state ?? ((await job.getState()) as JobState);\n\n return {\n id: String(job.id),\n name: job.name,\n queue: job.queueName,\n state: resolvedState,\n payload: unwrapPayload(job.data).payload as TPayload,\n progress: job.progress as JobSnapshot[\"progress\"],\n attemptsMade: job.attemptsMade,\n result: job.returnvalue as TResult | undefined,\n failedReason: job.failedReason || undefined,\n createdAt: new Date(job.timestamp),\n finishedAt: job.finishedOn ? new Date(job.finishedOn) : undefined,\n };\n}\n","import { unwrapPayload } from \"./queue-context\";\nimport type { Job } from \"bullmq\";\nimport { defaultQueueName } from \"./config\";\nimport { FailedJobNotFoundError } from \"./errors\";\nimport { getQueue } from \"./queue-manager\";\nimport type { FailedJob } from \"./types\";\n\nexport type FailedJobsOptions = {\n /** Queue to read. Default: the default queue. */\n queue?: string;\n /** First index (newest first). Default `0`. */\n start?: number;\n /** Last index, inclusive. Default `99`. */\n end?: number;\n};\n\n/**\n * List failed jobs, newest first — jobs that used up every attempt, or\n * failed unrecoverably. Each entry can be retried.\n */\nexport async function failedJobs(options: FailedJobsOptions = {}): Promise<FailedJob[]> {\n const queueName = options.queue ?? defaultQueueName();\n const jobs = await getQueue(queueName).getFailed(options.start ?? 0, options.end ?? 99);\n\n return jobs.map((job) => toFailedJob(job));\n}\n\n/**\n * Retry one failed job by id: it goes back to waiting with its attempts reset.\n * Throws {@link FailedJobNotFoundError} when no FAILED job has that id.\n */\nexport async function retryFailedJob(id: string, options: { queue?: string } = {}): Promise<void> {\n const queueName = options.queue ?? defaultQueueName();\n const job = await getQueue(queueName).getJob(id);\n\n if (!job || !(await job.isFailed())) {\n throw new FailedJobNotFoundError(id, queueName);\n }\n\n await job.retry(\"failed\");\n}\n\nfunction toFailedJob(job: Job): FailedJob {\n return {\n id: String(job.id),\n name: job.name,\n queue: job.queueName,\n payload: unwrapPayload(job.data).payload,\n attemptsMade: job.attemptsMade,\n failedReason: job.failedReason,\n stacktrace: job.stacktrace ?? [],\n failedAt: job.finishedOn ? new Date(job.finishedOn) : undefined,\n retry: () => job.retry(\"failed\"),\n };\n}\n","/**\n * The queue's connector for `warlock.config.ts > connectors`.\n *\n * Deliberately a plain object with TYPE-ONLY imports from core: the config\n * file that constructs it must not drag core's runtime graph (or BullMQ) in\n * at config-load time. Core is imported lazily inside `start()`, where the\n * app has already loaded it.\n */\nimport type { Connector, ConnectorLifecyclePhase } from \"@warlock.js/core\";\nimport { log } from \"@warlock.js/logger\";\nimport { resetQueueConfig, setQueueConfig } from \"./config\";\nimport { mountQueueDashboard } from \"./dashboard-boot\";\nimport { closeQueue, startWorkers } from \"./queue-manager\";\nimport type { QueueConfig } from \"./types\";\n\n/**\n * Boots after every built-in connector (`ConnectorPriority.AI` is `10`), so\n * the logger is up and anything a job handler needs is already connected;\n * shuts down before them for the same reason.\n */\nexport const QUEUE_CONNECTOR_PRIORITY = 11;\n\nconst WATCHED_FILES = [\"src/config/queue.ts\"];\n\nexport type QueueConnectorOptions = {\n /**\n * Supply the configuration directly instead of reading the `queue` config\n * key (`src/config/queue.ts`).\n */\n config?: QueueConfig;\n};\n\n/**\n * Construct the queue connector.\n *\n * Runs in the `late` lifecycle phase — after app code is imported — so every\n * `defineJob` in the app has registered before workers start. At start it\n * reads the `queue` config and starts in-process workers unless\n * `workers.enabled` is `false`; at shutdown it closes workers (waiting for\n * active jobs, bounded by `workers.shutdownTimeout`) and then the queues.\n *\n * @example\n * // warlock.config.ts\n * import { queueConnector } from \"@warlock.js/queue\";\n *\n * export default defineConfig({ connectors: [queueConnector()] });\n */\nexport function queueConnector(options: QueueConnectorOptions = {}): Connector {\n let active = false;\n\n const connector: Connector = {\n name: \"queue\",\n priority: QUEUE_CONNECTOR_PRIORITY,\n // Core's `ConnectorLifecyclePhase.Late`; the value is spelled out so this\n // module stays free of a runtime import of core.\n lifecyclePhase: \"late\" as ConnectorLifecyclePhase,\n isActive: () => active,\n /**\n * Mounts the dashboard, when configured, here rather than in `start()`:\n * `boot()` runs for every late-phase connector, in priority order, before\n * any of them `start()`s — so by the time this runs, the HTTP connector\n * (priority 5, before queue's 11) has already built its Fastify instance\n * and registered its own plugins, but has not yet called `listen()`.\n * Fastify refuses new plugin registrations after `listen()`, so this is\n * the only point in the boot sequence where mounting is possible.\n */\n async boot() {\n const queueConfig = options.config ?? (await readQueueConfig());\n\n if (!queueConfig?.dashboard?.enabled) {\n return;\n }\n\n // The dashboard resolves queues through the active config, so it has to\n // be registered here rather than only in `start()`, which runs after\n // every late connector has booted. `start()` sets it again; the setter\n // is idempotent for the same object.\n setQueueConfig(queueConfig);\n\n const { getHttpServer } = await import(\"@warlock.js/core\");\n\n await mountQueueDashboard(getHttpServer(), queueConfig);\n },\n async start() {\n const queueConfig = options.config ?? (await readQueueConfig());\n\n if (!queueConfig) {\n log.warn(\n \"queue\",\n \"configured\",\n \"queueConnector() is registered but no `queue` config was found (src/config/queue.ts); queue not started\",\n );\n return;\n }\n\n setQueueConfig(queueConfig);\n const started = await startWorkers();\n active = true;\n\n log.info(\n \"queue\",\n \"configured\",\n started.length > 0\n ? `Queue workers running for: ${started.join(\", \")}`\n : \"Queue configured (no in-process workers)\",\n );\n },\n async restart() {\n await connector.shutdown();\n await connector.start();\n },\n async shutdown() {\n if (!active) {\n return;\n }\n\n await closeQueue();\n resetQueueConfig();\n active = false;\n },\n shouldRestart(changedFiles) {\n return changedFiles.some((file) => {\n const normalized = file.replace(/\\\\/g, \"/\");\n\n return WATCHED_FILES.some(\n (watched) => normalized === watched || normalized.endsWith(`/${watched}`),\n );\n });\n },\n };\n\n return connector;\n}\n\nasync function readQueueConfig(): Promise<QueueConfig | undefined> {\n const { config } = await import(\"@warlock.js/core\");\n\n return config.get<QueueConfig | undefined>(\"queue\");\n}\n"],"mappings":";;;;;;;;;AAIA,IAAa,0BAAb,cAA6C,MAAM;CACjD,AAAO,cAAc;EACnB,MACE,0LAGF;EACA,KAAK,OAAO;CACd;AACF;;;;AAKA,IAAa,uBAAb,cAA0C,MAAM;CAC9C,AAAO,YAAY,OAAgB;EACjC,MACE,oBAAoB,KAAK,UAAU,KAAK,EAAE,qGAE5C;EACA,KAAK,OAAO;CACd;AACF;;;;AAKA,IAAa,4BAAb,cAA+C,MAAM;CACnD,AAAO,YAAY,SAAiB;EAClC,MAAM,OAAO;EACb,KAAK,OAAO;CACd;AACF;;;;AAKA,IAAa,yBAAb,cAA4C,MAAM;CAChD,AAAO,YAAY,IAAY,OAAe;EAC5C,MAAM,0BAA0B,GAAG,cAAc,MAAM,GAAG;EAC1D,KAAK,OAAO;CACd;AACF;;;;;AAMA,IAAa,gCAAb,cAAmD,MAAM;CACvD,AAAO,YAAY,SAAiB;EAClC,MACE,mDAAmD,QAAQ;;;CAG7D;EACA,KAAK,OAAO;CACd;AACF;;;;;AAMA,IAAa,wBAAb,cAA2C,MAAM;CAC/C,AAAO,YAAY,SAAiB,SAA+B;EACjE,MAAM,SAAS,OAAO;EACtB,KAAK,OAAO;CACd;AACF;;;;ACrEA,IAAI;;;;;;AAOJ,SAAgB,eAAe,QAA2B;CACxD,eAAe;AACjB;;;;;AAMA,SAAgB,iBAA8B;CAC5C,IAAI,CAAC,cACH,MAAM,IAAI,wBAAwB;CAGpC,OAAO;AACT;;AAGA,SAAgB,mBAAyB;CACvC,eAAe;AACjB;;AAGA,SAAgB,mBAA2B;CACzC,OAAO,cAAc,gBAAgB;AACvC;;;;;;;;;;;;;;;;;;;;;;;ACZA,eAAsB,uBACpB,gBACA,gBACA,cACkB;CAClB,IAAI,eAAe,WAAW,GAC5B,OAAO;CAGT,MAAM,EAAE,SAAS,aAAa,MAAM,OAAO;CAE3C,MAAM,UAAU,IAAI,QAAQ;CAC5B,MAAM,WAAW,IAAI,SAAS;CAE9B,SAAS,YAAY,YAAY;CACjC,QAAQ,WAAW;CACnB,SAAS,UAAU;CACnB,QAAQ,WAAW,cAAc;CAEjC,KAAK,MAAM,sBAAsB,gBAG/B,IAAI,MAFiB,mBAAmB;EAAE;EAAS;CAAS,CAAC,GAG3D,OAAO;CAIX,OAAO;AACT;;;;;;;;;;;;;;;;AClCA,SAAgB,0BACd,gBACA,iBACuB;CACvB,OAAO,SAAS,qBAAqB,UAA2B,UAAU,MAAM;EAC9E,IAAI,eAAe,SAAS,GAC1B,SAAS,QAAQ,aAAa,OAAO,SAAS,UAAU;GAGtD,IAAI,MAFkB,uBAAuB,gBAAgB,SAAS,KAAK,GAUzE,OAAO;EAEX,CAAC;EAGH,SAAS,SAAS,eAAwB;EAC1C,KAAK;CACP;AACF;;;;ACjCA,MAAM,uBAAO,IAAI,IAA2B;AAC5C,MAAM,4BAAY,IAAI,IAAsB;;;;;;;;;;AAW5C,SAAgB,YAAY,KAA0B;CACpD,KAAK,IAAI,IAAI,MAAM,GAAG;CAEtB,KAAK,MAAM,YAAY,WACrB,SAAS,GAAG;AAEhB;;AAGA,SAAgB,kBAAkB,MAAyC;CACzE,OAAO,KAAK,IAAI,IAAI;AACtB;;AAGA,SAAgB,iBAAkC;CAChD,OAAO,CAAC,GAAG,KAAK,OAAO,CAAC;AAC1B;;AAGA,SAAgB,QAAQ,KAA2C;CACjE,OAAO,IAAI,SAAS,iBAAiB;AACvC;;AAGA,SAAgB,gBAAgB,UAAwC;CACtE,UAAU,IAAI,QAAQ;CAEtB,aAAa,UAAU,OAAO,QAAQ;AACxC;;;;;;;;;;;;;;;AC1BA,SAAgB,mBAAsB,SAA2C;CAC/E,OAAO;AACT;AAEA,IAAI;;AAGJ,SAAgB,gBAAgB,SAAkC;CAChE,gBAAgB;AAClB;;AAGA,SAAgB,kBAAiD;CAC/D,OAAO;AACT;;AAGA,SAAgB,oBAA0B;CACxC,gBAAgB;AAClB;;AASA,SAAgB,YAAY,SAAkB,SAA2B;CACvE,IAAI,YAAY,QAAW,OAAO;CAElC,OAAO;EAAE,WAAW;GAAE,GAAG;GAAG;EAAQ;EAAG;CAAQ;AACjD;;AAGA,SAAgB,cAAc,MAAuD;CACnF,MAAM,WAAW;CAEjB,IACE,aAAa,QACb,OAAO,aAAa,YACpB,OAAO,SAAS,cAAc,YAC9B,SAAS,cAAc,QACvB,SAAS,UAAU,MAAM,KACzB,aAAa,UAEb,OAAO;EAAE,SAAS,SAAS;EAAS,SAAS,SAAS,UAAU;CAAQ;CAG1E,OAAO;EAAE,SAAS;EAAM,SAAS;CAAU;AAC7C;;;;;;;;;;;;AC1DA,eAAsB,WAAW,KAA4B;CAC3D,MAAM,aAAa,kBAAkB,IAAI,IAAI;CAE7C,IAAI,CAAC,YACH,MAAM,IAAIA,0BACR,iBAAiB,IAAI,KAAK,6GAE5B;CAGF,MAAM,EAAE,SAAS,SAAS,aAAa,cAAc,IAAI,IAAI;CAE7D,MAAM,UAAsB;EAC1B,IAAI,OAAO,IAAI,EAAE;EACjB,MAAM,IAAI;EACV,OAAO,IAAI;EACX,SAAS,IAAI,eAAe;EAC5B,aAAa,IAAI,KAAK,YAAY;EAClC,WAAW,UAAU,IAAI,eAAe,KAAK;EAC7C,KAAK,OAAO,SAAS;GACnB,MAAM,IAAI,IAAI,IAAI;EACpB;EACA,SAAS;CACX;CAEA,MAAM,MAAM,YAAY,WAAW,OAAO,SAAS,OAAO;CAC1D,MAAM,eAAe,gBAAgB;CAErC,IAAI;EACF,OAAO,aAAa,UAAa,eAC7B,MAAM,aAAa,QAAQ,UAAU,GAAG,IACxC,MAAM,IAAI;CAChB,SAAS,OAAO;EACd,IAAI,iBAAiB,uBACnB,MAAM,IAAIA,0BAAmB,MAAM,OAAO;EAG5C,MAAM;CACR;AACF;;;;AC/CA,MAAM,2BAA2B;AAEjC,MAAM,yBAAS,IAAI,IAAmB;AACtC,MAAM,0BAAU,IAAI,IAAoB;AACxC,IAAI;;;;;AAMJ,SAAgB,SAAS,MAAqB;CAC5C,IAAI,QAAQ,OAAO,IAAI,IAAI;CAE3B,IAAI,CAAC,OAAO;EACV,MAAM,SAAS,eAAe;EAE9B,QAAQ,IAAIC,aAAM,MAAM;GACtB,YAAY,OAAO;GACnB,QAAQ,OAAO,UAAU;EAC3B,CAAC;EAED,MAAM,GAAG,UAAU,UAAU;GAC3B,uBAAI,MAAM,SAAS,cAAc,KAAK;EACxC,CAAC;EAED,OAAO,IAAI,MAAM,KAAK;CACxB;CAEA,OAAO;AACT;;;;;;;;;;;;;AAcA,eAAsB,eAAkC;CACtD,MAAM,EAAE,gBAAgB,MAAM,OAAO;CAErC,IAAI,CAAC,YAAY,QAAQ,QAAQ,GAAG;EAClC,uBAAI,KACF,SACA,WACA,qCAAqC,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC,KAAK,GAAG,EAAE,EACxE;EACA,OAAO,CAAC;CACV;CAIA,IAFe,eAEN,CAAC,CAAC,SAAS,YAAY,OAC9B,OAAO,CAAC;CAGV,KAAK,MAAM,OAAO,eAAe,GAC/B,aAAa,QAAQ,GAAG,CAAC;CAG3B,kBAAkB,iBAAiB,QAAQ;EACzC,aAAa,QAAQ,GAAG,CAAC;CAC3B,CAAC;CAED,MAAM,QAAQ,IAAI,CAAC,GAAG,QAAQ,OAAO,CAAC,CAAC,CAAC,KAAK,WAAW,OAAO,eAAe,CAAC,CAAC;CAEhF,OAAO,CAAC,GAAG,QAAQ,KAAK,CAAC;AAC3B;;AAGA,SAAgB,iBAA2B;CACzC,OAAO,CAAC,GAAG,QAAQ,KAAK,CAAC;AAC3B;AAEA,SAAS,aAAa,WAAyB;CAC7C,IAAI,QAAQ,IAAI,SAAS,GACvB;CAGF,MAAM,SAAS,eAAe;CAE9B,MAAM,SAAS,IAAIC,cAAO,WAAW,YAAY;EAC/C,YAAY,OAAO;EACnB,QAAQ,OAAO,UAAU;EACzB,aAAa,OAAO,SAAS,eAAe;CAC9C,CAAC;CAED,OAAO,GAAG,UAAU,UAAU;EAC5B,uBAAI,MAAM,SAAS,UAAU,KAAK;CACpC,CAAC;CAED,OAAO,GAAG,WAAW,KAAK,UAAU;EAClC,uBAAI,MACF,SACA,cACA,GAAG,KAAK,QAAQ,UAAU,IAAI,KAAK,MAAM,IAAI,KAAK,MAAM,SAC1D;CACF,CAAC;CAED,QAAQ,IAAI,WAAW,MAAM;AAC/B;;;;;;AAeA,eAAsB,WAAW,UAA6B,CAAC,GAAkB;CAC/E,gBAAgB;CAChB,gBAAgB;CAEhB,MAAM,UAAU,QAAQ,WAAW,0BAA0B;CAC7D,MAAM,iBAAiB,CAAC,GAAG,QAAQ,OAAO,CAAC;CAC3C,QAAQ,MAAM;CAEd,MAAM,QAAQ,IAAI,eAAe,KAAK,WAAW,YAAY,QAAQ,OAAO,CAAC,CAAC;CAE9E,MAAM,gBAAgB,CAAC,GAAG,OAAO,OAAO,CAAC;CACzC,OAAO,MAAM;CAEb,MAAM,QAAQ,IAAI,cAAc,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AAC/D;AAEA,SAAS,4BAAoC;CAC3C,IAAI;EACF,OAAO,eAAe,CAAC,CAAC,SAAS,mBAAmB;CACtD,QAAQ;EACN,OAAO;CACT;AACF;AAEA,eAAe,YAAY,QAAgB,SAAgC;CACzE,IAAI;CAEJ,MAAM,WAAW,IAAI,SAAoB,YAAY;EACnD,QAAQ,iBAAiB,QAAQ,SAAS,GAAG,OAAO;CACtD,CAAC;CAKD,MAAM,UAAU,OAAO,MAAM,CAAC,CAAC,WAAW,QAAiB;CAC3D,QAAQ,YAAY,MAAS;CAE7B,MAAM,UAAU,MAAM,QAAQ,KAAK,CAAC,SAAS,QAAQ,CAAC;CAEtD,aAAa,KAAK;CAElB,IAAI,YAAY,WAAW;EACzB,uBAAI,KACF,SACA,YACA,eAAe,OAAO,KAAK,gCAAgC,QAAQ,mEAErE;EAEA,MAAM,OAAO,WAAW;CAC1B;AACF;;;;;;;;;;;;;;;;;AC/HA,eAAsB,eACpB,QACA,UAAiC,CAAC,GACnB;CACf,MAAM,WAAW,QAAQ,YAAY;CACrC,MAAM,EAAE,iBAAiB,eAAe,mBAAmB,MAAM,cAAc;CAC/E,MAAM,aAAa,QAAQ,UAAU,CACnC,mBAAG,IAAI,IAAI,CAAC,iBAAiB,GAAG,GAAG,eAAe,CAAC,CAAC,KAAK,QAAQ,QAAQ,GAAG,CAAC,CAAC,CAAC,CACjF;CAEA,MAAM,gBAAgB,IAAI,eAAe;CACzC,cAAc,YAAY,QAAQ;CAElC,gBAAgB;EACd,QAAQ,WAAW,KAAK,SAAS,IAAI,cAAc,SAAS,IAAI,CAAC,CAAC;EAClE;CACF,CAAC;CAED,MAAM,gBAAgB,0BACpB,QAAQ,cAAc,CAAC,GACvB,cAAc,eAAe,CAC/B;CAEA,MAAM,OAAO,SAAS,eAAwB,EAAE,QAAQ,SAAS,CAAC;AACpE;;;;;AAMA,eAAsB,cACpB,YAAqE,cACnE,OAAO,YACkB;CAC3B,MAAM,MAAM,MAAM,eAAe,UAAU,iBAAiB;CAC5D,MAAM,UAAU,MAAM,eAAe,UAAU,+BAA+B;CAC9E,MAAM,UAAU,MAAM,eAAe,UAAU,qBAAqB;CAEpE,OAAO;EACL,iBAAiB,IAAI;EACrB,eAAe,QAAQ;EACvB,gBAAgB,QAAQ;CAC1B;AACF;AAEA,eAAe,eACb,UACA,WACkC;CAClC,IAAI;EACF,OAAO,MAAM,SAAS,SAAS;CACjC,SAAS,OAAO;EACd,IAAI,iBAAiB,KAAK,GACxB,MAAM,IAAI,8BAA8B,UAAU,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC;EAGpF,MAAM;CACR;AACF;AAEA,SAAS,iBAAiB,OAAyB;CACjD,MAAM,OAAQ,OAA0C;CAExD,OAAO,SAAS,0BAA0B,SAAS;AACrD;;;;;;;;;;AC5GA,IAAa,+BAAb,cAAkD,MAAM;CACtD,AAAO,cAAc;EACnB,MACE,6bAYF;EACA,KAAK,OAAO;CACd;AACF;;;;;AClBA,MAAa,yBAAyB;;;;;;;;;;;AAYtC,eAAsB,oBACpB,QACA,QACe;CACf,MAAM,YAAY,OAAO;CAEzB,IAAI,CAAC,WAAW,SACd;CAGF,MAAM,iBAAiB,UAAU,cAAc,CAAC;CAEhD,IAAI,eAAe,WAAW,GAAG;EAC/B,IAAI,QAAQ,IAAI,aAAa,cAC3B,MAAM,IAAI,6BAA6B;EAGzC,uBAAI,KACF,SACA,aACA,uJAEF;CACF;CAEA,IAAI,CAAC,QAAQ;EACX,uBAAI,KACF,SACA,aACA,8FACF;EAEA;CACF;CAEA,MAAM,eAAe,QAAQ;EAC3B,UAAU,UAAU;EACpB,YAAY;CACd,CAAC;AACH;;;;AC7BA,MAAM,kBAAkB,OAAO,IAAI,mCAAmC;;AAKtE,SAAgB,uBAAuB,aAAoD;CACzF,AAAC,WAAiC,mBAAmB;AACvD;;AAGA,SAAgB,yBAA0D;CACxE,OAAQ,WAAiC;AAC3C;;;;ACrCA,MAAM,oBAA4C;CAChD,IAAI;CACJ,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;AACL;AAEA,MAAM,mBAAmB;;;;;AAMzB,SAAgB,eAAe,OAAkC;CAC/D,IAAI,OAAO,UAAU,UAAU;EAC7B,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,GACrC,MAAM,IAAI,qBAAqB,KAAK;EAGtC,OAAO,KAAK,MAAM,KAAK;CACzB;CAEA,MAAM,QAAQ,OAAO,UAAU,WAAW,iBAAiB,KAAK,MAAM,KAAK,CAAC,IAAI;CAEhF,IAAI,CAAC,OACH,MAAM,IAAI,qBAAqB,KAAK;CAGtC,OAAO,KAAK,MAAM,OAAO,MAAM,EAAE,IAAI,kBAAkB,MAAM,GAAK;AACpE;;;;;;;;;;;;;;;;;;;;;;ACGA,SAAgB,UACd,YAC6B;CAC7B,sBAAsB,UAAU;CAChC,YAAY,UAA2B;CAEvC,OAAO;EACL,MAAM,WAAW;EACjB,IAAI,QAAQ;GACV,OAAO,QAAQ,UAAU;EAC3B;EACA,MAAM,SAAS,SAAS,UAAU,CAAC,GAAG;GACpC,MAAM,YAAY,QAAQ,UAAU;GACpC,MAAM,cAAc,uBAAuB;GAG3C,IAAI,aACF,OAAO,YAAY,SAAS;IAAE,MAAM,WAAW;IAAM,OAAO;IAAW;IAAS;GAAQ,CAAC;GAI3F,MAAM,WAAW,WAAW,YAAY,QAAQ,SAAY,gBAAgB,CAAC,EAAE,QAAQ;GACvF,MAAM,MAAM,MAAM,SAAS,SAAS,CAAC,CAAC,IACpC,WAAW,MACX,YAAY,SAAS,QAAQ,GAC7B,iBAAiB,YAAY,OAAO,CACtC;GAEA,OAAO;IAAE,IAAI,OAAO,IAAI,EAAE;IAAG,MAAM,WAAW;IAAM,OAAO;GAAU;EACvE;EACA,MAAM,KAAK,IAAI;GACb,MAAM,cAAc,uBAAuB;GAE3C,IAAI,aAOF,OAAO,MANa,YAAY,OAAO;IACrC,MAAM,WAAW;IACjB,OAAO,QAAQ,UAAU;IACzB;GACF,CAAC;GAKH,MAAM,QAAQ,SAAS,QAAQ,UAAU,CAAC;GAC1C,MAAM,aAAa,MAAM,MAAM,OAAO,EAAE;GAExC,IAAI,CAAC,cAAc,WAAW,SAAS,WAAW,MAChD;GAQF,MAAM,QAAS,MAAM,WAAW,SAAS;GAGzC,OAAO,WAFM,MAAM,MAAM,OAAO,EAAE,KAAM,YAEE,KAAK;EACjD;CACF;AACF;AAEA,SAAS,sBAAsB,YAAmD;CAChF,IAAI,OAAO,WAAW,SAAS,YAAY,WAAW,KAAK,KAAK,MAAM,IACpE,MAAM,IAAI,0BAA0B,0CAA0C;CAGhF,IAAI,OAAO,WAAW,WAAW,YAC/B,MAAM,IAAI,0BACR,cAAc,WAAW,KAAK,iDAChC;CAGF,IACE,WAAW,aAAa,UACxB,EAAE,OAAO,UAAU,WAAW,QAAQ,KAAK,WAAW,YAAY,IAElE,MAAM,IAAI,0BACR,cAAc,WAAW,KAAK,gDAAgD,WAAW,SAAS,EACpG;AAEJ;;;;AAKA,SAAS,iBAAiB,YAAwB,SAAuC;CACvF,MAAM,WAAW,eAAe,CAAC,CAAC,qBAAqB,CAAC;CACxD,MAAM,WAAW,QAAQ,YAAY,WAAW,YAAY,SAAS;CACrE,MAAM,UAAU,QAAQ,WAAW,WAAW,WAAW,SAAS;CAClE,MAAM,mBAAmB,WAAW,oBAAoB,SAAS;CACjE,MAAM,eAAe,WAAW,gBAAgB,SAAS;CAEzD,MAAM,cAA2B,CAAC;CAElC,IAAI,aAAa,QAAW,YAAY,WAAW;CACnD,IAAI,YAAY,QAAW,YAAY,UAAU,cAAc,OAAO;CACtE,IAAI,qBAAqB,QAAW,YAAY,mBAAmB;CACnE,IAAI,iBAAiB,QAAW,YAAY,eAAe;CAC3D,IAAI,QAAQ,UAAU,QAAW,YAAY,QAAQ,eAAe,QAAQ,KAAK;CACjF,IAAI,QAAQ,aAAa,QAAW,YAAY,WAAW,QAAQ;CACnE,IAAI,QAAQ,UAAU,QAAW,YAAY,QAAQ,QAAQ;CAE7D,OAAO;AACT;AAEA,SAAS,cAAc,SAA6C;CAClE,OAAO,OAAO,YAAY,WAAW;EAAE,MAAM;EAAS,OAAO;CAAQ,IAAI;AAC3E;;;;;;;;;AAUA,eAAsB,WACpB,KACA,OACyC;CACzC,MAAM,gBAAgB,SAAW,MAAM,IAAI,SAAS;CAEpD,OAAO;EACL,IAAI,OAAO,IAAI,EAAE;EACjB,MAAM,IAAI;EACV,OAAO,IAAI;EACX,OAAO;EACP,SAAS,cAAc,IAAI,IAAI,CAAC,CAAC;EACjC,UAAU,IAAI;EACd,cAAc,IAAI;EAClB,QAAQ,IAAI;EACZ,cAAc,IAAI,gBAAgB;EAClC,WAAW,IAAI,KAAK,IAAI,SAAS;EACjC,YAAY,IAAI,aAAa,IAAI,KAAK,IAAI,UAAU,IAAI;CAC1D;AACF;;;;;;;;AC1JA,eAAsB,WAAW,UAA6B,CAAC,GAAyB;CACtF,MAAM,YAAY,QAAQ,SAAS,iBAAiB;CAGpD,QAAO,MAFY,SAAS,SAAS,CAAC,CAAC,UAAU,QAAQ,SAAS,GAAG,QAAQ,OAAO,EAAE,EAE3E,CAAC,KAAK,QAAQ,YAAY,GAAG,CAAC;AAC3C;;;;;AAMA,eAAsB,eAAe,IAAY,UAA8B,CAAC,GAAkB;CAChG,MAAM,YAAY,QAAQ,SAAS,iBAAiB;CACpD,MAAM,MAAM,MAAM,SAAS,SAAS,CAAC,CAAC,OAAO,EAAE;CAE/C,IAAI,CAAC,OAAO,CAAE,MAAM,IAAI,SAAS,GAC/B,MAAM,IAAI,uBAAuB,IAAI,SAAS;CAGhD,MAAM,IAAI,MAAM,QAAQ;AAC1B;AAEA,SAAS,YAAY,KAAqB;CACxC,OAAO;EACL,IAAI,OAAO,IAAI,EAAE;EACjB,MAAM,IAAI;EACV,OAAO,IAAI;EACX,SAAS,cAAc,IAAI,IAAI,CAAC,CAAC;EACjC,cAAc,IAAI;EAClB,cAAc,IAAI;EAClB,YAAY,IAAI,cAAc,CAAC;EAC/B,UAAU,IAAI,aAAa,IAAI,KAAK,IAAI,UAAU,IAAI;EACtD,aAAa,IAAI,MAAM,QAAQ;CACjC;AACF;;;;;;;;;AClCA,MAAa,2BAA2B;AAExC,MAAM,gBAAgB,CAAC,qBAAqB;;;;;;;;;;;;;;;;AAyB5C,SAAgB,eAAe,UAAiC,CAAC,GAAc;CAC7E,IAAI,SAAS;CAEb,MAAM,YAAuB;EAC3B,MAAM;EACN;EAGA,gBAAgB;EAChB,gBAAgB;;;;;;;;;;EAUhB,MAAM,OAAO;GACX,MAAM,cAAc,QAAQ,UAAW,MAAM,gBAAgB;GAE7D,IAAI,CAAC,aAAa,WAAW,SAC3B;GAOF,eAAe,WAAW;GAE1B,MAAM,EAAE,kBAAkB,MAAM,OAAO;GAEvC,MAAM,oBAAoB,cAAc,GAAG,WAAW;EACxD;EACA,MAAM,QAAQ;GACZ,MAAM,cAAc,QAAQ,UAAW,MAAM,gBAAgB;GAE7D,IAAI,CAAC,aAAa;IAChB,uBAAI,KACF,SACA,cACA,yGACF;IACA;GACF;GAEA,eAAe,WAAW;GAC1B,MAAM,UAAU,MAAM,aAAa;GACnC,SAAS;GAET,uBAAI,KACF,SACA,cACA,QAAQ,SAAS,IACb,8BAA8B,QAAQ,KAAK,IAAI,MAC/C,0CACN;EACF;EACA,MAAM,UAAU;GACd,MAAM,UAAU,SAAS;GACzB,MAAM,UAAU,MAAM;EACxB;EACA,MAAM,WAAW;GACf,IAAI,CAAC,QACH;GAGF,MAAM,WAAW;GACjB,iBAAiB;GACjB,SAAS;EACX;EACA,cAAc,cAAc;GAC1B,OAAO,aAAa,MAAM,SAAS;IACjC,MAAM,aAAa,KAAK,QAAQ,OAAO,GAAG;IAE1C,OAAO,cAAc,MAClB,YAAY,eAAe,WAAW,WAAW,SAAS,IAAI,SAAS,CAC1E;GACF,CAAC;EACH;CACF;CAEA,OAAO;AACT;AAEA,eAAe,kBAAoD;CACjE,MAAM,EAAE,WAAW,MAAM,OAAO;CAEhC,OAAO,OAAO,IAA6B,OAAO;AACpD"}
|
package/esm/config.d.mts
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import { QueueConfig } from "./types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/queue/src/config.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Set the active queue configuration. In a Warlock app the queue connector
|
|
6
5
|
* calls this at boot with `src/config/queue.ts`; scripts and tests may call
|
|
7
6
|
* it directly. Replaces (does not merge) any previous configuration.
|
|
8
7
|
*/
|
|
9
|
-
declare function setQueueConfig(config: QueueConfig): void;
|
|
8
|
+
export declare function setQueueConfig(config: QueueConfig): void;
|
|
10
9
|
/**
|
|
11
10
|
* The active queue configuration. Throws {@link QueueNotConfiguredError}
|
|
12
11
|
* when none was set.
|
|
13
12
|
*/
|
|
14
|
-
declare function getQueueConfig(): QueueConfig;
|
|
13
|
+
export declare function getQueueConfig(): QueueConfig;
|
|
15
14
|
/** Forget the active configuration. */
|
|
16
|
-
declare function resetQueueConfig(): void;
|
|
15
|
+
export declare function resetQueueConfig(): void;
|
|
17
16
|
/** The queue a job runs on when it names none. */
|
|
18
|
-
declare function defaultQueueName(): string;
|
|
17
|
+
export declare function defaultQueueName(): string;
|
|
19
18
|
//#endregion
|
|
20
|
-
export { defaultQueueName, getQueueConfig, resetQueueConfig, setQueueConfig };
|
|
21
19
|
//# sourceMappingURL=config.d.mts.map
|
package/esm/config.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/config.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/config.ts"],"mappings":";;;;;;;wBAUgB,eAAe,QAAQ;;;;;wBAQvB,kBAAkB;;wBASlB;;wBAKA"}
|
package/esm/dashboard-boot.d.mts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
import { QueueConfig } from "./types.mjs";
|
|
2
2
|
import { DashboardServer } from "./dashboard.mjs";
|
|
3
|
-
|
|
4
3
|
//#region ../../packages/queue/src/dashboard-boot.d.ts
|
|
5
4
|
/** Default path the dashboard mounts on when `dashboard.path` is not set. */
|
|
6
|
-
declare const DEFAULT_DASHBOARD_PATH = "/admin/queues";
|
|
5
|
+
export declare const DEFAULT_DASHBOARD_PATH = "/admin/queues";
|
|
7
6
|
/**
|
|
8
7
|
* Mount the bull-board dashboard for `queue.dashboard` in `config`, applying
|
|
9
8
|
* the safety rule: `enabled` in production with no middleware throws
|
|
@@ -14,7 +13,6 @@ declare const DEFAULT_DASHBOARD_PATH = "/admin/queues";
|
|
|
14
13
|
* before it starts listening — see `queue-connector.ts`. Exported so it can
|
|
15
14
|
* be unit-tested without going through the whole connector lifecycle.
|
|
16
15
|
*/
|
|
17
|
-
declare function mountQueueDashboard(server: DashboardServer | undefined, config: QueueConfig): Promise<void>;
|
|
16
|
+
export declare function mountQueueDashboard(server: DashboardServer | undefined, config: QueueConfig): Promise<void>;
|
|
18
17
|
//#endregion
|
|
19
|
-
export { DEFAULT_DASHBOARD_PATH, mountQueueDashboard };
|
|
20
18
|
//# sourceMappingURL=dashboard-boot.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard-boot.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/dashboard-boot.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"dashboard-boot.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/dashboard-boot.ts"],"mappings":";;;;qBAMa;;;;;;;;;;;wBAYS,oBACpB,QAAQ,6BACR,QAAQ,cACP"}
|
package/esm/dashboard.d.mts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
1
|
import { Middleware } from "@warlock.js/core";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/queue/src/dashboard.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* The part of a Fastify instance the dashboard needs. Warlock's HTTP server
|
|
6
5
|
* (`getHttpServer()` from `@warlock.js/core`) satisfies it.
|
|
7
6
|
*/
|
|
8
|
-
type DashboardServer = {
|
|
7
|
+
export type DashboardServer = {
|
|
9
8
|
register(plugin: never, options: {
|
|
10
9
|
prefix: string;
|
|
11
10
|
}): unknown;
|
|
12
11
|
};
|
|
13
|
-
type QueueDashboardOptions = {
|
|
14
|
-
/** URL prefix the dashboard is mounted on. Default `"/admin/queues"`. */
|
|
12
|
+
export type QueueDashboardOptions = {
|
|
13
|
+
/** URL prefix the dashboard is mounted on. Default `"/admin/queues"`. */
|
|
14
|
+
basePath?: string;
|
|
15
|
+
/** Queues to show. Default: every queue with a defined job, plus the default queue. */
|
|
15
16
|
queues?: string[];
|
|
16
17
|
/**
|
|
17
18
|
* Run before every dashboard route. Applied via a wrapping Fastify plugin
|
|
@@ -44,12 +45,12 @@ type BullBoardModules = {
|
|
|
44
45
|
* import { getHttpServer } from "@warlock.js/core";
|
|
45
46
|
* await queueDashboard(getHttpServer(), { basePath: "/admin/queues" });
|
|
46
47
|
*/
|
|
47
|
-
declare function queueDashboard(server: DashboardServer, options?: QueueDashboardOptions): Promise<void>;
|
|
48
|
+
export declare function queueDashboard(server: DashboardServer, options?: QueueDashboardOptions): Promise<void>;
|
|
48
49
|
/**
|
|
49
50
|
* Load the optional bull-board packages. Exported for tests of the missing
|
|
50
51
|
* dependency path; `importer` defaults to a real dynamic import.
|
|
51
52
|
*/
|
|
52
|
-
declare function loadBullBoard(importer?: (specifier: string) => Promise<Record<string, unknown>>): Promise<BullBoardModules>;
|
|
53
|
+
export declare function loadBullBoard(importer?: (specifier: string) => Promise<Record<string, unknown>>): Promise<BullBoardModules>;
|
|
53
54
|
//#endregion
|
|
54
|
-
export {
|
|
55
|
+
export {};
|
|
55
56
|
//# sourceMappingURL=dashboard.d.mts.map
|
package/esm/dashboard.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/dashboard.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"dashboard.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/dashboard.ts"],"mappings":";;;;;;YAWY;EACV,SAAS,eAAe;IAAW;;;YAGzB;;EAEV;;EAEA;;;;;;EAMA,aAAa;;KAGV;EACH,kBAAkB;IAAW;IAAmB;;EAChD,oBAAoB;EACpB;IACE,YAAY;IACZ;;;;;;;;;;;;;;;;wBAiBkB,eACpB,QAAQ,iBACR,UAAS,wBACR;;;;;wBA2BmB,cACpB,YAAW,sBAAsB,QAAQ,2BAExC,QAAQ"}
|
package/esm/dashboard.mjs
CHANGED
|
@@ -21,7 +21,7 @@ import { getQueue } from "./queue-manager.mjs";
|
|
|
21
21
|
async function queueDashboard(server, options = {}) {
|
|
22
22
|
const basePath = options.basePath ?? "/admin/queues";
|
|
23
23
|
const { createBullBoard, BullMQAdapter, FastifyAdapter } = await loadBullBoard();
|
|
24
|
-
const queueNames = options.queues ?? [
|
|
24
|
+
const queueNames = options.queues ?? [.../* @__PURE__ */ new Set([defaultQueueName(), ...registeredJobs().map((job) => queueOf(job))])];
|
|
25
25
|
const serverAdapter = new FastifyAdapter();
|
|
26
26
|
serverAdapter.setBasePath(basePath);
|
|
27
27
|
createBullBoard({
|
package/esm/dashboard.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dashboard.mjs","names":[],"sources":["../../../../../../../packages/queue/src/dashboard.ts"],"sourcesContent":["import type { Middleware } from \"@warlock.js/core\";\nimport { defaultQueueName } from \"./config\";\nimport { buildDashboardGuardPlugin } from \"./dashboard-guard-plugin\";\nimport { QueueDashboardDependencyError } from \"./errors\";\nimport { queueOf, registeredJobs } from \"./job-registry\";\nimport { getQueue } from \"./queue-manager\";\n\n/**\n * The part of a Fastify instance the dashboard needs. Warlock's HTTP server\n * (`getHttpServer()` from `@warlock.js/core`) satisfies it.\n */\nexport type DashboardServer = {\n register(plugin: never, options: { prefix: string }): unknown;\n};\n\nexport type QueueDashboardOptions = {\n /** URL prefix the dashboard is mounted on. Default `\"/admin/queues\"`. */\n basePath?: string;\n /** Queues to show. Default: every queue with a defined job, plus the default queue. */\n queues?: string[];\n /**\n * Run before every dashboard route. Applied via a wrapping Fastify plugin\n * scope, since bull-board's own plugin has no hook to splice Warlock\n * middleware into — see `dashboard-guard-plugin.ts`.\n */\n middleware?: Middleware[];\n};\n\ntype BullBoardModules = {\n createBullBoard: (options: { queues: unknown[]; serverAdapter: unknown }) => unknown;\n BullMQAdapter: new (queue: unknown) => unknown;\n FastifyAdapter: new () => {\n setBasePath(path: string): unknown;\n registerPlugin(): unknown;\n };\n};\n\n/**\n * Mount the bull-board failed/active/delayed job UI on Warlock's Fastify\n * server. Requires the optional packages `@bull-board/api` and\n * `@bull-board/fastify`; they are loaded only when this is called, and a\n * missing one throws {@link QueueDashboardDependencyError}.\n *\n * Call it before the HTTP server starts listening, and put it behind your\n * own authentication — the dashboard can retry and delete jobs.\n *\n * @example\n * import { getHttpServer } from \"@warlock.js/core\";\n * await queueDashboard(getHttpServer(), { basePath: \"/admin/queues\" });\n */\nexport async function queueDashboard(\n server: DashboardServer,\n options: QueueDashboardOptions = {},\n): Promise<void> {\n const basePath = options.basePath ?? \"/admin/queues\";\n const { createBullBoard, BullMQAdapter, FastifyAdapter } = await loadBullBoard();\n const queueNames = options.queues ?? [\n ...new Set([defaultQueueName(), ...registeredJobs().map((job) => queueOf(job))]),\n ];\n\n const serverAdapter = new FastifyAdapter();\n serverAdapter.setBasePath(basePath);\n\n createBullBoard({\n queues: queueNames.map((name) => new BullMQAdapter(getQueue(name))),\n serverAdapter,\n });\n\n const guardedPlugin = buildDashboardGuardPlugin(\n options.middleware ?? [],\n serverAdapter.registerPlugin(),\n );\n\n await server.register(guardedPlugin as never, { prefix: basePath });\n}\n\n/**\n * Load the optional bull-board packages. Exported for tests of the missing\n * dependency path; `importer` defaults to a real dynamic import.\n */\nexport async function loadBullBoard(\n importer: (specifier: string) => Promise<Record<string, unknown>> = (specifier) =>\n import(specifier),\n): Promise<BullBoardModules> {\n const api = await importOptional(importer, \"@bull-board/api\");\n const adapter = await importOptional(importer, \"@bull-board/api/bullMQAdapter\");\n const fastify = await importOptional(importer, \"@bull-board/fastify\");\n\n return {\n createBullBoard: api.createBullBoard as BullBoardModules[\"createBullBoard\"],\n BullMQAdapter: adapter.BullMQAdapter as BullBoardModules[\"BullMQAdapter\"],\n FastifyAdapter: fastify.FastifyAdapter as BullBoardModules[\"FastifyAdapter\"],\n };\n}\n\nasync function importOptional(\n importer: (specifier: string) => Promise<Record<string, unknown>>,\n specifier: string,\n): Promise<Record<string, unknown>> {\n try {\n return await importer(specifier);\n } catch (error) {\n if (isModuleNotFound(error)) {\n throw new QueueDashboardDependencyError(specifier.split(\"/\").slice(0, 2).join(\"/\"));\n }\n\n throw error;\n }\n}\n\nfunction isModuleNotFound(error: unknown): boolean {\n const code = (error as { code?: unknown } | undefined)?.code;\n\n return code === \"ERR_MODULE_NOT_FOUND\" || code === \"MODULE_NOT_FOUND\";\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAkDA,eAAsB,eACpB,QACA,UAAiC,CAAC,GACnB;CACf,MAAM,WAAW,QAAQ,YAAY;CACrC,MAAM,EAAE,iBAAiB,eAAe,mBAAmB,MAAM,cAAc;CAC/E,MAAM,aAAa,QAAQ,UAAU,CACnC,
|
|
1
|
+
{"version":3,"file":"dashboard.mjs","names":[],"sources":["../../../../../../../packages/queue/src/dashboard.ts"],"sourcesContent":["import type { Middleware } from \"@warlock.js/core\";\nimport { defaultQueueName } from \"./config\";\nimport { buildDashboardGuardPlugin } from \"./dashboard-guard-plugin\";\nimport { QueueDashboardDependencyError } from \"./errors\";\nimport { queueOf, registeredJobs } from \"./job-registry\";\nimport { getQueue } from \"./queue-manager\";\n\n/**\n * The part of a Fastify instance the dashboard needs. Warlock's HTTP server\n * (`getHttpServer()` from `@warlock.js/core`) satisfies it.\n */\nexport type DashboardServer = {\n register(plugin: never, options: { prefix: string }): unknown;\n};\n\nexport type QueueDashboardOptions = {\n /** URL prefix the dashboard is mounted on. Default `\"/admin/queues\"`. */\n basePath?: string;\n /** Queues to show. Default: every queue with a defined job, plus the default queue. */\n queues?: string[];\n /**\n * Run before every dashboard route. Applied via a wrapping Fastify plugin\n * scope, since bull-board's own plugin has no hook to splice Warlock\n * middleware into — see `dashboard-guard-plugin.ts`.\n */\n middleware?: Middleware[];\n};\n\ntype BullBoardModules = {\n createBullBoard: (options: { queues: unknown[]; serverAdapter: unknown }) => unknown;\n BullMQAdapter: new (queue: unknown) => unknown;\n FastifyAdapter: new () => {\n setBasePath(path: string): unknown;\n registerPlugin(): unknown;\n };\n};\n\n/**\n * Mount the bull-board failed/active/delayed job UI on Warlock's Fastify\n * server. Requires the optional packages `@bull-board/api` and\n * `@bull-board/fastify`; they are loaded only when this is called, and a\n * missing one throws {@link QueueDashboardDependencyError}.\n *\n * Call it before the HTTP server starts listening, and put it behind your\n * own authentication — the dashboard can retry and delete jobs.\n *\n * @example\n * import { getHttpServer } from \"@warlock.js/core\";\n * await queueDashboard(getHttpServer(), { basePath: \"/admin/queues\" });\n */\nexport async function queueDashboard(\n server: DashboardServer,\n options: QueueDashboardOptions = {},\n): Promise<void> {\n const basePath = options.basePath ?? \"/admin/queues\";\n const { createBullBoard, BullMQAdapter, FastifyAdapter } = await loadBullBoard();\n const queueNames = options.queues ?? [\n ...new Set([defaultQueueName(), ...registeredJobs().map((job) => queueOf(job))]),\n ];\n\n const serverAdapter = new FastifyAdapter();\n serverAdapter.setBasePath(basePath);\n\n createBullBoard({\n queues: queueNames.map((name) => new BullMQAdapter(getQueue(name))),\n serverAdapter,\n });\n\n const guardedPlugin = buildDashboardGuardPlugin(\n options.middleware ?? [],\n serverAdapter.registerPlugin(),\n );\n\n await server.register(guardedPlugin as never, { prefix: basePath });\n}\n\n/**\n * Load the optional bull-board packages. Exported for tests of the missing\n * dependency path; `importer` defaults to a real dynamic import.\n */\nexport async function loadBullBoard(\n importer: (specifier: string) => Promise<Record<string, unknown>> = (specifier) =>\n import(specifier),\n): Promise<BullBoardModules> {\n const api = await importOptional(importer, \"@bull-board/api\");\n const adapter = await importOptional(importer, \"@bull-board/api/bullMQAdapter\");\n const fastify = await importOptional(importer, \"@bull-board/fastify\");\n\n return {\n createBullBoard: api.createBullBoard as BullBoardModules[\"createBullBoard\"],\n BullMQAdapter: adapter.BullMQAdapter as BullBoardModules[\"BullMQAdapter\"],\n FastifyAdapter: fastify.FastifyAdapter as BullBoardModules[\"FastifyAdapter\"],\n };\n}\n\nasync function importOptional(\n importer: (specifier: string) => Promise<Record<string, unknown>>,\n specifier: string,\n): Promise<Record<string, unknown>> {\n try {\n return await importer(specifier);\n } catch (error) {\n if (isModuleNotFound(error)) {\n throw new QueueDashboardDependencyError(specifier.split(\"/\").slice(0, 2).join(\"/\"));\n }\n\n throw error;\n }\n}\n\nfunction isModuleNotFound(error: unknown): boolean {\n const code = (error as { code?: unknown } | undefined)?.code;\n\n return code === \"ERR_MODULE_NOT_FOUND\" || code === \"MODULE_NOT_FOUND\";\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAkDA,eAAsB,eACpB,QACA,UAAiC,CAAC,GACnB;CACf,MAAM,WAAW,QAAQ,YAAY;CACrC,MAAM,EAAE,iBAAiB,eAAe,mBAAmB,MAAM,cAAc;CAC/E,MAAM,aAAa,QAAQ,UAAU,CACnC,mBAAG,IAAI,IAAI,CAAC,iBAAiB,GAAG,GAAG,eAAe,CAAC,CAAC,KAAK,QAAQ,QAAQ,GAAG,CAAC,CAAC,CAAC,CACjF;CAEA,MAAM,gBAAgB,IAAI,eAAe;CACzC,cAAc,YAAY,QAAQ;CAElC,gBAAgB;EACd,QAAQ,WAAW,KAAK,SAAS,IAAI,cAAc,SAAS,IAAI,CAAC,CAAC;EAClE;CACF,CAAC;CAED,MAAM,gBAAgB,0BACpB,QAAQ,cAAc,CAAC,GACvB,cAAc,eAAe,CAC/B;CAEA,MAAM,OAAO,SAAS,eAAwB,EAAE,QAAQ,SAAS,CAAC;AACpE;;;;;AAMA,eAAsB,cACpB,YAAqE,cACnE,OAAO,YACkB;CAC3B,MAAM,MAAM,MAAM,eAAe,UAAU,iBAAiB;CAC5D,MAAM,UAAU,MAAM,eAAe,UAAU,+BAA+B;CAC9E,MAAM,UAAU,MAAM,eAAe,UAAU,qBAAqB;CAEpE,OAAO;EACL,iBAAiB,IAAI;EACrB,eAAe,QAAQ;EACvB,gBAAgB,QAAQ;CAC1B;AACF;AAEA,eAAe,eACb,UACA,WACkC;CAClC,IAAI;EACF,OAAO,MAAM,SAAS,SAAS;CACjC,SAAS,OAAO;EACd,IAAI,iBAAiB,KAAK,GACxB,MAAM,IAAI,8BAA8B,UAAU,MAAM,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,KAAK,GAAG,CAAC;EAGpF,MAAM;CACR;AACF;AAEA,SAAS,iBAAiB,OAAyB;CACjD,MAAM,OAAQ,OAA0C;CAExD,OAAO,SAAS,0BAA0B,SAAS;AACrD"}
|
package/esm/define-job.d.mts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { JobDefinition, QueueJob } from "./types.mjs";
|
|
2
|
+
import "bullmq";
|
|
2
3
|
//#region ../../packages/queue/src/define-job.d.ts
|
|
3
4
|
/**
|
|
4
5
|
* Define a background job.
|
|
@@ -18,7 +19,6 @@ import { JobDefinition, QueueJob } from "./types.mjs";
|
|
|
18
19
|
*
|
|
19
20
|
* await sendInvoice.dispatch({ invoiceId: "42" }, { delay: "10m", priority: 1 });
|
|
20
21
|
*/
|
|
21
|
-
declare function defineJob<TPayload, TResult = unknown>(definition: JobDefinition<TPayload, TResult>): QueueJob<TPayload, TResult>;
|
|
22
|
+
export declare function defineJob<TPayload, TResult = unknown>(definition: JobDefinition<TPayload, TResult>): QueueJob<TPayload, TResult>;
|
|
22
23
|
//#endregion
|
|
23
|
-
export { defineJob };
|
|
24
24
|
//# sourceMappingURL=define-job.d.mts.map
|
package/esm/define-job.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"define-job.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/define-job.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"define-job.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/define-job.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;wBAoCgB,UAAU,UAAU,mBAClC,YAAY,cAAc,UAAU,WACnC,SAAS,UAAU"}
|
package/esm/define-job.mjs
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { DispatchOptions, DispatchedJob, JobSnapshot } from "./types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/queue/src/dispatch-interceptor.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* A stand-in for the broker, used by test fakes.
|
|
@@ -13,7 +12,7 @@ import { DispatchOptions, DispatchedJob, JobSnapshot } from "./types.mjs";
|
|
|
13
12
|
* it. The slot lives on `globalThis` so every copy of this package in a
|
|
14
13
|
* process (a Vitest worker can load two) sees the same one.
|
|
15
14
|
*/
|
|
16
|
-
type DispatchInterceptor = {
|
|
15
|
+
export type DispatchInterceptor = {
|
|
17
16
|
dispatch(job: {
|
|
18
17
|
name: string;
|
|
19
18
|
queue: string;
|
|
@@ -27,9 +26,8 @@ type DispatchInterceptor = {
|
|
|
27
26
|
}): JobSnapshot | undefined | Promise<JobSnapshot | undefined>;
|
|
28
27
|
};
|
|
29
28
|
/** Install an interceptor, or remove it with `undefined`. */
|
|
30
|
-
declare function setDispatchInterceptor(interceptor: DispatchInterceptor | undefined): void;
|
|
29
|
+
export declare function setDispatchInterceptor(interceptor: DispatchInterceptor | undefined): void;
|
|
31
30
|
/** The installed interceptor, if any. */
|
|
32
|
-
declare function getDispatchInterceptor(): DispatchInterceptor | undefined;
|
|
31
|
+
export declare function getDispatchInterceptor(): DispatchInterceptor | undefined;
|
|
33
32
|
//#endregion
|
|
34
|
-
export { DispatchInterceptor, getDispatchInterceptor, setDispatchInterceptor };
|
|
35
33
|
//# sourceMappingURL=dispatch-interceptor.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dispatch-interceptor.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/dispatch-interceptor.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"dispatch-interceptor.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/dispatch-interceptor.ts"],"mappings":";;;;;;;;;;;;;;YAcY;EACV,SAAS;IACP;IACA;IACA;IACA,SAAS;MACP,gBAAgB,QAAQ;EAC5B,MAAM;IACJ;IACA;IACA;MACE,0BAA0B,QAAQ;;;wBAQxB,uBAAuB,aAAa;;wBAKpC,0BAA0B"}
|
package/esm/duration.d.mts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
import { Duration } from "./types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/queue/src/duration.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* Convert a {@link Duration} to milliseconds. Numbers are already
|
|
6
5
|
* milliseconds. Anything else is rejected loudly rather than guessed at.
|
|
7
6
|
*/
|
|
8
|
-
declare function toMilliseconds(value: Duration | string): number;
|
|
7
|
+
export declare function toMilliseconds(value: Duration | string): number;
|
|
9
8
|
//#endregion
|
|
10
|
-
export { toMilliseconds };
|
|
11
9
|
//# sourceMappingURL=duration.d.mts.map
|
package/esm/duration.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duration.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/duration.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"duration.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/duration.ts"],"mappings":";;;;;;wBAiBgB,eAAe,OAAO"}
|
package/esm/errors.d.mts
CHANGED
|
@@ -3,43 +3,42 @@
|
|
|
3
3
|
* Thrown when the queue is used before `setQueueConfig` (or the queue
|
|
4
4
|
* connector) supplied a configuration.
|
|
5
5
|
*/
|
|
6
|
-
declare class QueueNotConfiguredError extends Error {
|
|
6
|
+
export declare class QueueNotConfiguredError extends Error {
|
|
7
7
|
constructor();
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* Thrown for a malformed duration such as `"10 minutes"`.
|
|
11
11
|
*/
|
|
12
|
-
declare class InvalidDurationError extends Error {
|
|
12
|
+
export declare class InvalidDurationError extends Error {
|
|
13
13
|
constructor(value: unknown);
|
|
14
14
|
}
|
|
15
15
|
/**
|
|
16
16
|
* Thrown by `defineJob` for an invalid definition.
|
|
17
17
|
*/
|
|
18
|
-
declare class InvalidJobDefinitionError extends Error {
|
|
18
|
+
export declare class InvalidJobDefinitionError extends Error {
|
|
19
19
|
constructor(message: string);
|
|
20
20
|
}
|
|
21
21
|
/**
|
|
22
22
|
* Thrown by `retryFailedJob` when no failed job has the given id.
|
|
23
23
|
*/
|
|
24
|
-
declare class FailedJobNotFoundError extends Error {
|
|
24
|
+
export declare class FailedJobNotFoundError extends Error {
|
|
25
25
|
constructor(id: string, queue: string);
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
28
|
* Thrown by `queueDashboard` when an optional bull-board package is not
|
|
29
29
|
* installed.
|
|
30
30
|
*/
|
|
31
|
-
declare class QueueDashboardDependencyError extends Error {
|
|
31
|
+
export declare class QueueDashboardDependencyError extends Error {
|
|
32
32
|
constructor(missing: string);
|
|
33
33
|
}
|
|
34
34
|
/**
|
|
35
35
|
* Throw from a job handler (or a queue context's `restore`) to fail the job
|
|
36
36
|
* permanently — it is not retried, whatever `attempts` allows.
|
|
37
37
|
*/
|
|
38
|
-
declare class UnrecoverableJobError extends Error {
|
|
38
|
+
export declare class UnrecoverableJobError extends Error {
|
|
39
39
|
constructor(message: string, options?: {
|
|
40
40
|
cause?: unknown;
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
43
|
//#endregion
|
|
44
|
-
export { FailedJobNotFoundError, InvalidDurationError, InvalidJobDefinitionError, QueueDashboardDependencyError, QueueNotConfiguredError, UnrecoverableJobError };
|
|
45
44
|
//# sourceMappingURL=errors.d.mts.map
|
package/esm/errors.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"errors.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/errors.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"errors.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/errors.ts"],"mappings":";;;;;qBAIa,gCAAgC;EAC3C;;;;;qBAaW,6BAA6B;EACxC,YAAmB;;;;;qBAYR,kCAAkC;EAC7C,YAAmB;;;;;qBASR,+BAA+B;EAC1C,YAAmB,YAAY;;;;;;qBAUpB,sCAAsC;EACjD,YAAmB;;;;;;qBAcR,8BAA8B;EACzC,YAAmB,iBAAiB;IAAY"}
|
package/esm/failed-jobs.d.mts
CHANGED
|
@@ -1,23 +1,24 @@
|
|
|
1
1
|
import { FailedJob } from "./types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/queue/src/failed-jobs.d.ts
|
|
4
|
-
type FailedJobsOptions = {
|
|
5
|
-
/** Queue to read. Default: the default queue. */
|
|
6
|
-
|
|
3
|
+
export type FailedJobsOptions = {
|
|
4
|
+
/** Queue to read. Default: the default queue. */
|
|
5
|
+
queue?: string;
|
|
6
|
+
/** First index (newest first). Default `0`. */
|
|
7
|
+
start?: number;
|
|
8
|
+
/** Last index, inclusive. Default `99`. */
|
|
7
9
|
end?: number;
|
|
8
10
|
};
|
|
9
11
|
/**
|
|
10
12
|
* List failed jobs, newest first — jobs that used up every attempt, or
|
|
11
13
|
* failed unrecoverably. Each entry can be retried.
|
|
12
14
|
*/
|
|
13
|
-
declare function failedJobs(options?: FailedJobsOptions): Promise<FailedJob[]>;
|
|
15
|
+
export declare function failedJobs(options?: FailedJobsOptions): Promise<FailedJob[]>;
|
|
14
16
|
/**
|
|
15
17
|
* Retry one failed job by id: it goes back to waiting with its attempts reset.
|
|
16
18
|
* Throws {@link FailedJobNotFoundError} when no FAILED job has that id.
|
|
17
19
|
*/
|
|
18
|
-
declare function retryFailedJob(id: string, options?: {
|
|
20
|
+
export declare function retryFailedJob(id: string, options?: {
|
|
19
21
|
queue?: string;
|
|
20
22
|
}): Promise<void>;
|
|
21
23
|
//#endregion
|
|
22
|
-
export { FailedJobsOptions, failedJobs, retryFailedJob };
|
|
23
24
|
//# sourceMappingURL=failed-jobs.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failed-jobs.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/failed-jobs.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"failed-jobs.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/failed-jobs.ts"],"mappings":";;YAOY;;EAEV;;EAEA;;EAEA;;;;;;wBAOoB,WAAW,UAAS,oBAAyB,QAAQ;;;;;wBAWrD,eAAe,YAAY;EAAW;IAAwB"}
|
package/esm/failed-jobs.mjs
CHANGED
|
@@ -9,7 +9,8 @@ import { getQueue } from "./queue-manager.mjs";
|
|
|
9
9
|
* failed unrecoverably. Each entry can be retried.
|
|
10
10
|
*/
|
|
11
11
|
async function failedJobs(options = {}) {
|
|
12
|
-
|
|
12
|
+
const queueName = options.queue ?? defaultQueueName();
|
|
13
|
+
return (await getQueue(queueName).getFailed(options.start ?? 0, options.end ?? 99)).map((job) => toFailedJob(job));
|
|
13
14
|
}
|
|
14
15
|
/**
|
|
15
16
|
* Retry one failed job by id: it goes back to waiting with its attempts reset.
|
package/esm/failed-jobs.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failed-jobs.mjs","names":[],"sources":["../../../../../../../packages/queue/src/failed-jobs.ts"],"sourcesContent":["import { unwrapPayload } from \"./queue-context\";\nimport type { Job } from \"bullmq\";\nimport { defaultQueueName } from \"./config\";\nimport { FailedJobNotFoundError } from \"./errors\";\nimport { getQueue } from \"./queue-manager\";\nimport type { FailedJob } from \"./types\";\n\nexport type FailedJobsOptions = {\n /** Queue to read. Default: the default queue. */\n queue?: string;\n /** First index (newest first). Default `0`. */\n start?: number;\n /** Last index, inclusive. Default `99`. */\n end?: number;\n};\n\n/**\n * List failed jobs, newest first — jobs that used up every attempt, or\n * failed unrecoverably. Each entry can be retried.\n */\nexport async function failedJobs(options: FailedJobsOptions = {}): Promise<FailedJob[]> {\n const queueName = options.queue ?? defaultQueueName();\n const jobs = await getQueue(queueName).getFailed(options.start ?? 0, options.end ?? 99);\n\n return jobs.map((job) => toFailedJob(job));\n}\n\n/**\n * Retry one failed job by id: it goes back to waiting with its attempts reset.\n * Throws {@link FailedJobNotFoundError} when no FAILED job has that id.\n */\nexport async function retryFailedJob(id: string, options: { queue?: string } = {}): Promise<void> {\n const queueName = options.queue ?? defaultQueueName();\n const job = await getQueue(queueName).getJob(id);\n\n if (!job || !(await job.isFailed())) {\n throw new FailedJobNotFoundError(id, queueName);\n }\n\n await job.retry(\"failed\");\n}\n\nfunction toFailedJob(job: Job): FailedJob {\n return {\n id: String(job.id),\n name: job.name,\n queue: job.queueName,\n payload: unwrapPayload(job.data).payload,\n attemptsMade: job.attemptsMade,\n failedReason: job.failedReason,\n stacktrace: job.stacktrace ?? [],\n failedAt: job.finishedOn ? new Date(job.finishedOn) : undefined,\n retry: () => job.retry(\"failed\"),\n };\n}\n"],"mappings":";;;;;;;;;;AAoBA,eAAsB,WAAW,UAA6B,CAAC,GAAyB;
|
|
1
|
+
{"version":3,"file":"failed-jobs.mjs","names":[],"sources":["../../../../../../../packages/queue/src/failed-jobs.ts"],"sourcesContent":["import { unwrapPayload } from \"./queue-context\";\nimport type { Job } from \"bullmq\";\nimport { defaultQueueName } from \"./config\";\nimport { FailedJobNotFoundError } from \"./errors\";\nimport { getQueue } from \"./queue-manager\";\nimport type { FailedJob } from \"./types\";\n\nexport type FailedJobsOptions = {\n /** Queue to read. Default: the default queue. */\n queue?: string;\n /** First index (newest first). Default `0`. */\n start?: number;\n /** Last index, inclusive. Default `99`. */\n end?: number;\n};\n\n/**\n * List failed jobs, newest first — jobs that used up every attempt, or\n * failed unrecoverably. Each entry can be retried.\n */\nexport async function failedJobs(options: FailedJobsOptions = {}): Promise<FailedJob[]> {\n const queueName = options.queue ?? defaultQueueName();\n const jobs = await getQueue(queueName).getFailed(options.start ?? 0, options.end ?? 99);\n\n return jobs.map((job) => toFailedJob(job));\n}\n\n/**\n * Retry one failed job by id: it goes back to waiting with its attempts reset.\n * Throws {@link FailedJobNotFoundError} when no FAILED job has that id.\n */\nexport async function retryFailedJob(id: string, options: { queue?: string } = {}): Promise<void> {\n const queueName = options.queue ?? defaultQueueName();\n const job = await getQueue(queueName).getJob(id);\n\n if (!job || !(await job.isFailed())) {\n throw new FailedJobNotFoundError(id, queueName);\n }\n\n await job.retry(\"failed\");\n}\n\nfunction toFailedJob(job: Job): FailedJob {\n return {\n id: String(job.id),\n name: job.name,\n queue: job.queueName,\n payload: unwrapPayload(job.data).payload,\n attemptsMade: job.attemptsMade,\n failedReason: job.failedReason,\n stacktrace: job.stacktrace ?? [],\n failedAt: job.finishedOn ? new Date(job.finishedOn) : undefined,\n retry: () => job.retry(\"failed\"),\n };\n}\n"],"mappings":";;;;;;;;;;AAoBA,eAAsB,WAAW,UAA6B,CAAC,GAAyB;CACtF,MAAM,YAAY,QAAQ,SAAS,iBAAiB;CAGpD,QAAO,MAFY,SAAS,SAAS,CAAC,CAAC,UAAU,QAAQ,SAAS,GAAG,QAAQ,OAAO,EAAE,EAE3E,CAAC,KAAK,QAAQ,YAAY,GAAG,CAAC;AAC3C;;;;;AAMA,eAAsB,eAAe,IAAY,UAA8B,CAAC,GAAkB;CAChG,MAAM,YAAY,QAAQ,SAAS,iBAAiB;CACpD,MAAM,MAAM,MAAM,SAAS,SAAS,CAAC,CAAC,OAAO,EAAE;CAE/C,IAAI,CAAC,OAAO,CAAE,MAAM,IAAI,SAAS,GAC/B,MAAM,IAAI,uBAAuB,IAAI,SAAS;CAGhD,MAAM,IAAI,MAAM,QAAQ;AAC1B;AAEA,SAAS,YAAY,KAAqB;CACxC,OAAO;EACL,IAAI,OAAO,IAAI,EAAE;EACjB,MAAM,IAAI;EACV,OAAO,IAAI;EACX,SAAS,cAAc,IAAI,IAAI,CAAC,CAAC;EACjC,cAAc,IAAI;EAClB,cAAc,IAAI;EAClB,YAAY,IAAI,cAAc,CAAC;EAC/B,UAAU,IAAI,aAAa,IAAI,KAAK,IAAI,UAAU,IAAI;EACtD,aAAa,IAAI,MAAM,QAAQ;CACjC;AACF"}
|
package/esm/job-registry.d.mts
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { JobDefinition } from "./types.mjs";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/queue/src/job-registry.d.ts
|
|
4
3
|
/** A registered definition, payload/result erased for storage. */
|
|
5
|
-
type RegisteredJob = JobDefinition<unknown, unknown>;
|
|
4
|
+
export type RegisteredJob = JobDefinition<unknown, unknown>;
|
|
6
5
|
/** Every registered definition. */
|
|
7
|
-
declare function registeredJobs(): RegisteredJob[];
|
|
6
|
+
export declare function registeredJobs(): RegisteredJob[];
|
|
8
7
|
/** The queue a definition runs on, resolved against the active config. */
|
|
9
|
-
declare function queueOf(job: Pick<RegisteredJob, "queue">): string;
|
|
8
|
+
export declare function queueOf(job: Pick<RegisteredJob, "queue">): string;
|
|
10
9
|
//#endregion
|
|
11
|
-
export { RegisteredJob, queueOf, registeredJobs };
|
|
12
10
|
//# sourceMappingURL=job-registry.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"job-registry.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/job-registry.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"job-registry.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/job-registry.ts"],"mappings":";;;YAIY,gBAAgB;;wBA8BZ,kBAAkB;;wBAKlB,QAAQ,KAAK,KAAK"}
|
|
@@ -1,14 +1,13 @@
|
|
|
1
1
|
import { QueueConfig } from "./types.mjs";
|
|
2
2
|
import { Connector } from "@warlock.js/core";
|
|
3
|
-
|
|
4
3
|
//#region ../../packages/queue/src/queue-connector.d.ts
|
|
5
4
|
/**
|
|
6
5
|
* Boots after every built-in connector (`ConnectorPriority.AI` is `10`), so
|
|
7
6
|
* the logger is up and anything a job handler needs is already connected;
|
|
8
7
|
* shuts down before them for the same reason.
|
|
9
8
|
*/
|
|
10
|
-
declare const QUEUE_CONNECTOR_PRIORITY = 11;
|
|
11
|
-
type QueueConnectorOptions = {
|
|
9
|
+
export declare const QUEUE_CONNECTOR_PRIORITY = 11;
|
|
10
|
+
export type QueueConnectorOptions = {
|
|
12
11
|
/**
|
|
13
12
|
* Supply the configuration directly instead of reading the `queue` config
|
|
14
13
|
* key (`src/config/queue.ts`).
|
|
@@ -30,7 +29,6 @@ type QueueConnectorOptions = {
|
|
|
30
29
|
*
|
|
31
30
|
* export default defineConfig({ connectors: [queueConnector()] });
|
|
32
31
|
*/
|
|
33
|
-
declare function queueConnector(options?: QueueConnectorOptions): Connector;
|
|
32
|
+
export declare function queueConnector(options?: QueueConnectorOptions): Connector;
|
|
34
33
|
//#endregion
|
|
35
|
-
export { QUEUE_CONNECTOR_PRIORITY, QueueConnectorOptions, queueConnector };
|
|
36
34
|
//# sourceMappingURL=queue-connector.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue-connector.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/queue-connector.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"queue-connector.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/queue-connector.ts"],"mappings":";;;;;;;;qBAoBa;YAID;;;;;EAKV,SAAS;;;;;;;;;;;;;;;;;wBAkBK,eAAe,UAAS,wBAA6B"}
|
package/esm/queue-context.d.mts
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
* Carries ambient state (tenant, request id, …) from the code that
|
|
4
4
|
* dispatches a job into the worker that runs it.
|
|
5
5
|
*/
|
|
6
|
-
type QueueContext<T> = {
|
|
7
|
-
/** Read the ambient state at dispatch time; `undefined` means nothing to carry. */
|
|
6
|
+
export type QueueContext<T> = {
|
|
7
|
+
/** Read the ambient state at dispatch time; `undefined` means nothing to carry. */
|
|
8
|
+
capture: () => T | undefined;
|
|
9
|
+
/** Run `run` with the captured state re-established. */
|
|
8
10
|
restore: (captured: T, run: () => Promise<unknown>) => Promise<unknown>;
|
|
9
11
|
};
|
|
10
12
|
/**
|
|
@@ -18,13 +20,12 @@ type QueueContext<T> = {
|
|
|
18
20
|
* }),
|
|
19
21
|
* );
|
|
20
22
|
*/
|
|
21
|
-
declare function defineQueueContext<T>(context: QueueContext<T>): QueueContext<T>;
|
|
23
|
+
export declare function defineQueueContext<T>(context: QueueContext<T>): QueueContext<T>;
|
|
22
24
|
/** Register the queue context used by every job dispatch and run. */
|
|
23
|
-
declare function setQueueContext(context: QueueContext<any>): void;
|
|
25
|
+
export declare function setQueueContext(context: QueueContext<any>): void;
|
|
24
26
|
/** The registered queue context, if any. */
|
|
25
|
-
declare function getQueueContext(): QueueContext<any> | undefined;
|
|
27
|
+
export declare function getQueueContext(): QueueContext<any> | undefined;
|
|
26
28
|
/** Forget the registered queue context. */
|
|
27
|
-
declare function resetQueueContext(): void;
|
|
29
|
+
export declare function resetQueueContext(): void;
|
|
28
30
|
//#endregion
|
|
29
|
-
export { QueueContext, defineQueueContext, getQueueContext, resetQueueContext, setQueueContext };
|
|
30
31
|
//# sourceMappingURL=queue-context.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue-context.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/queue-context.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"queue-context.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/queue-context.ts"],"mappings":";;;;;YAIY,aAAa;;EAEvB,eAAe;;EAEf,UAAU,UAAU,GAAG,WAAW,qBAAqB;;;;;;;;;;;;;wBAczC,mBAAmB,GAAG,SAAS,aAAa,KAAK,aAAa;;wBAO9D,gBAAgB,SAAS;;wBAKzB,mBAAmB;;wBAKnB"}
|
|
@@ -5,9 +5,8 @@
|
|
|
5
5
|
* on the open internet without a guard is a production incident waiting to
|
|
6
6
|
* happen, so this fails the boot instead of shipping the hole.
|
|
7
7
|
*/
|
|
8
|
-
declare class QueueDashboardUnguardedError extends Error {
|
|
8
|
+
export declare class QueueDashboardUnguardedError extends Error {
|
|
9
9
|
constructor();
|
|
10
10
|
}
|
|
11
11
|
//#endregion
|
|
12
|
-
export { QueueDashboardUnguardedError };
|
|
13
12
|
//# sourceMappingURL=queue-dashboard-unguarded.error.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue-dashboard-unguarded.error.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/queue-dashboard-unguarded.error.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"queue-dashboard-unguarded.error.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/queue-dashboard-unguarded.error.ts"],"mappings":";;;;;;;qBAMa,qCAAqC;EAChD"}
|
package/esm/queue-manager.d.mts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import { Queue } from "bullmq";
|
|
2
|
-
|
|
3
2
|
//#region ../../packages/queue/src/queue-manager.d.ts
|
|
4
3
|
/**
|
|
5
4
|
* The BullMQ queue for `name`, created on first use with the configured
|
|
6
5
|
* connection and prefix.
|
|
7
6
|
*/
|
|
8
|
-
declare function getQueue(name: string): Queue;
|
|
7
|
+
export declare function getQueue(name: string): Queue;
|
|
9
8
|
/**
|
|
10
9
|
* Start one worker per queue that has a registered job, and keep starting
|
|
11
10
|
* workers for queues whose first job is defined later.
|
|
@@ -18,10 +17,10 @@ declare function getQueue(name: string): Queue;
|
|
|
18
17
|
*
|
|
19
18
|
* @returns the queue names that now have a worker in this process.
|
|
20
19
|
*/
|
|
21
|
-
declare function startWorkers(): Promise<string[]>;
|
|
20
|
+
export declare function startWorkers(): Promise<string[]>;
|
|
22
21
|
/** The queue names with a running worker in this process. */
|
|
23
|
-
declare function runningWorkers(): string[];
|
|
24
|
-
type CloseQueueOptions = {
|
|
22
|
+
export declare function runningWorkers(): string[];
|
|
23
|
+
export type CloseQueueOptions = {
|
|
25
24
|
/**
|
|
26
25
|
* How long to wait for active jobs before force-closing workers, in
|
|
27
26
|
* milliseconds. Default: `workers.shutdownTimeout`, else `30000`.
|
|
@@ -33,7 +32,6 @@ type CloseQueueOptions = {
|
|
|
33
32
|
* (bounded by `timeout`, then force-close), then close every queue
|
|
34
33
|
* connection. Safe to call when nothing was started, and more than once.
|
|
35
34
|
*/
|
|
36
|
-
declare function closeQueue(options?: CloseQueueOptions): Promise<void>;
|
|
35
|
+
export declare function closeQueue(options?: CloseQueueOptions): Promise<void>;
|
|
37
36
|
//#endregion
|
|
38
|
-
export { CloseQueueOptions, closeQueue, getQueue, runningWorkers, startWorkers };
|
|
39
37
|
//# sourceMappingURL=queue-manager.d.mts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"queue-manager.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/queue-manager.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"queue-manager.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/queue-manager.ts"],"mappings":";;;;;;wBAgBgB,SAAS,eAAe;;;;;;;;;;;;;wBAiClB,gBAAgB;;wBAgCtB;YAgCJ;;;;;EAKV;;;;;;;wBAQoB,WAAW,UAAS,oBAAyB"}
|
package/esm/types.d.mts
CHANGED
|
@@ -1,46 +1,52 @@
|
|
|
1
1
|
import { ConnectionOptions } from "bullmq";
|
|
2
2
|
import { Middleware } from "@warlock.js/core";
|
|
3
|
-
|
|
4
3
|
//#region ../../packages/queue/src/types.d.ts
|
|
5
4
|
/** Units accepted in a {@link Duration} string. */
|
|
6
|
-
type DurationUnit = "ms" | "s" | "m" | "h" | "d";
|
|
5
|
+
export type DurationUnit = "ms" | "s" | "m" | "h" | "d";
|
|
7
6
|
/**
|
|
8
7
|
* A duration: milliseconds as a number, or a string such as `"500ms"`,
|
|
9
8
|
* `"30s"`, `"10m"`, `"2h"`, `"1d"`.
|
|
10
9
|
*/
|
|
11
|
-
type Duration = number | `${number}${DurationUnit}`;
|
|
10
|
+
export type Duration = number | `${number}${DurationUnit}`;
|
|
12
11
|
/**
|
|
13
12
|
* How a failed attempt waits before the next one. A number is a fixed delay in
|
|
14
13
|
* milliseconds.
|
|
15
14
|
*/
|
|
16
|
-
type JobBackoff = number | {
|
|
17
|
-
/** `fixed` waits `delay` every time; `exponential` waits `delay * 2^(attempt - 1)`. */
|
|
15
|
+
export type JobBackoff = number | {
|
|
16
|
+
/** `fixed` waits `delay` every time; `exponential` waits `delay * 2^(attempt - 1)`. */
|
|
17
|
+
type: "fixed" | "exponential";
|
|
18
|
+
/** Base delay in milliseconds. */
|
|
18
19
|
delay: number;
|
|
19
20
|
};
|
|
20
21
|
/**
|
|
21
22
|
* How many finished jobs to keep: `true` removes them immediately, `false`
|
|
22
23
|
* keeps them all, a number keeps the newest N.
|
|
23
24
|
*/
|
|
24
|
-
type JobRetention = boolean | number;
|
|
25
|
+
export type JobRetention = boolean | number;
|
|
25
26
|
/**
|
|
26
27
|
* Options that shape how a job is retried and retained. Set app-wide in
|
|
27
28
|
* `queue.defaultJobOptions`, per job in `defineJob`.
|
|
28
29
|
*/
|
|
29
|
-
type JobOptions = {
|
|
30
|
-
/** Total attempts including the first. Default `1` (no retry). */
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
export type JobOptions = {
|
|
31
|
+
/** Total attempts including the first. Default `1` (no retry). */
|
|
32
|
+
attempts?: number;
|
|
33
|
+
/** Wait between attempts. */
|
|
34
|
+
backoff?: JobBackoff;
|
|
35
|
+
/** Retention for completed jobs. Default keeps them. */
|
|
36
|
+
removeOnComplete?: JobRetention;
|
|
37
|
+
/** Retention for failed jobs. Default keeps them, so `failedJobs()` can list them. */
|
|
33
38
|
removeOnFail?: JobRetention;
|
|
34
39
|
};
|
|
35
40
|
/**
|
|
36
41
|
* In-process worker settings.
|
|
37
42
|
*/
|
|
38
|
-
type QueueWorkersConfig = {
|
|
43
|
+
export type QueueWorkersConfig = {
|
|
39
44
|
/**
|
|
40
45
|
* Start workers in this process. Default `true`. Set `false` for a process
|
|
41
46
|
* that only dispatches jobs while another process consumes them.
|
|
42
47
|
*/
|
|
43
|
-
enabled?: boolean;
|
|
48
|
+
enabled?: boolean;
|
|
49
|
+
/** Jobs processed in parallel per queue. Default `1`. */
|
|
44
50
|
concurrency?: number;
|
|
45
51
|
/**
|
|
46
52
|
* How long shutdown waits for active jobs before force-closing the workers,
|
|
@@ -53,8 +59,10 @@ type QueueWorkersConfig = {
|
|
|
53
59
|
* `enabled` is `true`. Equivalent to calling `queueDashboard()` yourself at
|
|
54
60
|
* boot, driven by config instead — see `warlock add bull-board`.
|
|
55
61
|
*/
|
|
56
|
-
type QueueDashboardConfig = {
|
|
57
|
-
/** Mount the dashboard at boot. Default `false`. */
|
|
62
|
+
export type QueueDashboardConfig = {
|
|
63
|
+
/** Mount the dashboard at boot. Default `false`. */
|
|
64
|
+
enabled?: boolean;
|
|
65
|
+
/** URL path the dashboard is mounted on. Default `"/admin/queues"`. */
|
|
58
66
|
path?: string;
|
|
59
67
|
/**
|
|
60
68
|
* Run before every dashboard route — this is how the dashboard is guarded.
|
|
@@ -66,47 +74,65 @@ type QueueDashboardConfig = {
|
|
|
66
74
|
/**
|
|
67
75
|
* The `queue` configuration key — `src/config/queue.ts`.
|
|
68
76
|
*/
|
|
69
|
-
type QueueConfig = {
|
|
77
|
+
export type QueueConfig = {
|
|
70
78
|
/**
|
|
71
79
|
* Redis connection. Any BullMQ connection option: `{ host, port, password, db }`,
|
|
72
80
|
* `{ url }`, or an ioredis instance.
|
|
73
81
|
*/
|
|
74
|
-
connection: ConnectionOptions;
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
82
|
+
connection: ConnectionOptions;
|
|
83
|
+
/** Redis key prefix for every queue. Default `"warlock"`. */
|
|
84
|
+
prefix?: string;
|
|
85
|
+
/** Queue name used when a job does not name one. Default `"default"`. */
|
|
86
|
+
defaultQueue?: string;
|
|
87
|
+
/** Defaults merged under every job's own options. */
|
|
88
|
+
defaultJobOptions?: JobOptions;
|
|
89
|
+
/** In-process workers. */
|
|
90
|
+
workers?: QueueWorkersConfig;
|
|
91
|
+
/** The bull-board job dashboard. */
|
|
79
92
|
dashboard?: QueueDashboardConfig;
|
|
80
93
|
};
|
|
81
94
|
/** A progress value: a number (e.g. a percentage) or a JSON object. */
|
|
82
|
-
type JobProgress = number | Record<string, unknown>;
|
|
95
|
+
export type JobProgress = number | Record<string, unknown>;
|
|
83
96
|
/**
|
|
84
97
|
* What a job handler receives beside its payload.
|
|
85
98
|
*/
|
|
86
|
-
type JobContext = {
|
|
87
|
-
/** The job id. */
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
99
|
+
export type JobContext = {
|
|
100
|
+
/** The job id. */
|
|
101
|
+
id: string;
|
|
102
|
+
/** The job name given to `defineJob`. */
|
|
103
|
+
name: string;
|
|
104
|
+
/** The queue the job runs on. */
|
|
105
|
+
queue: string;
|
|
106
|
+
/** The current attempt, starting at `1`. */
|
|
107
|
+
attempt: number;
|
|
108
|
+
/** Total attempts allowed. */
|
|
109
|
+
maxAttempts: number;
|
|
110
|
+
/** Record progress; readable through `job.find(id)`. */
|
|
111
|
+
progress(value: JobProgress): Promise<void>;
|
|
112
|
+
/** Append a line to the job's log. */
|
|
113
|
+
log(line: string): Promise<void>;
|
|
114
|
+
/** State captured at dispatch by the registered queue context, if any. */
|
|
94
115
|
context: unknown;
|
|
95
116
|
};
|
|
96
117
|
/**
|
|
97
118
|
* The definition passed to `defineJob`.
|
|
98
119
|
*/
|
|
99
|
-
type JobDefinition<TPayload, TResult> = JobOptions & {
|
|
100
|
-
/** Set `false` to skip capturing the queue context for this job. */
|
|
101
|
-
|
|
102
|
-
|
|
120
|
+
export type JobDefinition<TPayload, TResult> = JobOptions & {
|
|
121
|
+
/** Set `false` to skip capturing the queue context for this job. */
|
|
122
|
+
context?: false;
|
|
123
|
+
/** Unique job name, e.g. `"invoices.send"`. */
|
|
124
|
+
name: string;
|
|
125
|
+
/** Queue to run on. Default: `queue.defaultQueue` (`"default"`). */
|
|
126
|
+
queue?: string;
|
|
127
|
+
/** The work. Throw to fail the attempt; the return value is stored as the job result. */
|
|
103
128
|
handle(payload: TPayload, context: JobContext): Promise<TResult> | TResult;
|
|
104
129
|
};
|
|
105
130
|
/**
|
|
106
131
|
* Per-dispatch options.
|
|
107
132
|
*/
|
|
108
|
-
type DispatchOptions = {
|
|
109
|
-
/** Wait before the job becomes available. */
|
|
133
|
+
export type DispatchOptions = {
|
|
134
|
+
/** Wait before the job becomes available. */
|
|
135
|
+
delay?: Duration;
|
|
110
136
|
/**
|
|
111
137
|
* Priority: `1` is the highest; larger numbers run later. Omit for no
|
|
112
138
|
* priority — such jobs run ahead of every prioritized job.
|
|
@@ -116,20 +142,22 @@ type DispatchOptions = {
|
|
|
116
142
|
* Explicit job id. Dispatching again with an id that still exists is a
|
|
117
143
|
* no-op, which makes dispatch idempotent.
|
|
118
144
|
*/
|
|
119
|
-
jobId?: string;
|
|
120
|
-
|
|
145
|
+
jobId?: string;
|
|
146
|
+
/** Override the job's attempts for this dispatch. */
|
|
147
|
+
attempts?: number;
|
|
148
|
+
/** Override the job's backoff for this dispatch. */
|
|
121
149
|
backoff?: JobBackoff;
|
|
122
150
|
};
|
|
123
151
|
/** What `dispatch` resolves with. */
|
|
124
|
-
type DispatchedJob = {
|
|
152
|
+
export type DispatchedJob = {
|
|
125
153
|
id: string;
|
|
126
154
|
name: string;
|
|
127
155
|
queue: string;
|
|
128
156
|
};
|
|
129
157
|
/** A job's lifecycle state. */
|
|
130
|
-
type JobState = "waiting" | "delayed" | "prioritized" | "active" | "completed" | "failed" | "waiting-children" | "unknown";
|
|
158
|
+
export type JobState = "waiting" | "delayed" | "prioritized" | "active" | "completed" | "failed" | "waiting-children" | "unknown";
|
|
131
159
|
/** A point-in-time view of a job. */
|
|
132
|
-
type JobSnapshot<TPayload = unknown, TResult = unknown> = {
|
|
160
|
+
export type JobSnapshot<TPayload = unknown, TResult = unknown> = {
|
|
133
161
|
id: string;
|
|
134
162
|
name: string;
|
|
135
163
|
queue: string;
|
|
@@ -143,7 +171,7 @@ type JobSnapshot<TPayload = unknown, TResult = unknown> = {
|
|
|
143
171
|
finishedAt?: Date;
|
|
144
172
|
};
|
|
145
173
|
/** A failed job, with the means to retry it. */
|
|
146
|
-
type FailedJob<TPayload = unknown> = {
|
|
174
|
+
export type FailedJob<TPayload = unknown> = {
|
|
147
175
|
id: string;
|
|
148
176
|
name: string;
|
|
149
177
|
queue: string;
|
|
@@ -151,18 +179,20 @@ type FailedJob<TPayload = unknown> = {
|
|
|
151
179
|
attemptsMade: number;
|
|
152
180
|
failedReason: string;
|
|
153
181
|
stacktrace: string[];
|
|
154
|
-
failedAt?: Date;
|
|
182
|
+
failedAt?: Date;
|
|
183
|
+
/** Move the job back to waiting so a worker picks it up again. */
|
|
155
184
|
retry(): Promise<void>;
|
|
156
185
|
};
|
|
157
186
|
/**
|
|
158
187
|
* A job returned by `defineJob`.
|
|
159
188
|
*/
|
|
160
|
-
type QueueJob<TPayload, TResult = unknown> = {
|
|
189
|
+
export type QueueJob<TPayload, TResult = unknown> = {
|
|
161
190
|
readonly name: string;
|
|
162
|
-
readonly queue: string;
|
|
163
|
-
|
|
191
|
+
readonly queue: string;
|
|
192
|
+
/** Enqueue the job. */
|
|
193
|
+
dispatch(payload: TPayload, options?: DispatchOptions): Promise<DispatchedJob>;
|
|
194
|
+
/** Read a job of this type by id; `undefined` when it no longer exists. */
|
|
164
195
|
find(id: string): Promise<JobSnapshot<TPayload, TResult> | undefined>;
|
|
165
196
|
};
|
|
166
197
|
//#endregion
|
|
167
|
-
export { DispatchOptions, DispatchedJob, Duration, DurationUnit, FailedJob, JobBackoff, JobContext, JobDefinition, JobOptions, JobProgress, JobRetention, JobSnapshot, JobState, QueueConfig, QueueDashboardConfig, QueueJob, QueueWorkersConfig };
|
|
168
198
|
//# sourceMappingURL=types.d.mts.map
|
package/esm/types.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/types.ts"],"mappings":";;;;;
|
|
1
|
+
{"version":3,"file":"types.d.mts","names":[],"sources":["../../../../../../../packages/queue/src/types.ts"],"mappings":";;;;YAIY;;;;;YAMA,gCAAgC;;;;;YAMhC;;EAIN;;EAEA;;;;;;YAOM;;;;;YAMA;;EAEV;;EAEA,UAAU;;EAEV,mBAAmB;;EAEnB,eAAe;;;;;YAML;;;;;EAKV;;EAEA;;;;;EAKA;;;;;;;YAQU;;EAEV;;EAEA;;;;;;EAMA,aAAa;;;;;YAMH;;;;;EAKV,YAAY;;EAEZ;;EAEA;;EAEA,oBAAoB;;EAEpB,UAAU;;EAEV,YAAY;;;YAIF,uBAAuB;;;;YAKvB;;EAEV;;EAEA;;EAEA;;EAEA;;EAEA;;EAEA,SAAS,OAAO,cAAc;;EAE9B,IAAI,eAAe;;EAEnB;;;;;YAMU,cAAc,UAAU,WAAW;;EAE7C;;EAEA;;EAEA;;EAEA,OAAO,SAAS,UAAU,SAAS,aAAa,QAAQ,WAAW;;;;;YAMzD;;EAEV,QAAQ;;;;;EAKR;;;;;EAKA;;EAEA;;EAEA,UAAU;;;YAIA;EACV;EACA;EACA;;;YAIU;;YAWA,YAAY,oBAAoB;EAC1C;EACA;EACA;EACA,OAAO;EACP,SAAS;EACT,UAAU;EACV;EACA,SAAS;EACT;EACA,WAAW;EACX,aAAa;;;YAIH,UAAU;EACpB;EACA;EACA;EACA,SAAS;EACT;EACA;EACA;EACA,WAAW;;EAEX,SAAS;;;;;YAMC,SAAS,UAAU;WACpB;WACA;;EAET,SAAS,SAAS,UAAU,UAAU,kBAAkB,QAAQ;;EAEhE,KAAK,aAAa,QAAQ,YAAY,UAAU"}
|
package/package.json
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
"peerDependencies": {
|
|
12
12
|
"@bull-board/api": "^9.0.0",
|
|
13
13
|
"@bull-board/fastify": "^9.0.0",
|
|
14
|
-
"@warlock.js/core": "5.
|
|
15
|
-
"@warlock.js/logger": "5.
|
|
14
|
+
"@warlock.js/core": "5.32.0",
|
|
15
|
+
"@warlock.js/logger": "5.32.0"
|
|
16
16
|
},
|
|
17
17
|
"peerDependenciesMeta": {
|
|
18
18
|
"@bull-board/api": {
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
],
|
|
39
39
|
"author": "hassanzohdy",
|
|
40
40
|
"license": "MIT",
|
|
41
|
-
"version": "5.
|
|
41
|
+
"version": "5.32.0",
|
|
42
42
|
"main": "./cjs/index.cjs",
|
|
43
43
|
"module": "./esm/index.mjs",
|
|
44
44
|
"types": "./esm/index.d.mts",
|