Skip to content

chore(backend): take the bullmq, jwks-rsa and escape-string-regexp majors - #2188

Merged
ankit-yc merged 1 commit into
devfrom
chore/dependency-majors
Aug 14, 2026
Merged

chore(backend): take the bullmq, jwks-rsa and escape-string-regexp majors#2188
ankit-yc merged 1 commit into
devfrom
chore/dependency-majors

Conversation

@ankit-yc

Copy link
Copy Markdown
Contributor

PR Checklist

What is the current behavior?

Three dependabot major bumps have been open and red because each needs a code change to land: bullmq #2182, escape-string-regexp #2119 and jwks-rsa #2118. They all touch apps/backend and all touch pnpm-lock.yaml, so merging them one at a time put each one into conflict with the other two. They are taken together here so there is one lockfile resolution and one validation run.

The behaviour being corrected in each case:

  • bullmq 5 to 6 removed repeat from JobsOptions. It is now dropped silently rather than rejected, so the seven recurring registrations in apps/backend/src/queues would have stopped firing on upgrade with nothing logged and nothing thrown.
  • escape-string-regexp 5 is ESM only. This backend compiles to CommonJS, so requireing it throws at runtime.
  • jwks-rsa 4 pulls in jose 6, which is ESM only, so Jest could not load it.

What is the new behavior?

bullmq. All seven recurring registrations moved from Queue.add(..., { repeat, jobId }) to Queue.upsertJobScheduler(id, { every }, { name, data }). Every scheduler id and every interval is unchanged, so a deploy re-uses the existing schedulers rather than stacking duplicates alongside them.

Five of the six scheduler modules had no test at all. They have one now: apps/backend/test/queues/schedulers.test.ts covers each registration's id, cadence and job name, asserts that recurrence no longer goes through Queue.add, asserts ids are stable across a restart, and asserts that all six recurring jobs across the app hold distinct ids.

escape-string-regexp. The dependency is removed in favour of apps/backend/src/utils/escape-regexp.ts. Pinning to the last CommonJS release would leave the same wall in front of every future bump, and the implementation is two replacements against a fixed specification. Behaviour matches 5.0.0 exactly, including the \x2d form for a hyphen: a plain backslash-hyphen is valid in most patterns but rejected by the stricter grammar Unicode-mode patterns use, so the numeric escape is the form that is always safe. The test asserts against an inlined copy of the 5.0.0 implementation rather than against a restatement of the new one.

jwks-rsa. apps/backend/jest.config.cjs now transforms jose through ts-jest with allowJs, and narrows transformIgnorePatterns to just that package rather than opening up node_modules generally.

Impact area

apps/backend and packages/auth. No API surface, route, schema or migration changes. The queue work is a same-behaviour port to the new bullmq API.

Validation performed

Run locally on this branch, on Node 20:

  • pnpm run type-check - 17/17 tasks pass
  • pnpm run lint - 10/10 tasks pass
  • pnpm run test:scripts - 71 pass, 0 fail
  • apps/backend full suite - 255 suites, 4848 tests, all passing

Coverage on everything this PR adds or changes:

File Stmts Branch Funcs Lines
appointment.scheduler.ts 100 100 100 100
idexx-reference.scheduler.ts 100 100 100 100
lab-results.scheduler.ts 100 100 100 100
lab-status.scheduler.ts 100 100 100 100
task-schedule.scheduler.ts 100 100 100 100
task.schedulers.ts 100 100 100 100
utils/escape-regexp.ts 100 100 100 100

The new tests were mutation tested rather than just run, since a test that passes against correct code proves nothing on its own. Each of these was applied to the source and the suite confirmed to go red, then reverted and confirmed green again:

  1. Revert lab-status.scheduler.ts to the old repeat-on-add API - 3 tests fail
  2. Change the task reminder interval from 60s to 30s - 1 test fails
  3. Give lab-results the same scheduler id as lab-status - 2 tests fail
  4. Give the task reminder the same id as task recurrence - 2 tests fail

Related Issue(s)

Supersedes and closes #2182, #2119 and #2118.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

…jors

Three dependabot major bumps that each needed a code change to land, taken
together because they all touch apps/backend and each one on its own branch
re-conflicted the others on pnpm-lock.yaml.

bullmq 5 to 6 removed `repeat` from JobsOptions. It is ignored rather than
rejected, so the seven recurring registrations would have gone quiet without
any error. All seven moved to Queue.upsertJobScheduler, keeping their existing
ids and intervals so a deploy does not stack duplicates. The five schedulers
that had no test now have one.

escape-string-regexp went ESM only at v5 and this backend compiles to
CommonJS, so requiring it throws at runtime. The dependency is replaced by a
local helper matching 5.0.0 exactly, including the \x2d form for a hyphen that
Unicode-mode patterns require.

