Commit Graph
7131 Commits
Author SHA1 Message Date
Marc-André Bombeck 730e9e7015 fix(doctor-report): take the day path for dense heart-rate variability and blood oxygen
The report's dense exception covered pulse and glucose. Heart-rate
variability and blood oxygen were read raw for any window, and a watch
or ring writing them every minute puts six figures of readings into a
long report that draws them as one sparkline.

Their report sections are a statistics row and a series, the same as
pulse, so they take the same rule: beyond 90 days per local day, and
inside 90 days per local day once the window holds more than 10 000 of
their readings. One grouped count decides each type on its own. Count,
minimum, maximum and the latest reading stay exact; the mean is the
readings' mean. Glucose keeps its own rule because its clinical panel
needs the per-reading counts.

The bucket query now takes a list of types instead of two flags.
2026-10-01 08:56:19 +02:00
Marc-André Bombeck e7fdcc208e test(targets): pin the route over the folded glucose and heart-rate reads
The route test mocked Prisma and asserted a raw glucose findMany. The
glucose and resting-pulse inputs now come from their SQL readers, so the
test mocks those, feeds the pinned response through the in-memory fold,
and asserts that neither stream is read as rows.
2026-10-01 08:54:20 +02:00
Marc-André Bombeck b2c484e25e fix(series): bucket heart-rate variability and blood oxygen like pulse
The dense rule in /api/measurements/series covered pulse and glucose
only. Heart-rate variability and blood oxygen read every raw row for
any window up to 3650 days, and a watch or ring that writes them every
minute makes that the same unbounded read pulse had.

Both kinds now fold per local day beyond 90 days and per local hour
inside 90 days once the window holds more than 10 000 readings. A
bucket keeps the point shape of its kind: only pulse carries
valueMin / valueMax. Statistics stay computed over every reading, and
a sparse series inside 90 days is unchanged. The OpenAPI description
names the four kinds.
2026-10-01 08:52:10 +02:00
Marc-André Bombeck 8949606de9 fix(cycle): read the insights crosstab as per-source day aggregates
GET /api/cycle/insights read a year of raw readings for its outcome
channels, glucose and heart-rate variability included. On a sensor
account that is over 100 000 objects per request.

The crosstab uses each local day's sum and count after picking one
source and device per day, so the route now reads readSourceDayAggregates
like the coach's cycle snapshot does, and each row carries its reading
count into metricDayMap.

The integration test runs the route over the fold and over every raw
reading handed in as a one-reading group, and compares the answers.
2026-10-01 08:50:31 +02:00
Marc-André Bombeck 7f2349e56f fix(targets): fold the glucose year and the heart-rate month in SQL
The targets page read a year of raw glucose readings to keep the newest
one per meal context, and thirty days of raw heart rate to derive one
resting figure per day. A sensor reporting every five minutes puts
105 000 glucose readings in that year and a watch recording once a
minute puts 43 000 heart-rate readings in the month, and the page is
read on every visit from the web and the app.

Glucose now reads one row per context (DISTINCT ON) for the newest
value and one row per context and local day for the thirty-day mean
and the consistency strip; the strip takes day sums directly through
rollupConsistencyFromDays. Heart rate reaches the resting-pulse card
through readRestingPulseProxy, the SQL fold the insights page already
uses, merged per day with the native resting rows exactly as before.

The cards are unchanged: the integration test rebuilds them from the
raw readings through the in-memory folds and compares.
2026-10-01 08:47:18 +02:00
Marc-André Bombeck 1b694ec222 Merge pull request #1068 from MBombeck/release/v1.39.6
Release v1.39.6
v1.39.6
2026-10-01 01:15:33 +02:00
Marc-André Bombeck 8ea9322c8e docs(changelog): say which charts the 1.39.5 pulse buckets reach 2026-10-01 00:50:18 +02:00
Marc-André Bombeck 3e4f431373 chore(release): v1.39.6 — a critical framework fix, a workout token, a lab scan that keeps every reading, and an instant restore preview 2026-10-01 00:48:46 +02:00
Marc-André Bombeck 3d6b052a05 Merge pull request #1058 from BernhardBuckel/fix/ocr-review-date-validation
fix(labs): block a lab-scan save while a selected reading is incomplete
2026-10-01 00:43:50 +02:00
Marc-André Bombeck 02be4d3dff Merge branch 'main' into fix/ocr-review-date-validation 2026-10-01 00:20:48 +02:00
Marc-André Bombeck c777e9e9e4 Merge pull request #1065 from MBombeck/fix/v1396-small-fixes
Quick-add follows intakeActionable; hourly buckets for dense CGM inside 90 days
2026-09-30 23:57:44 +02:00
Marc-André Bombeck 20752fb4bb Merge pull request #1066 from MBombeck/fix/backfill-lane-expiry
Keep a full-history import from running beside its own retry; tombstone purge steps around a restore
2026-09-30 23:46:16 +02:00
Marc-André Bombeck 85d11bc364 Merge branch 'main' into fix/v1396-small-fixes 2026-09-30 23:27:25 +02:00
Marc-André Bombeck a5d19bfe98 Merge pull request #1064 from MBombeck/fix/backup-summary-manifest
Answer the restore preview from counts kept with the backup
2026-09-30 23:25:26 +02:00
Marc-André Bombeck 3f6144a5b9 Merge pull request #1062 from MBombeck/feat/workouts-write-scope
Mint a workouts:write token for headless workout ingest
2026-09-30 23:25:09 +02:00
Marc-André Bombeck 70ce666d00 Merge pull request #1063 from MBombeck/fix/dev-deps-advisories
Raise the dev-only grpc-js, brace-expansion and undici 7 floors
2026-09-30 23:15:59 +02:00
Marc-André Bombeck a0c290e140 fix(jobs): let the nightly tombstone purge step around a running restore
A restore clears every row of the account, tombstones included, inside one
long transaction under the account's exclusive restore lock. The nightly
purge deleted expired tombstones across accounts, one statement per batch,
without that lock: on an account under restore it waited on the restore's
row locks for as long as the restore ran, past its statement timeout, or
deadlocked with it once each side held rows the other needed.

Each batch is now deleted one account at a time, in a transaction that
first takes the restore lock in shared mode, the same order the
consolidation passes keep. An account under restore is refused at once,
kept out of the rest of the run, and purged the next night. Still one
delete per account per batch, never one per row. The job reports how many
accounts it deferred.

The integration test holds a stand-in restore on one account while the
purge runs: the purge returns without waiting, leaves that account alone
and purges the other. With the lock call removed it waits out the hold.

Refs #1031
2026-09-30 23:11:03 +02:00
Marc-André Bombeck 029d6d881c fix(jobs): keep a full-history import from running beside its own retry
The shared admission queue sent its jobs without an expiry, so every one
took pg-boss's fifteen minutes. Against pg-boss 12.34, a handler that runs
past its expiry is not stopped: the job is failed, its abort signal fires,
the worker and group slots are freed, and the retry of the same job starts
in the same worker beside the run it replaced. A per-minute heart-rate
history is thousands of pages, so on a slow link one import turned into up
to four copies of itself, on the one queue meant to run a single import at
a time.

- The admission job now carries a sixteen-hour expiry, sized for the
  longest legitimate import and under pg-boss's 24-hour ceiling.
- Each run holds a session advisory lock on its identity (kind, provider,
  account) on a connection of its own. A delivery that finds it taken does
  nothing and says so in the log; the running import stamps its marker or
  is offered again at the next boot. Ending the connection or the process
  releases the lock.
- The Google Health backfill runs under the job's budget and abort signal.
  The page walk asks before each request, stops there, and reports the run
  truncated, so it throws without the completion stamp and is retried.
- The drain moves out of the registrar into its own module so it can be
  driven against a real pg-boss.

The integration test pins the pg-boss behaviour and runs the lane's
handler with a two-second expiry: without the lock the import ran twice.

Refs #1023
2026-09-30 23:11:03 +02:00
Marc-André Bombeck 731e073284 fix(measurements): hour-bucket a CGM glucose series inside the 90-day window
A sensor reading every five minutes puts about 26 000 glucose readings in
a 90-day window, and the per-kind series sent every one of them raw. The
hourly bucketing that already applies to a dense heart-rate stream now
applies to glucose too, with the same 10 000-reading cap: the hour's mean
converted once to the user's unit, statistics over every raw reading.
A fingerstick meter stays far under the cap and keeps its raw points.

