From 911524d6ee400fa8461cc0a452f1c4499503e267 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 10 Aug 2026 12:51:16 -0400 Subject: [PATCH 1/9] docs: add spec and plan for undici peer dependency Design and implementation plan for declaring undici as a peer dependency required by @slack/socket-mode@3, with a drift-guard test. Refs: https://github.com/slackapi/bolt-js/issues/3039 Co-Authored-By: Claude --- .../2026-08-10-undici-peer-dependency.md | 271 ++++++++++++++++++ ...026-08-10-undici-peer-dependency-design.md | 86 ++++++ 2 files changed, 357 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-10-undici-peer-dependency.md create mode 100644 docs/superpowers/specs/2026-08-10-undici-peer-dependency-design.md diff --git a/docs/superpowers/plans/2026-08-10-undici-peer-dependency.md b/docs/superpowers/plans/2026-08-10-undici-peer-dependency.md new file mode 100644 index 000000000..1b91e0fd5 --- /dev/null +++ b/docs/superpowers/plans/2026-08-10-undici-peer-dependency.md @@ -0,0 +1,271 @@ +# Undici Peer Dependency Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Make `@slack/bolt` formally provide the `undici` peer dependency required by `@slack/socket-mode@3`, with a CI guard that prevents the declared range from silently drifting out of sync. + +**Architecture:** Add `undici@^7.0.0` to Bolt's `peerDependencies` (not optional — undici loads eagerly for every Bolt app). Add a unit test that reads socket-mode's actual `peerDependencies.undici` at test time and asserts Bolt's declared range is a semver subset of it. Ship a changeset and a docs note. + +**Tech Stack:** TypeScript, npm, Mocha + Chai (unit tests), `semver` (range comparison), Changesets, Biome. + +**Branch:** `feat/undici-peer-dependency` (already created off `main`). + +## Global Constraints + +- **Undici range:** `^7.0.0` — must match `@slack/socket-mode`'s declared `peerDependencies.undici` range verbatim. Copied from `node_modules/@slack/socket-mode/package.json`. +- **Not optional:** do NOT add `undici` to `peerDependenciesMeta` — undici is required at module-load time for all consumers. +- **Node/npm floors:** unchanged (`node >=20`, `npm >=9.6.4` per `engines`). +- **Linting:** Biome only (`npm run lint`). Never ESLint/Prettier. +- **Full gate:** `npm test` (build → lint → type tests → unit coverage) MUST pass before the work is considered complete. `npm run test:unit` requires a prior build. +- **Commit trailer:** every commit message ends with: + `Co-Authored-By: Claude ` + +--- + +### Task 1: Add `undici` peer dependency and `semver` devDependencies + +**Files:** +- Modify: `package.json` (`peerDependencies`, `devDependencies`) + +**Interfaces:** +- Consumes: nothing. +- Produces: `peerDependencies.undici === "^7.0.0"` in `package.json`; `semver` and `@types/semver` present in `devDependencies` (consumed by Task 2's guard test). + +- [ ] **Step 1: Add `undici` to `peerDependencies`** + +Edit `package.json` so the `peerDependencies` block reads (keep keys alphabetized): + +```json +"peerDependencies": { + "@types/express": "^5.0.0", + "undici": "^7.0.0" +} +``` + +- [ ] **Step 2: Add `semver` + `@types/semver` to `devDependencies`** + +Add these entries into the existing `devDependencies` block (alphabetized), needed by the Task 2 guard test: + +```json +"@types/semver": "^7.7.0", +"semver": "^7.7.0", +``` + +- [ ] **Step 3: Install to refresh the lockfile** + +Run: `npm install` +Expected: succeeds; `package-lock.json` updated; `semver` and `@types/semver` resolve into `node_modules`. If the min-release-age gate blocks a fresh version, re-run with `npm install --min-release-age=0`. + +- [ ] **Step 4: Verify undici now resolves as provided by bolt** + +Run: `npm ls undici` +Expected: shows `undici` under `@slack/socket-mode`; no "unmet peer" / "invalid" error for bolt. + +- [ ] **Step 5: Commit** + +```bash +git add package.json package-lock.json +git commit -m "$(cat <<'EOF' +feat: declare undici peer dependency for socket-mode + +@slack/socket-mode@3 declares undici@^7 as a peer dependency, which Bolt +constructs internally via SocketModeClient. Declare it as a Bolt peer so +the dependency graph is complete under strict package managers (Yarn/pnpm). +Add semver + @types/semver as devDependencies for the drift-guard test. + +Refs: https://github.com/slackapi/bolt-js/issues/3039 + +Co-Authored-By: Claude +EOF +)" +``` + +--- + +### Task 2: Add the drift-guard unit test + +**Files:** +- Create: `test/unit/peer-dependencies.spec.ts` +- Reference (read-only): `package.json`, `node_modules/@slack/socket-mode/package.json` + +**Interfaces:** +- Consumes: `undici` peer range from Task 1; `semver` devDependency from Task 1. +- Produces: a mocha spec that fails when Bolt's declared `undici` range is not a subset of socket-mode's `peerDependencies.undici`. + +**Design notes:** +- Use `semver.subset(boltRange, socketModeRange)` — passes as long as every undici version Bolt accepts is acceptable to socket-mode. This tolerates cosmetic differences and only fails on a real incompatibility (e.g. socket-mode narrows to `^7.5.0` while Bolt stays `^7.0.0`). +- Resolve socket-mode's `package.json` via `require.resolve('@slack/socket-mode/package.json')` so the test reads the *installed* version, not a hardcoded string. +- Read Bolt's own `package.json` from the repo root (the spec file lives at `test/unit/`, so go up two levels). + +- [ ] **Step 1: Write the failing test** + +Create `test/unit/peer-dependencies.spec.ts`: + +```typescript +import { readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { assert } from 'chai'; +import semver from 'semver'; + +describe('undici peer dependency', () => { + // Bolt's own package.json (repo root, two levels up from test/unit/). + const boltPkg = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf8')) as { + peerDependencies?: Record; + }; + // The installed @slack/socket-mode manifest, resolved from node_modules. + const socketModePkg = JSON.parse(readFileSync(require.resolve('@slack/socket-mode/package.json'), 'utf8')) as { + peerDependencies?: Record; + }; + + it('is declared by Bolt', () => { + assert.isDefined( + boltPkg.peerDependencies?.undici, + 'Bolt must declare undici in peerDependencies so it provides the peer required by @slack/socket-mode', + ); + }); + + it("satisfies @slack/socket-mode's undici peer requirement", () => { + const boltRange = boltPkg.peerDependencies?.undici; + const socketModeRange = socketModePkg.peerDependencies?.undici; + assert.isDefined(socketModeRange, '@slack/socket-mode should declare an undici peer dependency'); + assert.isString(boltRange); + assert.isTrue( + semver.subset(boltRange as string, socketModeRange as string), + `Bolt's undici range "${boltRange}" must be a subset of @slack/socket-mode's "${socketModeRange}". ` + + 'Update peerDependencies.undici in package.json to match.', + ); + }); +}); +``` + +- [ ] **Step 2: Build, then run the test to verify it passes** + +Run: `npm run build && npm run test:unit -- --grep "undici peer dependency"` +Expected: both assertions PASS (Task 1 already added the matching `^7.0.0` range). + +Note: this test is designed to pass once Task 1 is done. To confirm it genuinely guards, do Step 3. + +- [ ] **Step 3: Temporarily prove the guard bites** + +Temporarily edit `package.json` `peerDependencies.undici` to an incompatible range like `"^6.0.0"`, then run: +`npm run build && npm run test:unit -- --grep "undici peer dependency"` +Expected: the second test FAILS with the "must be a subset" message. +Then **revert** the range back to `"^7.0.0"` and re-run to confirm PASS. + +- [ ] **Step 4: Run Biome on the new file** + +Run: `npm run lint` +Expected: no lint errors. If formatting differs, run `npm run lint:fix` and re-check. + +- [ ] **Step 5: Commit** + +```bash +git add test/unit/peer-dependencies.spec.ts +git commit -m "$(cat <<'EOF' +test: guard undici peer range against @slack/socket-mode drift + +Reads the installed @slack/socket-mode peerDependencies.undici at test time +and asserts Bolt's declared range is a semver subset, so the two can't +silently fall out of sync. + +Co-Authored-By: Claude +EOF +)" +``` + +--- + +### Task 3: Add changeset and docs note + +**Files:** +- Create: `.changeset/undici-peer-dependency.md` +- Modify: `docs/english/concepts/socket-mode.md` + +**Interfaces:** +- Consumes: nothing from prior tasks (documentation only). +- Produces: a patch changeset; a consumer-facing install note. + +- [ ] **Step 1: Create the changeset** + +Create `.changeset/undici-peer-dependency.md`: + +```markdown +--- +"@slack/bolt": patch +--- + +Declare `undici` as a peer dependency (`^7.0.0`). `@slack/bolt` constructs a `SocketModeClient` from `@slack/socket-mode@3`, which requires `undici@^7` as a peer. Consumers on strict package managers (Yarn Berry, pnpm) should install `undici` alongside `@slack/bolt`. Resolves #3039. +``` + +- [ ] **Step 2: Add the docs note** + +In `docs/english/concepts/socket-mode.md`, immediately after the opening paragraph (the line ending `be sure to enable it within your app configuration.`), insert a blank line and this note: + +```markdown +> #### Installing `undici` +> +> Socket Mode uses [`undici`](https://www.npmjs.com/package/undici) for its WebSocket connection, declared as a peer dependency. npm installs it automatically, but on strict package managers (Yarn Berry, pnpm) you may need to install it explicitly: +> +> ```shell +> npm install undici +> ``` +``` + +- [ ] **Step 3: Lint docs** + +Run: `npm run lint` +Expected: no errors (Biome checks `docs`). + +- [ ] **Step 4: Commit** + +```bash +git add .changeset/undici-peer-dependency.md docs/english/concepts/socket-mode.md +git commit -m "$(cat <<'EOF' +docs: note undici peer dependency for Socket Mode + +Add changeset and a Socket Mode install note for strict package managers. + +Co-Authored-By: Claude +EOF +)" +``` + +--- + +### Task 4: Full pipeline verification + +**Files:** none (verification only). + +**Interfaces:** +- Consumes: all prior tasks. +- Produces: green `npm test` run. + +- [ ] **Step 1: Run the full test pipeline** + +Run: `npm test` +Expected: build → lint → type tests → unit coverage all PASS, including `undici peer dependency` specs. + +- [ ] **Step 2: If anything fails, fix and re-run** + +Address failures, re-run `npm test` until green. Do not mark complete on a red pipeline. + +- [ ] **Step 3: Push the branch and open a PR (only if the user asks)** + +```bash +git push -u origin feat/undici-peer-dependency +``` +Then open a PR referencing issue #3039. Do not push or open the PR without explicit user go-ahead. + +--- + +## Self-Review + +- **Spec coverage:** + - `package.json` peer + devDeps → Task 1. ✓ + - Drift-guard test → Task 2. ✓ + - Changeset → Task 3 Step 1. ✓ + - Docs note → Task 3 Step 2. ✓ + - `npm test` verification → Task 4. ✓ + - Non-optional peer constraint → Global Constraints + Task 1 (no `peerDependenciesMeta`). ✓ +- **Placeholder scan:** no TBD/TODO; all code and commands are concrete. ✓ +- **Type consistency:** `boltRange`/`socketModeRange` typed and used consistently; `semver.subset(a, b)` argument order (bolt is subset of socket-mode) consistent between the design and the test. ✓ diff --git a/docs/superpowers/specs/2026-08-10-undici-peer-dependency-design.md b/docs/superpowers/specs/2026-08-10-undici-peer-dependency-design.md new file mode 100644 index 000000000..0b39335eb --- /dev/null +++ b/docs/superpowers/specs/2026-08-10-undici-peer-dependency-design.md @@ -0,0 +1,86 @@ +# Undici Peer Dependency Design + +**Issue:** [slackapi/bolt-js#3039](https://github.com/slackapi/bolt-js/issues/3039) + +## Problem + +`@slack/socket-mode@3.0.0` declares `undici@^7.0.0` as a **peer dependency** (v3 +replaced `ws` with `undici`). `@slack/bolt` depends on `@slack/socket-mode@^3.0.0` +and constructs `SocketModeClient` internally (`src/receivers/SocketModeReceiver.ts`), +but Bolt neither declares `undici` as a dependency nor forwards it as a peer. + +As a result, nothing in the ancestor chain formally *provides* the `undici` peer +that socket-mode requires. Strict package managers surface this: + +```text +@slack/bolt@npm:5.0.0 doesn't provide undici to @slack/socket-mode@npm:3.0.0 +``` + +npm 7+ hides the gap by auto-installing peers of transitive deps; Yarn Berry / +pnpm validate strictly and report the unmet peer. + +### Load-time reality + +`undici` is loaded **eagerly by every Bolt app**, not just Socket Mode apps. +Verified at runtime: `require('@slack/bolt')` pulls in `undici`. The chain is +static, top-level `require`s all the way down: + +`src/index.ts` → `import SocketModeReceiver` +→ `import { SocketModeClient } from '@slack/socket-mode'` +→ `require("undici")` at the top of `SocketModeClient.js`. + +There is no lazy/dynamic import in that path, so `undici` is required at +module-load time even for a plain HTTP-receiver app. + +## Decision + +Declare `undici@^7.0.0` in Bolt's `peerDependencies` (**not** marked optional). + +- **Not optional** is technically accurate: without `undici`, `require('@slack/bolt')` + throws `Cannot find module 'undici'` for *all* consumers. +- Matches socket-mode's own range (`^7.0.0`), so any future `7.x` peer bump in + socket-mode stays satisfied automatically. The range would only need touching + on an `undici` **major** bump — which coincides with a `@slack/socket-mode` + major upgrade done deliberately. + +### Trade-off (accepted, on record) + +Because `undici` loads eagerly regardless of receiver, a required (non-optional) +peer means existing v5 consumers on strict package managers who do **not** use +Socket Mode will now see a peer warning / be required to install `undici`, even +for a plain HTTP app. This is a minor behavior change for an already-released +major. Mitigated by the docs note and the drift-guard test below. + +### Sync burden + +There is no package.json mechanism to *inherit* a transitive peer's version +range — a declared range is static, so some duplication is unavoidable. Two +things make drift harmless and self-detecting: + +1. Matching caret range (`^7.0.0`) absorbs all `7.x` peer bumps automatically. +2. A drift-guard unit test fails CI if Bolt's declared range ever becomes + incompatible with socket-mode's actual `peerDependencies.undici`. + +## Scope + +1. **`package.json`** — add `undici: "^7.0.0"` to `peerDependencies`; add + `semver` + `@types/semver` to `devDependencies` for the guard test. +2. **Drift-guard test** — `test/unit/peer-dependencies.spec.ts`. Reads Bolt's + own declared `undici` peer range and the installed + `@slack/socket-mode` `peerDependencies.undici`; asserts Bolt declares + `undici` and that `semver.subset(boltRange, socketModeRange)` is `true`. +3. **Changeset** — patch entry describing the new peer dependency. +4. **Docs** — note in `docs/english/concepts/socket-mode.md` that consumers on + strict package managers must install `undici` alongside `@slack/bolt`. + (Japanese mirror out of scope for this change.) + +## Non-goals + +- Fixing `@slack/socket-mode` itself (different repo) — out of scope here. +- Marking the peer optional — rejected as inaccurate given eager load. +- Adding `undici` as a regular `dependency` — considered; peer chosen instead. + +## Verification + +Full `npm test` pipeline (build → lint → type tests → unit coverage) passes, +including the new guard test. From 76ea238e87f882d35a5a203e093915d633662b3f Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 10 Aug 2026 13:05:16 -0400 Subject: [PATCH 2/9] feat: declare undici peer dependency for socket-mode @slack/socket-mode@3 declares undici@^7 as a peer dependency, which Bolt constructs internally via SocketModeClient. Declare it as a Bolt peer so the dependency graph is complete under strict package managers (Yarn/pnpm). Add semver + @types/semver as devDependencies for the drift-guard test. Refs: https://github.com/slackapi/bolt-js/issues/3039 Co-Authored-By: Claude --- package-lock.json | 12 +++++++++++- package.json | 5 ++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index de005a304..083645d43 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,12 +27,14 @@ "@types/mocha": "^10.0.1", "@types/node": "20.19.0", "@types/proxyquire": "^1.3.31", + "@types/semver": "^7.7.0", "@types/sinon": "^17.0.4", "@types/tsscmp": "^1.0.0", "c8": "^10.1.2", "chai": "~4.3.0", "mocha": "^10.2.0", "proxyquire": "^2.1.3", + "semver": "^7.7.0", "shx": "^0.3.2", "sinon": "^20.0.0", "source-map-support": "^0.5.12", @@ -45,7 +47,8 @@ "npm": ">=9.6.4" }, "peerDependencies": { - "@types/express": "^5.0.0" + "@types/express": "^5.0.0", + "undici": "^7.0.0" } }, "node_modules/@babel/code-frame": { @@ -1053,6 +1056,13 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "license": "MIT" }, + "node_modules/@types/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-1mAINjtQCXXeLkJ9ehXkwOcBpqtLxiVtKhpUf83DdRNdQKV0iXZpaHYqRr7nj+wvxuJzoAmAwXI+sCNMv1CzLQ==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/send": { "version": "1.2.1", "license": "MIT", diff --git a/package.json b/package.json index 04a32c4bb..cf5527425 100644 --- a/package.json +++ b/package.json @@ -65,12 +65,14 @@ "@types/mocha": "^10.0.1", "@types/node": "20.19.0", "@types/proxyquire": "^1.3.31", + "@types/semver": "^7.7.0", "@types/sinon": "^17.0.4", "@types/tsscmp": "^1.0.0", "c8": "^10.1.2", "chai": "~4.3.0", "mocha": "^10.2.0", "proxyquire": "^2.1.3", + "semver": "^7.7.0", "shx": "^0.3.2", "sinon": "^20.0.0", "source-map-support": "^0.5.12", @@ -79,6 +81,7 @@ "typescript": "5.3.3" }, "peerDependencies": { - "@types/express": "^5.0.0" + "@types/express": "^5.0.0", + "undici": "^7.0.0" } } From d4877a688022c42d13a9865931b3b317b81fcaca Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 10 Aug 2026 15:39:20 -0400 Subject: [PATCH 3/9] test: guard undici peer range against @slack/socket-mode drift Reads the installed @slack/socket-mode peerDependencies.undici at test time and asserts Bolt's declared range is a semver subset, so the two can't silently fall out of sync. Co-Authored-By: Claude --- test/unit/peer-dependencies.spec.ts | 38 +++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 test/unit/peer-dependencies.spec.ts diff --git a/test/unit/peer-dependencies.spec.ts b/test/unit/peer-dependencies.spec.ts new file mode 100644 index 000000000..151b7c19f --- /dev/null +++ b/test/unit/peer-dependencies.spec.ts @@ -0,0 +1,38 @@ +import { readFileSync } from 'node:fs'; +import { createRequire } from 'node:module'; +import { join } from 'node:path'; +import { assert } from 'chai'; +import semver from 'semver'; + +// createRequire is needed for require.resolve in ESM contexts (Node 20+). +const moduleRequire = createRequire(join(process.cwd(), 'package.json')); + +describe('undici peer dependency', () => { + // Bolt's own package.json (repo root). + const boltPkg = JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf8')) as { + peerDependencies?: Record; + }; + // The installed @slack/socket-mode manifest, resolved from node_modules. + const socketModePkg = JSON.parse(readFileSync(moduleRequire.resolve('@slack/socket-mode/package.json'), 'utf8')) as { + peerDependencies?: Record; + }; + + it('is declared by Bolt', () => { + assert.isDefined( + boltPkg.peerDependencies?.undici, + 'Bolt must declare undici in peerDependencies so it provides the peer required by @slack/socket-mode', + ); + }); + + it("satisfies @slack/socket-mode's undici peer requirement", () => { + const boltRange = boltPkg.peerDependencies?.undici; + const socketModeRange = socketModePkg.peerDependencies?.undici; + assert.isDefined(socketModeRange, '@slack/socket-mode should declare an undici peer dependency'); + assert.isString(boltRange); + assert.isTrue( + semver.subset(boltRange as string, socketModeRange as string), + `Bolt's undici range "${boltRange}" must be a subset of @slack/socket-mode's "${socketModeRange}". ` + + 'Update peerDependencies.undici in package.json to match.', + ); + }); +}); From 2b8c0591eeae19d215a8dd2222842be4f30fb4ea Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 10 Aug 2026 15:41:12 -0400 Subject: [PATCH 4/9] docs: note undici peer dependency for Socket Mode Add changeset and a Socket Mode install note for strict package managers. Co-Authored-By: Claude --- .changeset/undici-peer-dependency.md | 5 +++++ docs/english/concepts/socket-mode.md | 8 ++++++++ 2 files changed, 13 insertions(+) create mode 100644 .changeset/undici-peer-dependency.md diff --git a/.changeset/undici-peer-dependency.md b/.changeset/undici-peer-dependency.md new file mode 100644 index 000000000..9fd194769 --- /dev/null +++ b/.changeset/undici-peer-dependency.md @@ -0,0 +1,5 @@ +--- +"@slack/bolt": patch +--- + +Declare `undici` as a peer dependency (`^7.0.0`). `@slack/bolt` constructs a `SocketModeClient` from `@slack/socket-mode@3`, which requires `undici@^7` as a peer. Consumers on strict package managers (Yarn Berry, pnpm) should install `undici` alongside `@slack/bolt`. Resolves #3039. diff --git a/docs/english/concepts/socket-mode.md b/docs/english/concepts/socket-mode.md index 1feb53851..2bdc9cbea 100644 --- a/docs/english/concepts/socket-mode.md +++ b/docs/english/concepts/socket-mode.md @@ -2,6 +2,14 @@ [Socket Mode](/apis/events-api/using-socket-mode) allows your app to connect and receive data from Slack via a WebSocket connection. To handle the connection, Bolt for JavaScript includes a `SocketModeReceiver` (in `@slack/bolt@3.0.0` and higher). Before using Socket Mode, be sure to enable it within your app configuration. +> #### Installing `undici` +> +> Socket Mode uses [`undici`](https://www.npmjs.com/package/undici) for its WebSocket connection, declared as a peer dependency. npm installs it automatically, but on strict package managers (Yarn Berry, pnpm) you may need to install it explicitly: +> +> ```shell +> npm install undici +> ``` + To use the `SocketModeReceiver`, just pass in `socketMode:true` and `appToken:YOUR_APP_TOKEN` when initializing `App`. You can get your App Level Token in your app configuration under the **Basic Information** section. ```javascript From a3925dd3552ae2c91e54d55b3dc0062d099d8e5f Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 10 Aug 2026 16:15:42 -0400 Subject: [PATCH 5/9] docs: remove Socket Mode undici install note Drop the install note added earlier; keep the changeset entry. Co-Authored-By: Claude --- docs/english/concepts/socket-mode.md | 8 -------- 1 file changed, 8 deletions(-) diff --git a/docs/english/concepts/socket-mode.md b/docs/english/concepts/socket-mode.md index 2bdc9cbea..1feb53851 100644 --- a/docs/english/concepts/socket-mode.md +++ b/docs/english/concepts/socket-mode.md @@ -2,14 +2,6 @@ [Socket Mode](/apis/events-api/using-socket-mode) allows your app to connect and receive data from Slack via a WebSocket connection. To handle the connection, Bolt for JavaScript includes a `SocketModeReceiver` (in `@slack/bolt@3.0.0` and higher). Before using Socket Mode, be sure to enable it within your app configuration. -> #### Installing `undici` -> -> Socket Mode uses [`undici`](https://www.npmjs.com/package/undici) for its WebSocket connection, declared as a peer dependency. npm installs it automatically, but on strict package managers (Yarn Berry, pnpm) you may need to install it explicitly: -> -> ```shell -> npm install undici -> ``` - To use the `SocketModeReceiver`, just pass in `socketMode:true` and `appToken:YOUR_APP_TOKEN` when initializing `App`. You can get your App Level Token in your app configuration under the **Basic Information** section. ```javascript From 0160f34cdce4a08aeffcf17967f57e036b8b4b48 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 10 Aug 2026 16:18:45 -0400 Subject: [PATCH 6/9] test: remove undici peer drift-guard test Drop the drift-guard spec and the now-unused semver / @types/semver devDependencies. The undici peerDependency declaration remains. Co-Authored-By: Claude --- test/unit/peer-dependencies.spec.ts | 38 ----------------------------- 1 file changed, 38 deletions(-) delete mode 100644 test/unit/peer-dependencies.spec.ts diff --git a/test/unit/peer-dependencies.spec.ts b/test/unit/peer-dependencies.spec.ts deleted file mode 100644 index 151b7c19f..000000000 --- a/test/unit/peer-dependencies.spec.ts +++ /dev/null @@ -1,38 +0,0 @@ -import { readFileSync } from 'node:fs'; -import { createRequire } from 'node:module'; -import { join } from 'node:path'; -import { assert } from 'chai'; -import semver from 'semver'; - -// createRequire is needed for require.resolve in ESM contexts (Node 20+). -const moduleRequire = createRequire(join(process.cwd(), 'package.json')); - -describe('undici peer dependency', () => { - // Bolt's own package.json (repo root). - const boltPkg = JSON.parse(readFileSync(join(process.cwd(), 'package.json'), 'utf8')) as { - peerDependencies?: Record; - }; - // The installed @slack/socket-mode manifest, resolved from node_modules. - const socketModePkg = JSON.parse(readFileSync(moduleRequire.resolve('@slack/socket-mode/package.json'), 'utf8')) as { - peerDependencies?: Record; - }; - - it('is declared by Bolt', () => { - assert.isDefined( - boltPkg.peerDependencies?.undici, - 'Bolt must declare undici in peerDependencies so it provides the peer required by @slack/socket-mode', - ); - }); - - it("satisfies @slack/socket-mode's undici peer requirement", () => { - const boltRange = boltPkg.peerDependencies?.undici; - const socketModeRange = socketModePkg.peerDependencies?.undici; - assert.isDefined(socketModeRange, '@slack/socket-mode should declare an undici peer dependency'); - assert.isString(boltRange); - assert.isTrue( - semver.subset(boltRange as string, socketModeRange as string), - `Bolt's undici range "${boltRange}" must be a subset of @slack/socket-mode's "${socketModeRange}". ` + - 'Update peerDependencies.undici in package.json to match.', - ); - }); -}); From 18251c041d616f3a2eef1bee02516c563583adb1 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 10 Aug 2026 16:19:07 -0400 Subject: [PATCH 7/9] chore: drop unused semver devDependencies Remove semver and @types/semver, which were only used by the drift-guard test removed in the previous commit. Co-Authored-By: Claude --- package-lock.json | 9 --------- package.json | 2 -- 2 files changed, 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 083645d43..258157196 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,14 +27,12 @@ "@types/mocha": "^10.0.1", "@types/node": "20.19.0", "@types/proxyquire": "^1.3.31", - "@types/semver": "^7.7.0", "@types/sinon": "^17.0.4", "@types/tsscmp": "^1.0.0", "c8": "^10.1.2", "chai": "~4.3.0", "mocha": "^10.2.0", "proxyquire": "^2.1.3", - "semver": "^7.7.0", "shx": "^0.3.2", "sinon": "^20.0.0", "source-map-support": "^0.5.12", @@ -1056,13 +1054,6 @@ "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "license": "MIT" }, - "node_modules/@types/semver": { - "version": "7.8.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.8.0.tgz", - "integrity": "sha512-1mAINjtQCXXeLkJ9ehXkwOcBpqtLxiVtKhpUf83DdRNdQKV0iXZpaHYqRr7nj+wvxuJzoAmAwXI+sCNMv1CzLQ==", - "dev": true, - "license": "MIT" - }, "node_modules/@types/send": { "version": "1.2.1", "license": "MIT", diff --git a/package.json b/package.json index cf5527425..912837130 100644 --- a/package.json +++ b/package.json @@ -65,14 +65,12 @@ "@types/mocha": "^10.0.1", "@types/node": "20.19.0", "@types/proxyquire": "^1.3.31", - "@types/semver": "^7.7.0", "@types/sinon": "^17.0.4", "@types/tsscmp": "^1.0.0", "c8": "^10.1.2", "chai": "~4.3.0", "mocha": "^10.2.0", "proxyquire": "^2.1.3", - "semver": "^7.7.0", "shx": "^0.3.2", "sinon": "^20.0.0", "source-map-support": "^0.5.12", From 8173fb640ae518627e93e7a8b44cdb9aaf600851 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 10 Aug 2026 16:20:50 -0400 Subject: [PATCH 8/9] chore: remove planning artifacts Drop the spec and plan scaffolding docs; they are not part of the change. Co-Authored-By: Claude --- .../2026-08-10-undici-peer-dependency.md | 271 ------------------ ...026-08-10-undici-peer-dependency-design.md | 86 ------ 2 files changed, 357 deletions(-) delete mode 100644 docs/superpowers/plans/2026-08-10-undici-peer-dependency.md delete mode 100644 docs/superpowers/specs/2026-08-10-undici-peer-dependency-design.md diff --git a/docs/superpowers/plans/2026-08-10-undici-peer-dependency.md b/docs/superpowers/plans/2026-08-10-undici-peer-dependency.md deleted file mode 100644 index 1b91e0fd5..000000000 --- a/docs/superpowers/plans/2026-08-10-undici-peer-dependency.md +++ /dev/null @@ -1,271 +0,0 @@ -# Undici Peer Dependency Implementation Plan - -> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. - -**Goal:** Make `@slack/bolt` formally provide the `undici` peer dependency required by `@slack/socket-mode@3`, with a CI guard that prevents the declared range from silently drifting out of sync. - -**Architecture:** Add `undici@^7.0.0` to Bolt's `peerDependencies` (not optional — undici loads eagerly for every Bolt app). Add a unit test that reads socket-mode's actual `peerDependencies.undici` at test time and asserts Bolt's declared range is a semver subset of it. Ship a changeset and a docs note. - -**Tech Stack:** TypeScript, npm, Mocha + Chai (unit tests), `semver` (range comparison), Changesets, Biome. - -**Branch:** `feat/undici-peer-dependency` (already created off `main`). - -## Global Constraints - -- **Undici range:** `^7.0.0` — must match `@slack/socket-mode`'s declared `peerDependencies.undici` range verbatim. Copied from `node_modules/@slack/socket-mode/package.json`. -- **Not optional:** do NOT add `undici` to `peerDependenciesMeta` — undici is required at module-load time for all consumers. -- **Node/npm floors:** unchanged (`node >=20`, `npm >=9.6.4` per `engines`). -- **Linting:** Biome only (`npm run lint`). Never ESLint/Prettier. -- **Full gate:** `npm test` (build → lint → type tests → unit coverage) MUST pass before the work is considered complete. `npm run test:unit` requires a prior build. -- **Commit trailer:** every commit message ends with: - `Co-Authored-By: Claude ` - ---- - -### Task 1: Add `undici` peer dependency and `semver` devDependencies - -**Files:** -- Modify: `package.json` (`peerDependencies`, `devDependencies`) - -**Interfaces:** -- Consumes: nothing. -- Produces: `peerDependencies.undici === "^7.0.0"` in `package.json`; `semver` and `@types/semver` present in `devDependencies` (consumed by Task 2's guard test). - -- [ ] **Step 1: Add `undici` to `peerDependencies`** - -Edit `package.json` so the `peerDependencies` block reads (keep keys alphabetized): - -```json -"peerDependencies": { - "@types/express": "^5.0.0", - "undici": "^7.0.0" -} -``` - -- [ ] **Step 2: Add `semver` + `@types/semver` to `devDependencies`** - -Add these entries into the existing `devDependencies` block (alphabetized), needed by the Task 2 guard test: - -```json -"@types/semver": "^7.7.0", -"semver": "^7.7.0", -``` - -- [ ] **Step 3: Install to refresh the lockfile** - -Run: `npm install` -Expected: succeeds; `package-lock.json` updated; `semver` and `@types/semver` resolve into `node_modules`. If the min-release-age gate blocks a fresh version, re-run with `npm install --min-release-age=0`. - -- [ ] **Step 4: Verify undici now resolves as provided by bolt** - -Run: `npm ls undici` -Expected: shows `undici` under `@slack/socket-mode`; no "unmet peer" / "invalid" error for bolt. - -- [ ] **Step 5: Commit** - -```bash -git add package.json package-lock.json -git commit -m "$(cat <<'EOF' -feat: declare undici peer dependency for socket-mode - -@slack/socket-mode@3 declares undici@^7 as a peer dependency, which Bolt -constructs internally via SocketModeClient. Declare it as a Bolt peer so -the dependency graph is complete under strict package managers (Yarn/pnpm). -Add semver + @types/semver as devDependencies for the drift-guard test. - -Refs: https://github.com/slackapi/bolt-js/issues/3039 - -Co-Authored-By: Claude -EOF -)" -``` - ---- - -### Task 2: Add the drift-guard unit test - -**Files:** -- Create: `test/unit/peer-dependencies.spec.ts` -- Reference (read-only): `package.json`, `node_modules/@slack/socket-mode/package.json` - -**Interfaces:** -- Consumes: `undici` peer range from Task 1; `semver` devDependency from Task 1. -- Produces: a mocha spec that fails when Bolt's declared `undici` range is not a subset of socket-mode's `peerDependencies.undici`. - -**Design notes:** -- Use `semver.subset(boltRange, socketModeRange)` — passes as long as every undici version Bolt accepts is acceptable to socket-mode. This tolerates cosmetic differences and only fails on a real incompatibility (e.g. socket-mode narrows to `^7.5.0` while Bolt stays `^7.0.0`). -- Resolve socket-mode's `package.json` via `require.resolve('@slack/socket-mode/package.json')` so the test reads the *installed* version, not a hardcoded string. -- Read Bolt's own `package.json` from the repo root (the spec file lives at `test/unit/`, so go up two levels). - -- [ ] **Step 1: Write the failing test** - -Create `test/unit/peer-dependencies.spec.ts`: - -```typescript -import { readFileSync } from 'node:fs'; -import { join } from 'node:path'; -import { assert } from 'chai'; -import semver from 'semver'; - -describe('undici peer dependency', () => { - // Bolt's own package.json (repo root, two levels up from test/unit/). - const boltPkg = JSON.parse(readFileSync(join(__dirname, '..', '..', 'package.json'), 'utf8')) as { - peerDependencies?: Record; - }; - // The installed @slack/socket-mode manifest, resolved from node_modules. - const socketModePkg = JSON.parse(readFileSync(require.resolve('@slack/socket-mode/package.json'), 'utf8')) as { - peerDependencies?: Record; - }; - - it('is declared by Bolt', () => { - assert.isDefined( - boltPkg.peerDependencies?.undici, - 'Bolt must declare undici in peerDependencies so it provides the peer required by @slack/socket-mode', - ); - }); - - it("satisfies @slack/socket-mode's undici peer requirement", () => { - const boltRange = boltPkg.peerDependencies?.undici; - const socketModeRange = socketModePkg.peerDependencies?.undici; - assert.isDefined(socketModeRange, '@slack/socket-mode should declare an undici peer dependency'); - assert.isString(boltRange); - assert.isTrue( - semver.subset(boltRange as string, socketModeRange as string), - `Bolt's undici range "${boltRange}" must be a subset of @slack/socket-mode's "${socketModeRange}". ` + - 'Update peerDependencies.undici in package.json to match.', - ); - }); -}); -``` - -- [ ] **Step 2: Build, then run the test to verify it passes** - -Run: `npm run build && npm run test:unit -- --grep "undici peer dependency"` -Expected: both assertions PASS (Task 1 already added the matching `^7.0.0` range). - -Note: this test is designed to pass once Task 1 is done. To confirm it genuinely guards, do Step 3. - -- [ ] **Step 3: Temporarily prove the guard bites** - -Temporarily edit `package.json` `peerDependencies.undici` to an incompatible range like `"^6.0.0"`, then run: -`npm run build && npm run test:unit -- --grep "undici peer dependency"` -Expected: the second test FAILS with the "must be a subset" message. -Then **revert** the range back to `"^7.0.0"` and re-run to confirm PASS. - -- [ ] **Step 4: Run Biome on the new file** - -Run: `npm run lint` -Expected: no lint errors. If formatting differs, run `npm run lint:fix` and re-check. - -- [ ] **Step 5: Commit** - -```bash -git add test/unit/peer-dependencies.spec.ts -git commit -m "$(cat <<'EOF' -test: guard undici peer range against @slack/socket-mode drift - -Reads the installed @slack/socket-mode peerDependencies.undici at test time -and asserts Bolt's declared range is a semver subset, so the two can't -silently fall out of sync. - -Co-Authored-By: Claude -EOF -)" -``` - ---- - -### Task 3: Add changeset and docs note - -**Files:** -- Create: `.changeset/undici-peer-dependency.md` -- Modify: `docs/english/concepts/socket-mode.md` - -**Interfaces:** -- Consumes: nothing from prior tasks (documentation only). -- Produces: a patch changeset; a consumer-facing install note. - -- [ ] **Step 1: Create the changeset** - -Create `.changeset/undici-peer-dependency.md`: - -```markdown ---- -"@slack/bolt": patch ---- - -Declare `undici` as a peer dependency (`^7.0.0`). `@slack/bolt` constructs a `SocketModeClient` from `@slack/socket-mode@3`, which requires `undici@^7` as a peer. Consumers on strict package managers (Yarn Berry, pnpm) should install `undici` alongside `@slack/bolt`. Resolves #3039. -``` - -- [ ] **Step 2: Add the docs note** - -In `docs/english/concepts/socket-mode.md`, immediately after the opening paragraph (the line ending `be sure to enable it within your app configuration.`), insert a blank line and this note: - -```markdown -> #### Installing `undici` -> -> Socket Mode uses [`undici`](https://www.npmjs.com/package/undici) for its WebSocket connection, declared as a peer dependency. npm installs it automatically, but on strict package managers (Yarn Berry, pnpm) you may need to install it explicitly: -> -> ```shell -> npm install undici -> ``` -``` - -- [ ] **Step 3: Lint docs** - -Run: `npm run lint` -Expected: no errors (Biome checks `docs`). - -- [ ] **Step 4: Commit** - -```bash -git add .changeset/undici-peer-dependency.md docs/english/concepts/socket-mode.md -git commit -m "$(cat <<'EOF' -docs: note undici peer dependency for Socket Mode - -Add changeset and a Socket Mode install note for strict package managers. - -Co-Authored-By: Claude -EOF -)" -``` - ---- - -### Task 4: Full pipeline verification - -**Files:** none (verification only). - -**Interfaces:** -- Consumes: all prior tasks. -- Produces: green `npm test` run. - -- [ ] **Step 1: Run the full test pipeline** - -Run: `npm test` -Expected: build → lint → type tests → unit coverage all PASS, including `undici peer dependency` specs. - -- [ ] **Step 2: If anything fails, fix and re-run** - -Address failures, re-run `npm test` until green. Do not mark complete on a red pipeline. - -- [ ] **Step 3: Push the branch and open a PR (only if the user asks)** - -```bash -git push -u origin feat/undici-peer-dependency -``` -Then open a PR referencing issue #3039. Do not push or open the PR without explicit user go-ahead. - ---- - -## Self-Review - -- **Spec coverage:** - - `package.json` peer + devDeps → Task 1. ✓ - - Drift-guard test → Task 2. ✓ - - Changeset → Task 3 Step 1. ✓ - - Docs note → Task 3 Step 2. ✓ - - `npm test` verification → Task 4. ✓ - - Non-optional peer constraint → Global Constraints + Task 1 (no `peerDependenciesMeta`). ✓ -- **Placeholder scan:** no TBD/TODO; all code and commands are concrete. ✓ -- **Type consistency:** `boltRange`/`socketModeRange` typed and used consistently; `semver.subset(a, b)` argument order (bolt is subset of socket-mode) consistent between the design and the test. ✓ diff --git a/docs/superpowers/specs/2026-08-10-undici-peer-dependency-design.md b/docs/superpowers/specs/2026-08-10-undici-peer-dependency-design.md deleted file mode 100644 index 0b39335eb..000000000 --- a/docs/superpowers/specs/2026-08-10-undici-peer-dependency-design.md +++ /dev/null @@ -1,86 +0,0 @@ -# Undici Peer Dependency Design - -**Issue:** [slackapi/bolt-js#3039](https://github.com/slackapi/bolt-js/issues/3039) - -## Problem - -`@slack/socket-mode@3.0.0` declares `undici@^7.0.0` as a **peer dependency** (v3 -replaced `ws` with `undici`). `@slack/bolt` depends on `@slack/socket-mode@^3.0.0` -and constructs `SocketModeClient` internally (`src/receivers/SocketModeReceiver.ts`), -but Bolt neither declares `undici` as a dependency nor forwards it as a peer. - -As a result, nothing in the ancestor chain formally *provides* the `undici` peer -that socket-mode requires. Strict package managers surface this: - -```text -@slack/bolt@npm:5.0.0 doesn't provide undici to @slack/socket-mode@npm:3.0.0 -``` - -npm 7+ hides the gap by auto-installing peers of transitive deps; Yarn Berry / -pnpm validate strictly and report the unmet peer. - -### Load-time reality - -`undici` is loaded **eagerly by every Bolt app**, not just Socket Mode apps. -Verified at runtime: `require('@slack/bolt')` pulls in `undici`. The chain is -static, top-level `require`s all the way down: - -`src/index.ts` → `import SocketModeReceiver` -→ `import { SocketModeClient } from '@slack/socket-mode'` -→ `require("undici")` at the top of `SocketModeClient.js`. - -There is no lazy/dynamic import in that path, so `undici` is required at -module-load time even for a plain HTTP-receiver app. - -## Decision - -Declare `undici@^7.0.0` in Bolt's `peerDependencies` (**not** marked optional). - -- **Not optional** is technically accurate: without `undici`, `require('@slack/bolt')` - throws `Cannot find module 'undici'` for *all* consumers. -- Matches socket-mode's own range (`^7.0.0`), so any future `7.x` peer bump in - socket-mode stays satisfied automatically. The range would only need touching - on an `undici` **major** bump — which coincides with a `@slack/socket-mode` - major upgrade done deliberately. - -### Trade-off (accepted, on record) - -Because `undici` loads eagerly regardless of receiver, a required (non-optional) -peer means existing v5 consumers on strict package managers who do **not** use -Socket Mode will now see a peer warning / be required to install `undici`, even -for a plain HTTP app. This is a minor behavior change for an already-released -major. Mitigated by the docs note and the drift-guard test below. - -### Sync burden - -There is no package.json mechanism to *inherit* a transitive peer's version -range — a declared range is static, so some duplication is unavoidable. Two -things make drift harmless and self-detecting: - -1. Matching caret range (`^7.0.0`) absorbs all `7.x` peer bumps automatically. -2. A drift-guard unit test fails CI if Bolt's declared range ever becomes - incompatible with socket-mode's actual `peerDependencies.undici`. - -## Scope - -1. **`package.json`** — add `undici: "^7.0.0"` to `peerDependencies`; add - `semver` + `@types/semver` to `devDependencies` for the guard test. -2. **Drift-guard test** — `test/unit/peer-dependencies.spec.ts`. Reads Bolt's - own declared `undici` peer range and the installed - `@slack/socket-mode` `peerDependencies.undici`; asserts Bolt declares - `undici` and that `semver.subset(boltRange, socketModeRange)` is `true`. -3. **Changeset** — patch entry describing the new peer dependency. -4. **Docs** — note in `docs/english/concepts/socket-mode.md` that consumers on - strict package managers must install `undici` alongside `@slack/bolt`. - (Japanese mirror out of scope for this change.) - -## Non-goals - -- Fixing `@slack/socket-mode` itself (different repo) — out of scope here. -- Marking the peer optional — rejected as inaccurate given eager load. -- Adding `undici` as a regular `dependency` — considered; peer chosen instead. - -## Verification - -Full `npm test` pipeline (build → lint → type tests → unit coverage) passes, -including the new guard test. From b2a569dd6caa820f198eca914b3e4775b8b65306 Mon Sep 17 00:00:00 2001 From: William Bergamin Date: Mon, 10 Aug 2026 16:48:55 -0400 Subject: [PATCH 9/9] fix: raise undici peer floor to ^7.28.0 for CVE-2026-12151 The undici peer range permitted versions affected by CVE-2026-12151 (GHSA-vxpw-j846-p89q), a high-severity WebSocket denial-of-service (unbounded fragment count). undici 7.28.0 is the first patched 7.x release. Bump the peer range to ^7.28.0 and refresh the resolved lockfile version accordingly. Co-Authored-By: Claude --- .changeset/undici-peer-dependency.md | 2 +- package-lock.json | 8 ++++---- package.json | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/.changeset/undici-peer-dependency.md b/.changeset/undici-peer-dependency.md index 9fd194769..de5072508 100644 --- a/.changeset/undici-peer-dependency.md +++ b/.changeset/undici-peer-dependency.md @@ -2,4 +2,4 @@ "@slack/bolt": patch --- -Declare `undici` as a peer dependency (`^7.0.0`). `@slack/bolt` constructs a `SocketModeClient` from `@slack/socket-mode@3`, which requires `undici@^7` as a peer. Consumers on strict package managers (Yarn Berry, pnpm) should install `undici` alongside `@slack/bolt`. Resolves #3039. +Declare `undici` as a peer dependency (`^7.28.0`). `@slack/bolt` constructs a `SocketModeClient` from `@slack/socket-mode@3`, which requires `undici@^7` as a peer. Consumers on strict package managers (Yarn Berry, pnpm) should install `undici` alongside `@slack/bolt`. The `^7.28.0` floor avoids `undici` releases affected by CVE-2026-12151 (GHSA-vxpw-j846-p89q), a high-severity WebSocket denial-of-service. Resolves #3039. diff --git a/package-lock.json b/package-lock.json index 258157196..8dc866cf0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -46,7 +46,7 @@ }, "peerDependencies": { "@types/express": "^5.0.0", - "undici": "^7.0.0" + "undici": "^7.28.0" } }, "node_modules/@babel/code-frame": { @@ -4348,9 +4348,9 @@ } }, "node_modules/undici": { - "version": "7.25.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.25.0.tgz", - "integrity": "sha512-xXnp4kTyor2Zq+J1FfPI6Eq3ew5h6Vl0F/8d9XU5zZQf1tX9s2Su1/3PiMmUANFULpmksxkClamIZcaUqryHsQ==", + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", "license": "MIT", "peer": true, "engines": { diff --git a/package.json b/package.json index 912837130..e2b71edcb 100644 --- a/package.json +++ b/package.json @@ -80,6 +80,6 @@ }, "peerDependencies": { "@types/express": "^5.0.0", - "undici": "^7.0.0" + "undici": "^7.28.0" } }