jwks-rsa 3 to 4 pulls jose 6, which is ESM only. Jest now transforms it.
@ankit-yc
ankit-yc force-pushed the chore/dependency-majors branch from aa87ce9 to a683bf2 Compare August 14, 2026 22:58
@sonarqubecloud

Copy link
Copy Markdown

@sonarqubecloud

Copy link
Copy Markdown

@sonarqubecloud

Copy link
Copy Markdown

@sonarqubecloud

Copy link
Copy Markdown

@ankit-yc
ankit-yc merged commit 9d357fc into dev Aug 14, 2026
62 checks passed
@ankit-yc
ankit-yc deleted the chore/dependency-majors branch August 14, 2026 23:28
ankit-yc pushed a commit that referenced this pull request Aug 15, 2026
…ix search escaping

Three defects from the bullmq/jwks-rsa/escape-string-regexp majors (#2188).
None reached production: only the frontend redeployed on that commit.

bullmq 6 moved ioredis from a direct dependency to an OPTIONAL peer, and
pnpm's auto-install-peers does not install optional peers, so the driver
dropped out of the lockfile entirely. bull.config.ts passes a plain options
object as `connection`, so bullmq has to require('ioredis') itself and threw
"BullMQ could not load the optional 'ioredis' package". initQueues is awaited
before app.listen inside a catch that exits, so this was a whole-backend
outage on next deploy. ioredis is now a direct dependency, pinned to the
5.11.1 line main already ran.

The scheduler ids carried across the migration unchanged, but that was the
wrong thing to check: bullmq 5 never keyed a repeatable by jobId, it keyed by
the md5 of name:jobId:endDate:tz:every. upsertJobScheduler keys by the plain
id, so the upsert added a second entry beside the old one in the same repeat
zset, and bullmq 6 still schedules from the legacy shape. Every recurring job
would have fired twice, indefinitely: duplicate no-show marking, duplicate
task reminders to users, doubled lab polling. initQueues now prunes the md5
entries before upserting.

The escape helper was replaced by a LIKE escape. Both call sites feed the
value into Prisma `contains`, which is an ILIKE pattern and not a regular
expression, so regex escaping was always wrong. It only looked correct
because a backslash before an ordinary character is a no-op in LIKE. Emitting
\x2d for a hyphen broke that: "Jean-Luc" searched for "Jeanx2dLuc" and matched
nothing. Escaping backslash, percent and underscore instead makes the search
mean exactly what was typed, and stops user input acting as a wildcard.

The scheduler suites mock every queue module, so no real Queue is ever built
and they could not have caught the first two. redis-driver.test.ts checks the
dependency graph itself instead.
ankit-yc pushed a commit that referenced this pull request Aug 15, 2026
…ix search escaping

Three defects from the bullmq/jwks-rsa/escape-string-regexp majors (#2188).
None reached production: only the frontend redeployed on that commit.

bullmq 6 moved ioredis from a direct dependency to an OPTIONAL peer, and
pnpm's auto-install-peers does not install optional peers, so the driver
dropped out of the lockfile entirely. bull.config.ts passes a plain options
object as `connection`, so bullmq has to require('ioredis') itself and threw
"BullMQ could not load the optional 'ioredis' package". initQueues is awaited
before app.listen inside a catch that exits, so this was a whole-backend
outage on next deploy. ioredis is now a direct dependency, pinned to the
5.11.1 line main already ran.

The scheduler ids carried across the migration unchanged, but that was the
wrong thing to check: bullmq 5 never keyed a repeatable by jobId, it keyed by
the md5 of name:jobId:endDate:tz:every. upsertJobScheduler keys by the plain
id, so the upsert added a second entry beside the old one in the same repeat
zset, and bullmq 6 still schedules from the legacy shape. Every recurring job
would have fired twice, indefinitely: duplicate no-show marking, duplicate
task reminders to users, doubled lab polling. initQueues now prunes the md5
entries before upserting.

The escape helper was replaced by a LIKE escape. Both call sites feed the
value into Prisma `contains`, which is an ILIKE pattern and not a regular
expression, so regex escaping was always wrong. It only looked correct
because a backslash before an ordinary character is a no-op in LIKE. Emitting
\x2d for a hyphen broke that: "Jean-Luc" searched for "Jeanx2dLuc" and matched
nothing. Escaping backslash, percent and underscore instead makes the search
mean exactly what was typed, and stops user input acting as a wildcard.

The scheduler suites mock every queue module, so no real Queue is ever built
and they could not have caught the first two. redis-driver.test.ts checks the
dependency graph itself instead.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants