feat!: modernize the toolchain and drop EOL Node versions - #331
Merged
Conversation
Migrate from Yarn 1.22.22 to Yarn 4.18.0 using the node-modules linker. - .yarnrc.yml: node-modules linker, global cache with hardlinks, enableScripts: false, 2-day minimum age gate for npm packages - CI: yarn install --frozen-lockfile -> --immutable (Yarn 1 only flag) - prepare: husky install -> postinstall: husky, as Yarn 4 does not run the root prepare script on install (hooks silently stopped installing) - lint scripts: pass explicit paths instead of **/*.ts, which Yarn 4's portable shell expands recursively into node_modules The lint change surfaced two violations in src/utils/create-token.ts that the previous shallow glob never checked; fixed via eslint --fix.
Upgrade vite 6.4.1 -> 8.2.1 and vitest 3.1.4 -> 4.1.10. No changes to vite.config.ts were required; vitest 4 swaps its internal transform from esbuild to rolldown. Both vite 8 and vitest 4 declare engines that exclude Node 18, and Node 20 reached end-of-life in April 2026, so the test matrix becomes [22, 24, 26] and @types/node moves to ^22.20.1 to match the new floor. Also drop vitest-mock-extended, which was never imported anywhere. BREAKING CHANGE: the minimum supported Node.js version is now 22.12.0. Node 18 (EOL April 2025) and Node 20 (EOL April 2026) are no longer supported.
The lint job set node-version to ${{ matrix.version }}, but that job has
no strategy.matrix, so it resolved to empty and lint ran on whatever Node
the runner shipped. Point it at .nvmrc (v24) and bump setup-node v3 -> v6
to match every other job.
It is only needed to build and type-check this package, not at runtime. BREAKING CHANGE: @types/node is no longer a runtime dependency, so it is no longer installed transitively. The public API exposes Buffer (e.g. verifyWebhook(requestBody: string | Buffer)), so consumers type-checking against those signatures now need @types/node in their own devDependencies.
Replace the deprecated eslint-config-standard-with-typescript, which is pinned to eslint ^8 and @typescript-eslint ^6, with typescript-eslint's own recommendedTypeChecked config. Its official successor eslint-config-love peers eslint ^9.35.0, so it could not reach latest either. - eslint 8.57.1 -> 10.8.1, @typescript-eslint 6.21.0 -> typescript-eslint 8.67.0, eslint-config-prettier 9.1.0 -> 10.1.8 - .eslintrc.js and .eslintignore -> eslint.config.mjs, as eslint 10 removed eslintrc support and the --ext flag - drop eslint-plugin-import, -n and -promise, only needed as peers of the standard config no-explicit-any is off: the generated OpenAPI models, JWT payload types and JSON transport are untyped by design. The no-unsafe-* rules are scoped off for src/gen, src/ApiClient.ts and __tests__, where every occurrence lived, so they stay enforced in hand-written source. The newly enabled rules surfaced 20 real issues, all fixed rather than suppressed: unused catch bindings (typescript-eslint 8 changed the no-unused-vars caughtErrors default from 'none' to 'all'), empty catch blocks, async functions with no await, a dead assignment, a single-argument Object.assign, and 4 eslint-disable directives for rules deleted in typescript-eslint 8.
Vite 8 bundles with rolldown, so this drops rollup from the tree entirely. - vite.config.ts -> vite.config.mts, adding build.lib alongside the existing vitest config; .mts avoids the ESM-in-CJS config loader warning - declarations now come from tsc --emitDeclarationOnly, which keeps type errors failing the build since rolldown only strips types - drop rollup, @rollup/plugin-replace, @rollup/plugin-typescript and tslib, the last only required as a peer of the typescript plugin - raise target to es2022, fully supported by the Node 22.12 floor Output is equivalent to the previous build: identical file names, identical declarations, the same four external imports, the same 12 exports, require() rather than import() in the cjs bundle, and PKG_VERSION still inlined.
Note that npmPublishProvenance only applies to `yarn npm publish`. The release workflow currently runs `npm publish`, which needs npm's own --provenance flag to emit an attestation.
- actions/checkout v6 -> v7 - actions/setup-node v6 -> v7 - googleapis/release-please-action v4 -> v5 - oven-sh/setup-bun v1 -> v2 aslafy-z/conventional-pr-title-action stays on v3, which is a branch already tracking the newest v3 release. The release-please major only changes its runtime to node24; no inputs or config changed.
Node.js floor is now 22.12, the runtime targets are 22, 24 and 26, and the package manager is Yarn 4. Also describe the Vite library-mode build and the flat ESLint config. Unrelated fixes while here: the consumer example pinned a long-stale 0.6.5, and the __tests__ folder name was being rendered as bold "tests", so the project layout is now a code block.
prepublish was the only thing building dist in CI, because Yarn 1 ran it on install. Yarn 4 never runs prepublish, and npm has not run it on publish since npm 5, so after the Yarn 4 upgrade the release job installed and packed without ever building. prepack runs before packing under both npm and Yarn, so the build no longer depends on an install-time side effect.
Yarn 4 supports npm trusted publishing, exchanging the GitHub Actions OIDC token for a registry token, so publishing stays tokenless. This also makes the existing npmPublishProvenance setting effective, as it never applied to `npm publish`. Yarn defaults scoped packages to restricted access, so set publishConfig.access to public explicitly; npm honours the same field.
The unfiltered queryChannels assertion required more than one channel to already exist, but the suite only keeps one alive by that point, so it passed only in apps with leftovers from earlier runs and failed in a clean app. Create the second channel in the test and delete it in a finally block.
The role is created and deleted by separate tests, so any failure between the two leaks it. Apps allow only 25 custom roles, so leaked roles would eventually fail every later run. Clean up in afterAll, tolerating a role that was never created or already removed.
- dotenv 17.3.1 -> 17.4.2 - prettier 3.8.1 -> 3.9.6 - lint-staged 16.3.3 -> 17.3.0 Prettier 3.9 wraps union types differently, which reformats the generated models; the change is cosmetic and is included here so the committed formatting matches the committed prettier version. lint-staged 17 requires Node 22.22.1, above the 22.12.0 engines floor, but it only runs in the pre-commit hook and .nvmrc already pins v24. Its other breaking changes do not apply: git is well past 2.32, and the lint-staged config lives in package.json rather than a YAML file.
postinstall runs when the package is installed as a dependency, and husky is a devDependency consumers do not get, so `npm install` failed with "husky: command not found". Guard on husky being resolvable so the script is a silent no-op outside this repo. prepare was safe here because npm never runs it for registry-installed dependencies, but Yarn 4 does not run prepare on install at all, so postinstall has to stay.
TypeScript 6 rejects moduleResolution=node10 (TS5107, removed in 7.0), so
the two changes go together. Pinned with ~ because typescript-eslint peers
typescript <6.1.0; TypeScript 7 is not usable until that ceiling lifts.
Declaration emit now references the package root as import("..") rather
than import("./gen/models"). Verified against the packed tarball from a
consumer project: type-checks under bundler, node16 and nodenext, and both
bundles are byte-identical.
szuperaz
approved these changes
Aug 17, 2026
oliverlaz
pushed a commit
that referenced
this pull request
Aug 18, 2026
🤖 I have created a release *beep* *boop* --- ## [0.8.0](v0.7.64...v0.8.0) (2026-08-18) ### ⚠ BREAKING CHANGES * minimum supported Node.js is now 22.12.0. Node 18 went EOL in April 2025, Node 20 in April 2026. ### Features * modernize the toolchain and drop EOL Node versions ([#331](#331)) ([dbabec0](dbabec0)) --- This PR was generated with [Release Please](https://github.com/googleapis/release-please). See [documentation](https://github.com/googleapis/release-please#release-please). Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Overview
Modernizes the build and test toolchain, and fixes CI, which has been red on
mainsince June.Yarn 1 → 4, Rollup → Vite 8 library mode, Vitest 3 → 4, ESLint 8 → 10 with typescript-eslint 8, TS target ES2020 → ES2022, and the GitHub Actions bumped to their latest majors. Node matrix is 22/24/26 now and
engines.nodeis>=22.12.0, since 18 and 20 are both EOL.Rollup drops out of the tree entirely — Vite 8 bundles with rolldown.
eslint-config-standard-with-typescripthad to go too, since it's deprecated and pinned to ESLint 8; the config uses typescript-eslint'srecommendedTypeCheckedinstead, which also madeeslint-plugin-import,-nand-promiseunnecessary.I diffed the built output against the old Rollup build to be sure nothing moved: same filenames, byte-identical
.d.ts, same externals and exports,PKG_VERSIONstill inlined. Declarations come fromtsc --emitDeclarationOnly, which also keeps type errors failing the build.BREAKING CHANGE: minimum supported Node.js is now 22.12.0. Node 18 went EOL in April 2025, Node 20 in April 2026.
BREAKING CHANGE:
@types/nodemoved to devDependencies, so it's no longer installed transitively. The public API exposesBuffer(e.g.verifyWebhook(requestBody: string | Buffer)), so consumers type-checking against those signatures now need@types/nodethemselves.