The doctor report keeps glucose raw inside 90 days. Its clinical panel is
computed from the raw readings of every source, while the day path works
from canonical-source day buckets, so the two do not give the same figures
when a meter and a sensor overlap.
2026-09-30 23:01:57 +02:00
Marc-André Bombeck ca10f2e5df fix(medications): dashboard quick-add offers only doses the server calls actionable
The dashboard and capture-picker intake form filtered the medication list
on `active && trackIntake`, so a course that had ended (still active,
nobody paused it) was offered for a dose while the cards, the table and
"take all" already followed the server's `intakeActionable`. The picker
now renders from that one answer as well.

When medications exist but none offers a dose today, the empty state no
longer claims there are no medications; it says nothing is due and
points to the medication history for a past dose.
2026-09-30 22:58:19 +02:00
Marc-André Bombeck a2fceb5585 fix(backups): answer the restore preview from counts kept with the copy
The restore dialog asked GET /api/admin/backups/{id}/summary for the
contents of a copy, and the route read the whole stored copy to answer:
every piece opened, every measurement parsed, the rest validated. On an
account of 1.8 million readings on slow hardware that took about two
minutes. The dialog stopped waiting after 15 s and said the contents
could not be read, and its automatic retry made the server read the
whole copy a second time.

The copy passes through the process once anyway while it is written, so
the preview is worked out there and kept on the row (DataBackup.preview,
migration 0364): the summarizeBackup counts, the schema version, and
the inner key ids with the sections they sit in and one sample value
each. Every creation path goes through storeBackupBlob (the weekly and
manual pass, and an upload), so every new copy carries one. The column
is plain JSON: counts and dates only, and the samples are ciphertext
that stands for what is inside the copy, which a key rotation must not
re-seal.

The route answers from it without opening the copy. Both verdicts are
taken again on every read, from what the preview keeps: whether this
release restores the schema version, and whether this server holds the
keys the copy needs. The pieces are still counted against the row, so
a copy that lost one is refused as the restore and the download refuse
it. A preview names the copy it describes and is ignored once the copy
changes; converting a single stored value into pieces carries it over.

A copy written before this has no preview. It is read whole once, as
before, and the result is kept on the row, so it is slow only the first
time; concurrent requests for the same copy share the one read.

Only a file that fails the schema answers 422 "Backup payload failed
schema validation". Read refusals keep their codes; anything else is
the server failing and answers 500.

The dialog no longer retries the preview, waits up to 55 s, and when it
stops waiting on an older copy it says the copy is still being read and
to open the dialog again in a few minutes.

Refs #1031
2026-09-30 22:51:02 +02:00
Marc-André Bombeck fab5cbccbf fix(deps): raise the dev-only grpc-js, brace-expansion and undici 7 floors
The dependency alerts flag development copies only: @grpc/grpc-js under the
test containers, brace-expansion on three major lines, and undici 7 next to
the app's own undici 8. None ships in the image.
2026-09-30 22:42:50 +02:00
Marc-André Bombeck c7be1a0c5f Merge branch 'main' into feat/workouts-write-scope 2026-09-30 22:35:44 +02:00
Marc-André Bombeck eb7c3fafe4 Merge branch 'main' into fix/ocr-review-date-validation 2026-09-30 22:35:42 +02:00
Marc-André Bombeck 95d3676821 Merge pull request #1061 from MBombeck/fix/deps-security-2026-09-30
Move next to 16.3.6 and raise three transitive floors
2026-09-30 22:35:26 +02:00
Marc-André Bombeck e7166cb3c6 feat(tokens): mint a workouts:write token for headless workout ingest
A bridge that pushes workouts from a watch vendor had no credential that
reached POST /api/workouts/batch: the route called requireAuth() with no
scope, so under the fail-closed default only a cookie session or a
wildcard token got through, and a measurements:write token was refused
403. The route's header still described the pre-fail-closed behaviour.

Add a separate workouts:write scope rather than widening
measurements:write, so a token already handed to a scale does not start
accepting workouts.

