v1.0.0
First public release. The library is feature-complete for the surface described
in docs/technical_specification.md, holds 100% line/branch/function/statement
coverage, and ships with zero runtime dependencies.
Added
BymaxQueueModule.forRoot()and.forRootAsync()dynamic module (built on
ConfigurableModuleBuilder;isGlobalmapped toDynamicModule.globalviasetExtras)QueueServicewith typedenqueue(nativededuplicationoptions),enqueueBulk,
getJob,getJobs,getMetrics,pauseQueue/resumeQueue/cleanQueue, and Job
SchedulersupsertJobScheduler/removeJobScheduler/getJobSchedulers@Processor,@Process,@OnWorkerEvent(worker-local, fullJob),@OnQueueEvent
(global) decorators plus automatic discovery viaDiscoveryService;job.updateProgress()
and progress event supportWorkerRegistryprogrammatic API, includingregisterSandboxedfor file-based
out-of-process processorsFlowService(opt-in viaoptions.flows.enabled)MetricsServicewith TTL cache (opt-in viaoptions.metrics.enabled)- Optional OpenTelemetry
telemetrypassthrough (viabullmq-otel, an optional peer dep) QueueLifecyclebounded graceful-shutdown protocol (bounded drain viaPromise.race+
worker.close(true), optional drain, Redis disconnect on Mode B); at-least-once semantics
documented- Dual-mode connection (Mode A: bring-your-own ioredis / Mode B: lib-owned), with per-role
maxRetriesPerRequestpolicy applied automatically - Subpaths:
.(server),./shared(zero-dep types and constants) 'error'as a subscribable@OnQueueEventname. It carries a realError
instance rather than the serialized payload every other queue event delivers,
because it reports a connection fault rather than a job transition- Peer deps:
@nestjs/common ^11,@nestjs/core ^11,bullmq ^5.16,ioredis ^5,
reflect-metadata ^0.2; optionalbullmq-otel ^1 - E2E tests with Testcontainers Redis
Fixed
-
The configured
prefixnow reaches every BullMQ object. Only the producer
Queuereceivedoptions.prefix; theWorker,QueueEventsandFlowProducer
were constructed without it and fell back to BullMQ's defaultbullprefix. With
any non-default prefix, producers and consumers diverged onto separate keyspaces —
workers never consumed jobs, event listeners never fired, and flow jobs were
enqueued where no worker polled. Nothing threw. -
Explicit
@Injecton every injectable constructor parameter. The package
ships as a tsup/esbuild bundle, and esbuild does not emit
emitDecoratorMetadata. Providers resolved purely by reflected type metadata
(QueueService,WorkerRegistry,QueueEventsRegistry,
ProcessorDiscoveryService,QueueLifecycle) could not be instantiated from
the builtdistby a consumer's Nest container. -
The
exportsmap hands CommonJS consumers CommonJS declarations. With
"type": "module", TypeScript reads a bare.d.tsas ESM, so arequire()
consumer landed on ESM declarations.typesis now declared per condition,
main/module/typesare present for legacy resolution,./package.jsonis
exported, and the package passesattwat the strict profile. -
Every BullMQ emitter the library creates carries a fallback
errorlistener.
Queue,Worker,QueueEventsandFlowProducerextend Node'sEventEmitter,
where emitting'error'with no listener throws rather than being delivered.
The library constructs all four on the consumer's behalf, so a transient Redis
fault surfaced as an uncaught exception in an application that never asked for
the emitter. The fallback logs and does not consume the event — an
@OnWorkerEvent('error')or@OnQueueEvent('error')handler still runs. -
One owner for shutdown.
QueueService,FlowServiceandConnectionResolver
each exposedonModuleDestroy, and NestJS binds lifecycle hooks by method name,
so Nest invoked them on its own schedule whileQueueLifecyclealso called two
of them inside its ordered sequence: the flow producer closed twice, and queues
could close before the bounded drain finished with them. They now expose plain
methods —QueueService.closeAll(),FlowService.close(),
ConnectionResolver.teardown()— andQueueLifecycleis the only lifecycle hook. -
Operational failures no longer masquerade as a bad cron.
upsertJobScheduler
wrapped every failure inINVALID_REPEAT_OPTIONSwith HTTP 400 and "pattern must
be a valid cron expression", so an unreachable Redis was reported as a client
mistake — with a status telling the caller not to retry. Faults carrying a string
codenow propagate untouched. -
A malformed
repeatraises the documented error instead of aTypeError.
The union forbids it for a TypeScript caller, but plain JavaScript and any
payload cast fromunknownreached theinoperator unchecked.null,
undefined, strings, numbers and arrays are rejected as
INVALID_REPEAT_OPTIONS, as is a blankpattern. -
QueueEventsRegistry.getConnections()returns a copy.ReadonlyMapis a
compile-time claim only; the live registry could be cast and mutated.
Security
- Release pipeline gated behind a manual-approval environment, with SHA-pinned
actions, tag/version verification, and OIDC provenance from the second release
onward.
BullMQ version policy
1.x — floor bullmq ^5.16.0
The 1.x series floors peerDependencies.bullmq at ^5.16.0, the release that
introduced the Job Schedulers API (upsertJobScheduler/removeJobScheduler/getJobSchedulers).
Current recommended peer: 5.79.1.
Forward compatibility with BullMQ v6
This library is forward-compatible with BullMQ v6 by design. It exclusively uses
Job Schedulers for recurring jobs and never calls the removed addRepeatable/removeRepeatable
API, so no public-API break is expected on promotion to v6.
Promotion trigger. When the E2E suite is green on both the v5 and v6 matrix, the peer
range will be widened to ^5.16.0 || ^6.0.0 in a minor release. No adapter is needed because
the recurring-jobs surface is unchanged.
Fallback. If another API used by the library breaks in BullMQ v6 without a trivial adapter,
a parallel branch will keep the 1.x line on ^5.16 only, and a 2.x series will track the
v6 peer range.