All versions since v0.11.0
v0.11.0
What’s Changed
Features
- Public in-process adapters.
createInProcessStateAdapterandcreateInProcessNotifyAdaptermoved fromqueuert/internalto the mainqueuertentry. The in-process state adapter was rewritten around per-type sorted sets and a journal-based rollback log. - New
queuert/conformancesubpath export. Framework-agnostic conformance runner (runStateAdapterConformance,runNotifyAdapterConformance,ConformanceError,ConformanceReport) with a built-inexpectshim, for users authoring custom adapters. Documented under Advanced → Custom Adapters. client.deleteJobChain(id, …)— new singular API, symmetric todeleteJobChains. Returns the deleted chain orundefinedif missing; throwsBlockerReferenceErrorif external jobs depend on it; supportscascade.client.triggerJobs(ids, …)— new plural API. Atomic validation: throwsJobNotFoundError/JobNotTriggerableErrorfor the first invalid job before any job is triggered; returns jobs in input order; empty input returns[].- Redis Cluster support (
@queuert/redis). SET+PUBLISH Lua script passes the pub/sub channel viaARGVinstead ofKEYS, avoidingCROSSSLOTrejection on Redis Cluster. New examples forioredisandnode-rediscluster clients. - Optional
withSavepointhook onPgStateProviderandSqliteStateProvider— drivers that track transaction state client-side (e.g. postgres.js) can override the defaultSAVEPOINT/RELEASE/ROLLBACK TOSQL path with native savepoint support. Job<…>gained aTOutputgeneric parameter — the completed variant now carriesoutput.- New guides and examples: Prioritization guide (with
showcase-multiworker-prioritizationexample) and Processing Reliability / error recovery guide (withshowcase-error-recoveryexample).
Breaking Changes
runInTransactionrenamed towithTransactiononStateAdapter,PgStateProvider,SqliteStateProvider.HookDefrenamed toHookDefinition.createPgNotifyAdapter/createRedisNotifyAdapter: optionproviderrenamed tonotifyProvider.createInProcessStateAdapterandcreateInProcessNotifyAdapterare now async and must beawait-ed.- In-process adapters moved from
queuert/internaltoqueuert. client.deleteJobChainno longer throwsJobChainNotFoundErrorfor missing chains — it returnsundefinedinstead.StateAdaptercontract:triggerJobreplaced bytriggerJobs(bucketedtriggered/notFound/notTriggerableresult);deleteJobChainsreturns{ deleted, blockerRefs }; the adapter enforces blocker validation in one all-or-nothing statement.executeSqlinPgStateProvider/SqliteStateProvidernow takes typed SQL hints (paramTypes,columnTypes) instead of a booleanreturnsflag. Custom providers must update accordingly.pgLiteralremoved from@queuert/postgresentry;sqliteLiteralremoved from@queuert/sqliteentry.
Packages
All packages released at 0.11.0: queuert, @queuert/postgres, @queuert/sqlite, @queuert/redis, @queuert/nats, @queuert/otel, @queuert/dashboard.
v0.12.0
What’s Changed
Features
- Onion-style attempt middleware. New
AttemptMiddlewareAPI withwrapHandler/wrapPrepare/wrapCompletehooks that wrap each phase of an attempt and inject typed context. Middleware now lives per-Processorsslice (createProcessors({ attemptMiddleware })) instead of running worker-wide. - Concurrent SELECT reads. New
AsyncRwLocklets pure-SELECTqueries run in parallel while writers stay serialized. End-to-end throughput improves on every adapter — for example, SQLite better-sqlite3 ~7,177 → ~8,396 jobs/s and Postgres pg notify ~1,536 → ~1,948 jobs/s. - Concurrent transaction-hook flush.
createTransactionHooksdispatchesflushanddiscardviaPromise.allSettledinstead of sequentially. Cross-hook ordering is no longer guaranteed; in-tree hooks are unaffected. - Cached prepared statements. Pass an optional
idtosql()and built-in providers cache the prepared statement (postgres.jsprepare: true, pgquery.name, SQLitedb.preparecache). Dynamic SQL still runs unprepared. - Adapter
close()lifecycle.NotifyAdapterandStateAdapternow expose an idempotentclose()for explicit teardown. - Validation-adapter conformance suite. New
runValidationAdapterConformancerunner exported fromqueuert/conformancefor validation-adapter authors. MirrorsrunStateAdapterConformancein shape. UnknownJobTypeError. Raised by mergedJobTypesslices when a referenced type name isn’t owned by any slice.
Breaking Changes
Slice-based registries
JobTypeRegistry/JobTypeProcessorRegistry→JobTypes/Processors.createClientandcreateInProcessWorkernow accept a single slice or areadonlyarray of slices and merge them inline.- Standalone merge helpers (
mergeJobTypeRegistries,mergeJobTypeProcessorRegistries) removed. createJobTypeRegistry→createJobTypes;defineJobTypeRegistry→defineJobTypes;DefineJobTypes→JobTypeDefs;JobAttemptMiddleware→AttemptMiddleware.
Worker option restructuring
pollIntervalMsis now a top-level option oncreateInProcessWorker.- Per-type
backoffConfig/leaseConfigdefaults move ontocreateProcessors. - The old worker-loop
backoffConfig(controlling crash recovery, not retries) is renamed torecoveryBackoffConfig. jobTypeProcessorDefaultsis gone.
Chain naming standardization
- Drop
JobfromJobChaineverywhere: types (Chain,ChainStatus,CompletedChain,ResolvedChain,ChainData,ChainNotFoundError), client methods (startChain(s),getChain,awaitChain,listChains,listChainJobs,completeChain,deleteChain(s)). StateAdaptergetters dropById:getJob,getChain,listChains,listChainJobs,deleteChains.NotifyAdapterevents:notifyChainCompleted,listenChainCompleted.ObservabilityAdapterevents:chainCreated,chainCompleted,chainDeleted,chainDuration.- Filter parameters:
excludeJobChainIds→excludeChainIds;jobChainId→chainId;jobChainTypeName→chainTypeName. - Log entry types and messages:
"chain_created"/"chain_completed"/"chain_deleted". - OTEL metric names renamed:
queuert.job_chain.{created,completed,deleted,duration}→queuert.chain.{...}. Update dashboards and alerts that reference the old names. Metric attributes (chainTypeName) are unchanged. - Schema migration
20260430000000_rename_chain_indexesruns automatically viamigrateToLatest(). Postgres usesALTER INDEX … RENAME TO; SQLite drops + recreates. Three indexes drop thejob_prefix.
Adapter lifecycle and wake hints
NotifyAdapterandStateAdapternow require an idempotentclose()that cascades into the underlying provider when one is owned. Afterclose(), notify/listen and most state methods reject.- Provider-level
close()is optional and only present on resource-owning providers (e.g.createPgPoolNotifyProvider). Pass-through providers over caller-owned pools/clients omit it. PgPoolNotifyProvidertype removed —createPgPoolNotifyProvidernow returns plainPgNotifyProvider.notifyJobScheduled(typeName)no longer takes a count. Wake-hint budgets move toprovideWakeHint(typeName, count)andconsumeWakeHint(typeName), keyed by type name and additive across concurrent publishers.- Application code using built-in adapters should explicitly close them before tearing down caller-owned pools/clients.
Concurrent reads
createAsyncLock/AsyncLock→createAsyncRwLock/AsyncRwLockwithacquireRead()/acquireWrite()returning aDisposable-compatibleLockHandle. Idiomatic usage:using h = await lock.acquireWrite();.SqliteStateProvider.executeSqlandPgStateProvider.executeSqlsignature changes:paramsis now required (was optional); new requiredparamTypes: Record<number, RuntimeType>andreadOnly: booleanfields.- Custom SQLite providers must consult
readOnlyto choose betweenacquireRead()andacquireWrite()— wiring everything to an exclusive lock silently disables concurrent reads. @queuert/typed-sql’ssql()now takesreadOnlyvia the options object instead of as a second positional argument.
Lock option replaces FOR UPDATE getters
getJobForUpdateandgetLatestChainJobForUpdateremoved.- Pass
lock: "exclusive"togetJob/getChaininstead. getChain({ lock: "exclusive" })now locks only the latest job in the chain (the rootJob if no continuation, otherwise the last continuation). The rootJob’schainTypeNameis immutable, so locking it earned nothing. Return shape unchanged.
Cached prepared statements (custom providers)
- Custom
PgStateProvider/SqliteStateProvidersee an optionalid?: stringonexecuteSql. They continue to type-check unchanged and execute every statement unprepared. To opt into caching, acceptidand use it as a cache key / pgquery.name/ postgres.jspreparetoggle. - PgBouncer / Supavisor users (transaction-mode poolers) should write a custom provider that ignores
id, since server-side prepared statements break under those proxies.
Smaller surface tweaks
RescheduleJobErrorno longer carriescause: undefinedwhen constructed without an explicit cause.'cause' in erris nowfalsewhen not supplied (matching every other queuert error). Value-level (err.cause) is stillundefined.BaseTxContextis now exported only fromqueuert. The duplicate re-export fromqueuert/internalis removed.@queuert/postgresnow imports it fromqueuertto match@queuert/sqlite.
TypeScript flags every wrong call site at compile time across all of these — there is no runtime fallback or deprecation period.
Bug Fixes
@queuert/postgres:addJobsBlockersSqlnow acquiresFOR UPDATEon the latest job of each blocker chain (via a newlocked_blocker_chain_latestCTE) before inserting blocker rows. Closes a race where a blocker chain could complete between the status read and the blocker insert and leave the blocked job stuck. No schema change.
Packages
All packages released at 0.12.0: queuert, @queuert/postgres, @queuert/sqlite, @queuert/redis, @queuert/nats, @queuert/otel, @queuert/dashboard.
v0.13.0
What’s Changed
Features
- Worker defaults:
createInProcessWorkeraccepts a newdefaults: { backoffConfig?, leaseConfig? }option for fleet-wide fallbacks. Resolution order is processor → registry → workerdefaults→ library default.InProcessWorkerDefaultsis exported from the package root. - Required cross-slice middleware: new
requiredAttemptMiddlewareoption oncreateInProcessWorkerenforces (at compile time against the merged slice middleware, and at runtime by reference identity) that every slice includes the listedAttemptMiddlewareinstances as an in-order subsequence. The worker does not execute them itself — this guarantees cross-cutting concerns (auth, tracing, logging) are wired into every slice without giving up the per-slice middleware model. - Multi-slice validation: validation adapters now accept either a single external slice or a
readonlyarray, restoring the multi-slice composition path that was removed alongsidemergeJobTypeRegistriesin 0.12. New publicJobTypesDefinitions<T>resolves either form to the mergedBaseJobTypeDefinitionsrecord. RuntimeTypere-export from@queuert/postgresand@queuert/sqliteso custom state-provider authors can type their serialization/parsing without reaching into@queuert/typed-sql.- New
state-sqlite-bunexample demonstrates@queuert/sqliterunning onbun:sqlite.
Breaking Changes
workerId→workerName:createInProcessWorker’s identity option is renamed toworkerName— an optional human-readable label restricted to/^[A-Za-z0-9._-]+$/. The runtime always appends a random UUID to produce the final worker id (${workerName}-${uuid}, or just${uuid}when omitted), making duplicate worker ids impossible to express. Observability events and error fields continue to expose the full id underworkerId.- Validation adapter signatures: adapters accept the “single slice or readonly array” shape
createClientalready takes.ExternalJobTypeDefinitionsis no longer exported — useJobTypeDefinitionsfor a single slice orJobTypesDefinitionsfor either form. Adapter authors implementingrunValidationAdapterConformancemust add abuildWithExternalSlicesfixture, andbuildWithExternalSlice’sblockerstightens from a readonly array to a tuple — schemas under test must switch fromarray(...)totuple([...])(z.tuple,v.tuple,Type.Tuple,type([...])). - Stricter public error fields: contextual fields on
JobNotFoundError,ChainNotFoundError,JobTakenByAnotherWorkerError,JobAlreadyCompletedError,JobNotTriggerableError, andWaitChainTimeoutError(jobId,chainTypeName, etc.) are now non-optionalstring/number, and their constructor options are required — caught errors no longer need null-checks.JobTakenByAnotherWorkerError.leasedByis nowstring | null(an omitted constructor option is normalized tonull); narrowings via=== undefinedshould switch to=== null. @queuert/otelattribute renames: every metric attribute key is renamed to OpenTelemetry semantic-convention style (lowercase, dotted) to align with the span attributes already emitted by the tracing layer. Metric names are unchanged, so queries still resolve — but dashboards, alerts, and recording rules that group by or filter on the old keys silently return empty results until updated.workerId→queuert.worker.id,typeName→queuert.job.type,chainTypeName→queuert.chain.type,continued→queuert.job.continued,operation→queuert.adapter.operation. Two value encodings also changed onqueuert.job.completed:queuert.job.continuedis now a real boolean (not"true"/"false"), andqueuert.worker.idis omitted for workerless completions (no longer the literal string"null").executeSql.iduniqueness: in@queuert/postgresand@queuert/sqlite,executeSql’sidnow uniquely identifies the resolved SQL within a provider — the template applier folds variants likeschema/tablePrefixinto a hashed suffix. The built-inpg.Poolprovider drops its SHA1 name-hash (query.name = id); built-inbetter-sqlite3andnode:sqliteproviders drop their per-database SQL→Statement maps and key the cache byid. Custom providers can do the same.
Bug Fixes
- Relax the generic bound on
AttemptMiddleware’sTStateAdapterfromStateAdapter<BaseTxContext, any>toStateAdapter<any, any>. The previous bound was unusable in practice: because the transaction-context type parameter appears in contravariant positions, no concrete adapter with a non-emptytxCtxcould satisfyStateAdapter<BaseTxContext = {}, any>. Adapter-aware middleware and generic test helpers now type-check withoutanyworkarounds.
Packages
All packages released at 0.13.0: queuert, @queuert/postgres, @queuert/sqlite, @queuert/redis, @queuert/nats, @queuert/otel, @queuert/dashboard.
v0.14.0
What’s Changed
Features
- Caller-supplied job IDs (breaking):
startChain,startChains, and the workercontinueWithcallback now accept an optionalidthat becomes the new chain root (or continuation) job ID. State adapters accept a newvalidateIdpredicate that runs on both adapter-generated and caller-supplied IDs; failures throw the newInvalidJobIdError. When deduplication fires, the existing row’s ID wins over a caller-suppliedid(the returned chain carriesdeduplicated: true). - Aligned adapter ID-generation options (breaking): both PostgreSQL and SQLite adapters now expose a
generateIdfunction. PostgreSQL’sidDefault(SQL expression) is removed in favor of JS-side generation; SQLite’sidGeneratoris renamed togenerateId. A new PostgreSQL migration dropsDEFAULT gen_random_uuid()from theidcolumn. Existing UUID generation behavior is unchanged for default configurations.
Bug Fixes
scheduled_atis now an honest eligibility floor: state adapters clampscheduled_attoMAX(requested, now())oncreateJobs,rescheduleJob, andunblockJobs. Previously, jobs with a pastscheduled_at(or blocked-since-creation jobs whose originalscheduled_atwent stale while they waited) would jump to the front of the acquisition queue ahead of jobs that genuinely became ready earlier.unblockJobsnow preserves a futurescheduled_atset at job creation (it previously reset tonow()unconditionally);triggerJobscontinues to re-anchor tonow()as an explicit reset. Applies to PostgreSQL, SQLite, and in-process adapters; no schema migration or backfill required.lastAttemptErrorcleared on success: when a job completes successfully after a previously failed attempt,completeJobnow resetslast_attempt_errortoNULLalongside the existing status/output/lease updates. Completed rows no longer carry over an error string from a prior failed attempt.requiredAttemptMiddlewaretype fix: a spurious type error fromcreateInProcessWorker’srequiredAttemptMiddlewarecheck is fixed when middleware is typed against a user-suppliedStateAdapteralias (e.g.AttemptMiddleware<MyStateAdapter>). Valid processor slices were being flagged as missing required middleware. Runtime behavior was unaffected; this is a type-only fix.BaseTxContexttightened: changed from{}toRecord<string, unknown>. The previous{}constraint accepted any non-nullish value (string, number, function), letting customStateAdapterauthors pick a non-objectTTxContextwithout a type error. All built-in adapters are already object-shaped, so no runtime behavior changes.
Migration Notes
- PostgreSQL: replace
idDefault: "gen_random_uuid()"withgenerateId: () => crypto.randomUUID()(this is the default and can be omitted). - PostgreSQL: replace
idDefault: "'job.' || gen_random_uuid()::text"withgenerateId: () => `job.${crypto.randomUUID()}`. - SQLite: rename
idGeneratoroption togenerateId. - Custom state adapter implementations must accept an optional
idper entry increateJobsand apply their ownvalidateIdpredicate.
Packages
All packages released at 0.14.0: queuert, @queuert/postgres, @queuert/sqlite, @queuert/redis, @queuert/nats, @queuert/otel, @queuert/dashboard.
v0.14.1
What’s Changed
Improvements
- Adapter conformance suite:
StateConformanceFixturenow propagatesgenerateIdandgenerateInvalidIdthrough to the cases. Previously the runner only forwardedstateAdapterandpoisonTransaction, so adapters configured with a customvalidateIdcould not exercise the caller-suppliedidpath in conformance tests.
Breaking Changes (testing helpers only)
- Removed
StateAdapterConformanceContextandNotifyAdapterConformanceContexttypes. Callers writing conformance suites for external adapters (e.g.it.extend<NotifyAdapterConformanceContext>(...)in vitest specs) should switch toStateConformanceFixture/NotifyConformanceFixture. No runtime API changes — this only affects adapter authors using the conformance harness.
Packages
All packages released at 0.14.1: queuert, @queuert/postgres, @queuert/sqlite, @queuert/redis, @queuert/nats, @queuert/otel, @queuert/dashboard.
v0.15.0
What’s Changed
Breaking Changes
triggerJob/triggerJobsrenamed torescheduleJob/rescheduleJobswith an optionalscheduleparameter ({ at: Date } | { afterMs: number }). Omitting it reschedules to now (previous behavior). A past time clamps to now. Onlypendingjobs are reschedulable. Batch validation is atomic.- Error classes renamed:
JobNotTriggerableError/JobsNotTriggerableError→JobNotReschedulableError/JobsNotReschedulableError. New batch error variantsJobsNotFoundErrorandJobsNotReschedulableErrorforrescheduleJobs. Thestatusproperty removed from the singular error; the batch variant exposesjobIds. - Observability event renamed:
jobTriggered→jobRescheduled. Now also emitted on worker retry (not just client reschedules), making it the single signal for “scheduledAt changed.”jobAttemptFailedis now a pure failure event —rescheduledAt/rescheduledAfterMsremoved.scheduleAfterMsdropped fromjobCreatedandjobRescheduledevents. StateProvidergains requiredtransactionConcurrencyfield ("concurrent" | "serialized"). Custom provider authors must declare it.- Dashboard:
POST /jobs/:id/triggerrenamed to/reschedule; “Trigger now” button renamed to “Reschedule”. - OTEL: Counter
queuert.job.triggered→queuert.job.rescheduled; log entryjob_triggered→job_rescheduled.
Features
- Blocker chain cap: A job can declare at most 100 blocker chains.
startChain,startChains, andcontinueWiththrow the newBlockerLimitExceededErrorwhen exceeded. - Dashboard UX improvements: Infinite scroll on list views (chains, jobs, chain detail), real links on cards (right-click/Cmd-click to open in new tab), and smart back navigation that preserves filters.
Bug Fixes
createProcessorstype resolution: Previously resolved job type names from the local slice only; now resolves from all registered slices."incomplete"dedup scope: Multi-step chains that continued past the root job are now correctly matched. Previously, once the root completed, the chain was no longer matched even though it was still running.- Concurrent unblock race (
@queuert/postgres,@queuert/sqlite): Row-level locks on blocked jobs duringunblockJobsprevent a race where concurrent blocker completions could leave dependent jobs stranded inblockedstatus.job_blockerPK widened to support duplicate chain IDs.
Performance
- Autovacuum tuning (
@queuert/postgres): New migration pinsautovacuum_vacuum_threshold = 5000/scale_factor = 0onjobandjob_blockertables, replacing size-proportional0.02. Gives predictable vacuum cadence regardless of table size.
Packages
All packages released at 0.15.0: queuert, @queuert/postgres, @queuert/sqlite, @queuert/redis, @queuert/nats, @queuert/otel, @queuert/dashboard.
v0.15.1
What’s Changed
Bug Fixes
- Saturated workers no longer busy-loop. A worker whose slots were all busy kept polling
getNextJobAvailableInMsas fast as the event loop allowed: with a due job in the backlog the poll reported0ms, so the wait returned immediately and the worker re-entered the loop without being able to take the work. Saturated in-process workers now wait for a slot to free (or forpollIntervalMs) instead of polling.
Performance
- Postgres job acquisition now uses the acquisition index.
acquireJobandgetNextJobAvailableInMsmatched jobs withtype_name IN (...)ordered byscheduled_at, which Postgres cannot satisfy from the(type_name, scheduled_at)index for more than one job type — it fell back to scanning and sorting the entire pending backlog on every acquisition and every poll. Both queries are now per-job-typeLATERALlookups, turning a backlog scan into one index lookup per job type. Workers polling a large Postgres backlog should see a substantial drop in database load. - On Postgres, when several job types are polled together, acquisition now picks a job type at random and takes its oldest due job rather than always taking the globally oldest job. This keeps a backlogged job type from starving the others.
- Postgres
getNextJobAvailableInMsno longer takes row locks on jobs scheduled in the future.
Packages
All packages released at 0.15.1: queuert, @queuert/postgres, @queuert/sqlite, @queuert/redis, @queuert/nats, @queuert/otel, @queuert/dashboard.
v0.16.0 Latest
What’s Changed
Queuert 0.16 rebuilds the job model around the chain’s head row and reworks attempt handling around a single finish outcome API, with a one-step schema migration from v0.15.1. It also brings faster listing queries, a redesigned dashboard, and a leaner, more consistent public API. This release contains many breaking changes — review the notes below before upgrading.
Breaking Changes
- New job model — a chain’s head row is now the chain itself.
ChainStatusis justrunning|completed; completed jobs carrycontinuedToId; running jobs exposeattemptAt/attemptBy/attemptUntil. “Lease” is renamed to “attempt” throughout (leaseConfig→attemptConfigwithtimeoutMs/heartbeatMs,LeaseConfig→AttemptConfig, plus matching log events and metrics). - Attempt finalization — handlers end an attempt with
complete(async ({ finish }) => finish({ output | continueWith | reschedule })).rescheduleJob()andRescheduleJobErrorare removed; rescheduling is now a non-error outcome.completeChain’scompleteoption is renamed tohandler. - Renames —
startChain/startChains→createChain/createChains. - Listing API reshaped — flat options with a single
status, requiredtypeName, status-dependentorderBychecked at compile time,root→independent; thechainId/jobId/chainTypeNamefilters are removed;CreatedAtCursor→TimestampWithIdCursor. - Deduplication —
scopeis now required and"incomplete"is renamed to"running"(missing or unknown scopes throwTypeError).windowMsandexcludeChainIdsare removed. - Caller-supplied
idcollisions now throw instead of being silently ignored. cascaderemoved fromdeleteChain/deleteChains.TransactionContextRequiredErrorremoved — a plainErroris thrown instead.wrapHandlernow wraps the whole attempt, so acatcharoundnext()observes handler failures.- Public type exports pruned — internal helper types are no longer exported;
CompletedJobandAttemptConfigare newly exported.
Database Migrations (@queuert/postgres, @queuert/sqlite)
- All migrations are collapsed into
001_initial_schema. You must be on v0.15.1 before upgrading. - PostgreSQL: the upgrade copies tables while v0.15.1 workers keep running; a
migration_locktable makes concurrentmigrateToLatest()calls safe. - SQLite: stop all workers and clients before running the migration.
vacuum()is removed, along with SQLite’scheckForeignKeys/checkAutoVacuumoptions.
Features
steptransaction primitive inside attempt handlers, with a matchingwrapStepmiddleware hook.- Batched lookups —
getChains/getJobs, withChainTypeMismatchError. - Type names & counts —
listChainTypeNames/listJobTypeNamesandcountByChainTypeNames/countByJobTypeNames. - Worker shutdown signal — in-flight handlers receive
"worker_stopping"as the abort reason when the worker stops.
Dashboard
- Full UI redesign: Overview page, Find by ID (⌘K), status tabs, and themes.
- New endpoints for type names, counts, and by-ID lookups. Malformed IDs and unknown
/apipaths return 404; uncaught errors return 500 with an{ error }body.
Observability (@queuert/otel)
- Attempt spans record an
abortevent;prepare/step/completespans record exceptions andERRORstatus. - Breaking: blocker-completion span renamed
resolve chain.*→complete chain.*;queuert.attempt.resultattribute removed (read the outcome from span status); metric names follow the lease → attempt rename. @opentelemetry/apipeer dependency now also accepts^1.0.0.
Performance & Fixes
- Faster listing queries via per-type anchoring.
- PostgreSQL and SQLite skip the redundant
RELEASE SAVEPOINTround-trip. - Type checking is roughly 20–40% cheaper.
AttemptMiddlewaretyped against a concrete adapter now works.chain.inputnow correctly reflects the entry job’s input.- Fixed a race where a job created with blockers could start
pendinginstead ofblocked(#4).
Packages
All packages released at 0.16.0: queuert, @queuert/postgres, @queuert/sqlite, @queuert/redis, @queuert/nats, @queuert/otel, @queuert/dashboard.