- POST /api/tokens/workouts mints it, mirroring the measurement and
  document mints: cookie session plus a fresh proof, a literal permission
  array, 10 mints a minute, at most 10 live tokens (409
  tokens.workouts.ceiling_reached), a one-year default lifetime, audited.
- POST /api/workouts/batch names the scope. Sessions and wildcard tokens
  behave as before. A scoped caller's workouts are stored as EXTERNAL,
  resolved from the credential, and a batch in which any entry names a
  source is refused 422 (workout.batch.source_not_permitted), the same
  rule the measurement batch applies. EXTERNAL is on no source ladder, so
  when the phone recorded the same session the phone's copy is shown.
- Settings > API offers a workout token card next to the measurement one,
  and the endpoint list shows the batch route.
- Guards: the mint site, the declared scope vocabulary, the recent-proof
  routes, and a new check that only the batch declares the scope.
- OpenAPI documents the mint, the batch's narrow-token behaviour and both
  error codes; docs/api/README.md lists the workout and document mints.

Refs #1054
2026-09-30 22:21:46 +02:00
Marc-André Bombeck 162ba62752 fix(deps): move next to 16.3.6 and raise the fast-uri, ip-address and dompurify floors
The production audit flags a critical advisory against next 16.2.0 to 16.3.5,
fixed in 16.3.6, and newer advisories against the transitive fast-uri (ajv),
ip-address (the MCP SDK's rate limiter) and dompurify (jspdf) copies. Next is
pinned to 16.3.6 rather than the newest patch so the install stays inside the
minimum release age without new exclusions.
2026-09-30 22:12:47 +02:00
Marc-André Bombeck 1627aa6c57 fix(labs): translate the review validation messages and narrow a test lookup 2026-09-30 22:02:02 +02:00
BernhardBuckel eaced8226b fix(labs): block a lab-scan save while a selected reading is incomplete
The review dialog silently dropped any selected reading that could not be
written (no date, no unit, no value) and saved the rest. When nothing was
left it showed "Select at least one reading to save", which described a
different situation and said nothing about what was missing.

A save is now all-or-nothing over the selected readings. When one is
incomplete, nothing is written; the dialog states how many readings need
attention, marks each missing field (aria-invalid, inline message, red
border, required marker on the label) and moves focus to the first one.
Marks appear after a save attempt and follow the edits. The "nothing
selected" message is kept for that case only.

Validation is a pure function next to the payload mapping, so the rule that
blocks a save and the rule that builds the payload cannot drift apart.

Adds EN and DE strings; the other locales are left to the maintainers.

Refs #1055
2026-09-29 20:31:22 +02:00
Marc-André Bombeck ae6a5ac693 Merge pull request #1053 from MBombeck/release/v1.39.5
Release v1.39.5
v1.39.5
2026-09-28 01:17:57 +02:00
Marc-André Bombeck 3f3bb0579a chore(release): v1.39.5 — a dense heart-rate history within memory, and restores that hold less 2026-09-28 00:55:23 +02:00
Marc-André Bombeck 5bbcf28bdc Merge pull request #1052 from MBombeck/fix/1031-restore-memory
Bound restore memory and order it against the consolidation passes
2026-09-28 00:51:31 +02:00
Marc-André Bombeck add6d1fadc fix(restore): order the restore and the consolidation passes by one lock
A restore on a freshly booted instance failed its clearing step with a
deadlock (40P01) while the boot-time step consolidation was folding the
same account. A consolidation day updates or mints the day's total and
then soft-deletes the day's per-sample rows; the restore deletes every
reading of the account in one statement. When the restore reached a
day's samples before its total, each side held what the other needed
next.

The restore already took an advisory lock per account before its first
delete. Every day transaction of the passes built on runConsolidation
(step and mean consolidation, the per-sample cumulative drain, the dense
intraday retention) now takes the same lock in shared mode as its first
statement. A restore that starts mid-day waits for that day to commit;
a day that would start while a restore runs is refused at once, and the
pass leaves that account for its next run instead of folding rows the
restore is about to replace. The passes still share the lock with each
other.

A new integration test builds the lock order that deadlocked, with a
row trigger holding the consolidation on the day's total while the
restore starts; before this change the consolidation was cancelled with
a deadlock. A guard test requires every transaction in a pass on
runConsolidation to open with the shared lock.

Refs #1031
2026-09-28 00:34:11 +02:00
Marc-André Bombeck f2569b6fdf fix(restore): carry at most a hundred readings per insert statement
Prisma's debug logger keeps the arguments of its last hundred calls in
memory whether or not logging is on, and the client and the Postgres
adapter both log every statement with its parameters. A restore that
wrote 1 000 readings per statement therefore kept a copy of about the
last 100 000 readings alive for the whole measurement write: about
60 MB on 1.8 million Apple Health readings, next to a V8 heap limit of
524 MB in the default 1 GB container.

The bulk insert now splits whatever it is handed into statements of at
most 100 rows, which holds about 7 MB. The Apple Health export import
goes through the same helper and gets the same bound. The write costs a
little time: 94 s instead of 76 s for 1.8 million readings on a fast
host.

Refs #1031
2026-09-28 00:06:47 +02:00
Marc-André Bombeck 2eec103182 Merge pull request #1051 from MBombeck/fix/1023-dense-stream-memory
Keep a dense heart-rate stream out of memory
2026-09-27 21:47:18 +02:00
Marc-André Bombeck 6836830038 fix(doctor-report): report readings, not day buckets, for a dense type
A type served from day buckets (pulse or glucose past 90 days, and since
the previous change a dense pulse stream on any window) is drawn one point
per day, each the day's mean at the day's start. The summary line counted
those points as measurements, and the FHIR export stamped the last day's
mean as the latest observation: a value and a time nobody measured.

The day-bucket stats now carry when their newest reading was taken
(`latestAt`). `latestReading` returns that reading when it is there, and
the summary line counts each type's readings from its stats. Raw series
are unchanged: their stats count equals their points and their last point
is the newest reading.
2026-09-27 21:20:24 +02:00
Marc-André Bombeck 3458100ac4 test: give the report's pulse-window count a mock and a typed route call 2026-09-27 21:16:20 +02:00
Marc-André Bombeck 0e991c3640 fix(measurements): fold a dense pulse stream inside the 90-day raw window
Past 90 days every pulse reader already folded in SQL. Inside it, three
still read every raw heart-rate row, which on a watch that records a
reading a minute is 130 000 rows per read (#1023):

- The insights page's resting-pulse proxy, used when an account has no
  resting heart rate, walked the whole window to take one percentile per
  day and held the event loop for over a second per view. It now folds in
  Postgres (`readRestingPulseProxy`) with the same percentile, rounding,
  minimum-sample rule and day anchor.
- The per-kind series sent every reading: 14.7 MB for 90 days. A pulse
  window with more than 10 000 readings is now bucketed per local hour,
  mean plus low/high band, with stats still over the readings.
- The doctor report drew every reading into one sparkline. A pulse window
  over the same cap now takes the per-day path longer reports already use.

Sparse pulse (a cuff, hourly imports, a watch sampling every few minutes)
stays under the cap and keeps its raw shape.
2026-09-27 21:07:29 +02:00
Marc-André Bombeck 821c2eb884 test(google-health): pin a heart-rate walk past a thousand pages 2026-09-27 20:43:15 +02:00
Marc-André Bombeck e34741c077 fix(google-health): write a dense collection page by page during a backfill
A full-history backfill read every heart-rate point of an account into one
array, mapped it into a second, and wrote the lot in one call; the cycle's
deferred-rollup ledger then kept one entry per written reading. On a watch
that records a reading a minute that is over a million points, and the
backfill ran a 1 GB heap out of memory within seconds, restarting the
instance it shares with the app (#1023).

The metric walk now hands each page to its write before requesting the
next, and both the Google Health and Fitbit ledgers collapse to one key per
touched type-day as they record. The heart-rate walk's page ceiling rises
from 1000 to 10000: at one point a minute the old ceiling is under two
years, a longer history always ended truncated, and the backfill retried
the whole walk on every attempt and every boot without completing.
2026-09-27 20:42:54 +02:00
Marc-André Bombeck c2a1522634 Merge pull request #1049 from MBombeck/dependabot/npm_and_yarn/pg-boss-12.34.0
build(deps): bump pg-boss from 12.27.0 to 12.34.0
2026-09-27 20:14:27 +02:00
Marc-André Bombeck 1910a6f760 Merge pull request #1047 from MBombeck/dependabot/npm_and_yarn/types/node-26.6.2
build(deps-dev): bump @types/node from 26.5.1 to 26.6.2
2026-09-27 20:01:12 +02:00
Marc-André Bombeck b9af05cf8d Merge pull request #1046 from MBombeck/dependabot/npm_and_yarn/undici-8.11.2
build(deps): bump undici from 8.10.2 to 8.11.2
2026-09-27 19:52:25 +02:00
Marc-André Bombeck 7a21bb6787 Merge pull request #1045 from MBombeck/dependabot/npm_and_yarn/tanstack/react-query-5.103.2
build(deps): bump @tanstack/react-query from 5.102.8 to 5.103.2
2026-09-27 19:51:59 +02:00
Marc-André Bombeck 522b61fdec Merge pull request #1050 from MBombeck/dependabot/github_actions/codeql-action-9d88a12d2b
build(deps): bump the codeql-action group with 3 updates
2026-09-27 19:51:55 +02:00
Marc-André Bombeck 6f6c4ffff0 test(jobs): re-pin the pg-boss retention assumptions to 12.34.0 2026-09-27 19:51:46 +02:00
dependabot[bot] dfd0f2e6dd build(deps-dev): bump prettier from 3.9.6 to 3.9.9 (#1048)
Bumps [prettier](https://github.com/prettier/prettier) from 3.9.6 to 3.9.9.
- [Release notes](https://github.com/prettier/prettier/releases)
- [Changelog](https://github.com/prettier/prettier/blob/main/CHANGELOG.md)
- [Commits](https://github.com/prettier/prettier/compare/3.9.6...3.9.9)

---
updated-dependencies:
- dependency-name: prettier
  dependency-version: 3.9.9
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-09-27 16:35:23 +00:00
dependabot[bot] bdad4f3dcd build(deps): bump the codeql-action group with 3 updates
Bumps the codeql-action group with 3 updates: [github/codeql-action/init](https://github.com/github/codeql-action), [github/codeql-action/analyze](https://github.com/github/codeql-action) and [github/codeql-action/upload-sarif](https://github.com/github/codeql-action).


Updates `github/codeql-action/init` from 4.38.0 to 4.38.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2)

Updates `github/codeql-action/analyze` from 4.38.0 to 4.38.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2)

Updates `github/codeql-action/upload-sarif` from 4.38.0 to 4.38.2
- [Release notes](https://github.com/github/codeql-action/releases)
- [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/github/codeql-action/compare/b96794f015dfd88f77b49b1c93e0fa7110f94c63...2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2)

---
updated-dependencies:
- dependency-name: github/codeql-action/init
  dependency-version: 4.38.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-action
- dependency-name: github/codeql-action/analyze
  dependency-version: 4.38.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-action
- dependency-name: github/codeql-action/upload-sarif
  dependency-version: 4.38.2
  dependency-type: direct:production
  update-type: version-update:semver-patch
  dependency-group: codeql-action
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-27 15:55:47 +00:00
dependabot[bot] 891db1753e build(deps): bump pg-boss from 12.27.0 to 12.34.0
Bumps [pg-boss](https://github.com/timgit/pg-boss) from 12.27.0 to 12.34.0.
- [Release notes](https://github.com/timgit/pg-boss/releases)
- [Commits](https://github.com/timgit/pg-boss/compare/12.27.0...12.34.0)

---
updated-dependencies:
- dependency-name: pg-boss
  dependency-version: 12.34.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-27 15:55:34 +00:00
dependabot[bot] ddf685e2c9 build(deps-dev): bump @types/node from 26.5.1 to 26.6.2
Bumps [@types/node](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/node) from 26.5.1 to 26.6.2.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/node)

---
updated-dependencies:
- dependency-name: "@types/node"
  dependency-version: 26.6.2
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-09-27 15:54:38 +00:00