From 3ebeafbf0bdd886d33e1a9f318d7e50b46702307 Mon Sep 17 00:00:00 2001 From: Thomas <28439359+thoda-dev@users.noreply.github.com> Date: Wed, 7 Oct 2026 14:35:51 +0200 Subject: [PATCH 1/7] Test on every Node.js version allowed by engines Update checkout and setup-node, whose Node.js 12/16 runtimes no longer run on GitHub Actions. Move development and the lint job to Node.js 24 LTS, as Node.js 20 reached end of life. Keep the lint job name, it is the required check on main. --- .github/workflows/test.yml | 45 +++++++++++++++++++++++++++++++++++--- .tool-versions | 2 +- 2 files changed, 43 insertions(+), 4 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 8300389..7d337b1 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -5,15 +5,54 @@ on: branches: [main] pull_request: +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: + # The job name is the required status check on main, keep it unchanged. lint: runs-on: ubuntu-latest + timeout-minutes: 15 steps: - - uses: actions/checkout@v2 - - uses: actions/setup-node@v3 + - uses: actions/checkout@v7 + with: + persist-credentials: false + - uses: actions/setup-node@v7 with: - node-version: 20.20.2 + node-version-file: .tool-versions cache: 'yarn' - run: yarn install --frozen-lockfile - run: yarn lint - run: yarn test + + test: + name: test (Node.js ${{ matrix.node-version }}) + runs-on: ubuntu-latest + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + node-version: [20, 22, 24, 26] + include: + # Lowest versions allowed by "engines" in package.json. The library runs + # on them, but some dev dependencies (nodemailer 10, node-releases) require a + # newer Node.js, so their engine checks are skipped. + - node-version: 16 + install-flags: --ignore-engines + - node-version: 18 + install-flags: --ignore-engines + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - uses: actions/setup-node@v7 + with: + node-version: ${{ matrix.node-version }} + cache: 'yarn' + # Also builds dist/ through the "prepare" script. + - run: yarn install --frozen-lockfile ${{ matrix.install-flags }} + - run: yarn test diff --git a/.tool-versions b/.tool-versions index 39847f7..fed849c 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -nodejs 20.20.2 +nodejs 24.21.0 yarn 1.22.17 From 1f54f8d16f7b25a919418231aafd165d859d1dbc Mon Sep 17 00:00:00 2001 From: Thomas <28439359+thoda-dev@users.noreply.github.com> Date: Wed, 7 Oct 2026 14:35:51 +0200 Subject: [PATCH 2/7] Update CodeQL workflow to codeql-action v4 codeql-action v3 is deprecated. Drop the template comments and the unused manual build step. The job name stays the same. --- .github/workflows/codeql.yml | 67 ++++++------------------------------ 1 file changed, 11 insertions(+), 56 deletions(-) diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index 6f2e699..e516ed0 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -1,14 +1,3 @@ -# For most projects, this workflow file will not need changing; you simply need -# to commit it to your repository. -# -# You may wish to alter this file to override the set of languages analyzed, -# or to provide custom queries or build logic. -# -# ******** NOTE ******** -# We have attempted to detect the languages in your repository. Please check -# the `language` matrix defined below to confirm you have the correct set of -# supported CodeQL languages. -# name: "CodeQL" on: @@ -19,23 +8,18 @@ on: schedule: - cron: "29 19 * * 6" +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} + jobs: analyze: + # The job name is a status check on main, keep it unchanged. name: Analyze (${{ matrix.language }}) - # Runner size impacts CodeQL analysis time. To learn more, please see: - # - https://gh.io/recommended-hardware-resources-for-running-codeql - # - https://gh.io/supported-runners-and-hardware-resources - # - https://gh.io/using-larger-runners (GitHub.com only) - # Consider using larger runners or machines with greater resources for possible analysis time improvements. - runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} + runs-on: ubuntu-latest permissions: - # required for all workflows security-events: write - - # required to fetch internal or private CodeQL packs packages: read - - # only required for workflows in private repositories actions: read contents: read @@ -45,49 +29,20 @@ jobs: include: - language: javascript-typescript build-mode: none - # CodeQL supports the following values keywords for 'language': 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' - # Use `c-cpp` to analyze code written in C, C++ or both - # Use 'java-kotlin' to analyze code written in Java, Kotlin or both - # Use 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both - # To learn more about changing the languages that are analyzed or customizing the build mode for your analysis, - # see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/customizing-your-advanced-setup-for-code-scanning. - # If you are analyzing a compiled language, you can modify the 'build-mode' for that language to customize how - # your codebase is analyzed, see https://docs.github.com/en/code-security/code-scanning/creating-an-advanced-setup-for-code-scanning/codeql-code-scanning-for-compiled-languages steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@v7 + with: + persist-credentials: false - # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL - uses: github/codeql-action/init@v3 + uses: github/codeql-action/init@v4 with: languages: ${{ matrix.language }} build-mode: ${{ matrix.build-mode }} config-file: ./.github/codeql/codeql-config.yml - # If you wish to specify custom queries, you can do so here or in a config file. - # By default, queries listed here will override any specified in a config file. - # Prefix the list here with "+" to use these queries and those in the config file. - - # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs - # queries: security-extended,security-and-quality - - # If the analyze step fails for one of the languages you are analyzing with - # "We were unable to automatically build your code", modify the matrix above - # to set the build mode to "manual" for that language. Then modify this step - # to build your code. - # â„šī¸ Command-line programs to run using the OS shell. - # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun - - if: matrix.build-mode == 'manual' - shell: bash - run: | - echo 'If you are using a "manual" build mode for one or more of the' \ - 'languages you are analyzing, replace this with the commands to build' \ - 'your code, for example:' - echo ' make bootstrap' - echo ' make release' - exit 1 - name: Perform CodeQL Analysis - uses: github/codeql-action/analyze@v3 + uses: github/codeql-action/analyze@v4 with: category: "/language:${{matrix.language}}" From bb4c78ea21783ee0917a9b846158311d90b3c1ae Mon Sep 17 00:00:00 2001 From: Thomas <28439359+thoda-dev@users.noreply.github.com> Date: Wed, 7 Oct 2026 14:35:51 +0200 Subject: [PATCH 3/7] Update actions in the draft release workflow Pass the tag to scripts through env instead of interpolating it in the script body, like the other steps already do. --- .github/workflows/draft-release.yml | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/.github/workflows/draft-release.yml b/.github/workflows/draft-release.yml index bcf49f5..f13b81b 100644 --- a/.github/workflows/draft-release.yml +++ b/.github/workflows/draft-release.yml @@ -28,18 +28,18 @@ jobs: name: Draft mailtrap-nodejs Release runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: fetch-depth: 0 ref: main - - uses: actions/setup-node@v4 + - uses: actions/setup-node@v7 with: node-version-file: .tool-versions - name: Compute next version id: bump - uses: actions/github-script@v7 + uses: actions/github-script@v9 with: script: | const fs = require('fs'); @@ -59,10 +59,12 @@ jobs: core.setOutput('branch', `release-${tag}`); - name: Abort if tag already exists - uses: actions/github-script@v7 + uses: actions/github-script@v9 + env: + TAG: ${{ steps.bump.outputs.tag }} with: script: | - const tag = '${{ steps.bump.outputs.tag }}'; + const tag = process.env.TAG; try { await github.rest.git.getRef({ owner: context.repo.owner, @@ -76,10 +78,12 @@ jobs: - name: Generate release notes, abort if nothing to release id: notes - uses: actions/github-script@v7 + uses: actions/github-script@v9 + env: + TAG: ${{ steps.bump.outputs.tag }} with: script: | - const tag = '${{ steps.bump.outputs.tag }}'; + const tag = process.env.TAG; // Detect the previous release tag so notes only cover PRs since then. const latest = await github.rest.repos.getLatestRelease({ @@ -119,7 +123,7 @@ jobs: - name: Prepend new section to CHANGELOG.md if: steps.notes.outputs.release_notes != '' - uses: actions/github-script@v7 + uses: actions/github-script@v9 env: NEXT: ${{ steps.bump.outputs.next }} RELEASE_NOTES: ${{ steps.notes.outputs.release_notes }} @@ -163,12 +167,13 @@ jobs: - name: Create draft GitHub release if: steps.notes.outputs.release_notes != '' - uses: actions/github-script@v7 + uses: actions/github-script@v9 env: + TAG: ${{ steps.bump.outputs.tag }} RELEASE_NOTES: ${{ steps.notes.outputs.release_notes }} with: script: | - const tag = '${{ steps.bump.outputs.tag }}'; + const tag = process.env.TAG; const { data } = await github.rest.repos.createRelease({ owner: context.repo.owner, repo: context.repo.repo, From aa080306c8f24de4bb8f6adfddefd66218a8413d Mon Sep 17 00:00:00 2001 From: Thomas <28439359+thoda-dev@users.noreply.github.com> Date: Wed, 7 Oct 2026 14:49:25 +0200 Subject: [PATCH 4/7] Require Node.js 22 or newer --- .github/workflows/test.yml | 12 ++---------- package.json | 2 +- 2 files changed, 3 insertions(+), 11 deletions(-) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 7d337b1..e52dfd5 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -36,15 +36,7 @@ jobs: strategy: fail-fast: false matrix: - node-version: [20, 22, 24, 26] - include: - # Lowest versions allowed by "engines" in package.json. The library runs - # on them, but some dev dependencies (nodemailer 10, node-releases) require a - # newer Node.js, so their engine checks are skipped. - - node-version: 16 - install-flags: --ignore-engines - - node-version: 18 - install-flags: --ignore-engines + node-version: [22, 24, 26] steps: - uses: actions/checkout@v7 with: @@ -54,5 +46,5 @@ jobs: node-version: ${{ matrix.node-version }} cache: 'yarn' # Also builds dist/ through the "prepare" script. - - run: yarn install --frozen-lockfile ${{ matrix.install-flags }} + - run: yarn install --frozen-lockfile - run: yarn test diff --git a/package.json b/package.json index 24e99f7..f816a48 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "typescript": "^5.0.3" }, "engines": { - "node": ">=16.20.1", + "node": ">=22", "yarn": ">=1.22.17" }, "files": [ From afcb8c6e2933f6238106e02d4d64b8ee26c838c4 Mon Sep 17 00:00:00 2001 From: Thomas <28439359+thoda-dev@users.noreply.github.com> Date: Wed, 7 Oct 2026 14:56:01 +0200 Subject: [PATCH 5/7] Test against nodemailer 9 and 10 --- .github/workflows/test.yml | 50 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index e52dfd5..f261fb3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -48,3 +48,53 @@ jobs: # Also builds dist/ through the "prepare" script. - run: yarn install --frozen-lockfile - run: yarn test + + # Checks every nodemailer major allowed by peerDependencies, from the sources + # and from a consumer project using the packed build. + nodemailer: + name: nodemailer ${{ matrix.nodemailer }} + runs-on: ubuntu-latest + timeout-minutes: 15 + strategy: + fail-fast: false + matrix: + include: + - nodemailer: 9 + types: '@types/nodemailer@^6' + - nodemailer: 10 + steps: + - uses: actions/checkout@v7 + with: + persist-credentials: false + - uses: actions/setup-node@v7 + with: + node-version-file: .tool-versions + cache: 'yarn' + - run: yarn install --frozen-lockfile + - run: yarn add --dev "nodemailer@^${{ matrix.nodemailer }}" + - run: yarn tsc -p tsconfig.build.json --noEmit + - run: yarn test + - name: Set up consumer project + run: | + yarn pack --filename "$RUNNER_TEMP/mailtrap.tgz" + mkdir "$RUNNER_TEMP/consumer" + cd "$RUNNER_TEMP/consumer" + echo '{ "private": true }' > package.json + yarn add "$RUNNER_TEMP/mailtrap.tgz" "nodemailer@^${{ matrix.nodemailer }}" ${{ matrix.types }} typescript@^5 @types/node@^22 + cat > index.ts <<'EOF' + import { createTransport } from "nodemailer"; + import { MailtrapClient, MailtrapTransport } from "mailtrap"; + + const client = new MailtrapClient({ token: "token" }); + const transport = createTransport(MailtrapTransport({ token: "token" })); + + transport.sendMail({ from: "a@example.com", to: "b@example.com", subject: "s", text: "t", category: "c" }); + client.send({ from: { email: "a@example.com" }, to: [{ email: "b@example.com" }], subject: "s", text: "t" }); + EOF + cp index.ts index.mts + - name: Type-check a CommonJS consumer without esModuleInterop + working-directory: ${{ runner.temp }}/consumer + run: yarn tsc --noEmit --strict --skipLibCheck --module commonjs --moduleResolution node10 --types node index.ts + - name: Type-check a native ESM consumer + working-directory: ${{ runner.temp }}/consumer + run: yarn tsc --noEmit --strict --skipLibCheck --module nodenext --moduleResolution nodenext --types node index.mts From 68c3dd5eace6586668e916c25dca774707711556 Mon Sep 17 00:00:00 2001 From: Thomas <28439359+thoda-dev@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:00:11 +0200 Subject: [PATCH 6/7] Document the Node.js 22 requirement in README --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 67f64af..0928f56 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ To get the most out of this official Mailtrap.io Node.js SDK: - [Verify your domain](https://mailtrap.io/sending/domains) +- Use Node.js 22 or newer + ## Installation You can install the package via [npm](https://www.npmjs.com/) or [yarn](https://yarnpkg.com/): @@ -198,7 +200,7 @@ mailtrap ## Nodemailer Transport -> NOTE: [Nodemailer](https://www.npmjs.com/package/nodemailer) is needed as a dependency. Versions 9 and 10 are supported (Nodemailer 10 requires Node.js 20 or newer). +> NOTE: [Nodemailer](https://www.npmjs.com/package/nodemailer) is needed as a dependency. Versions 9 and 10 are supported. ```sh npm install nodemailer From 0ef3fde4c1369344a602aa15321ea5359a99e20c Mon Sep 17 00:00:00 2001 From: Thomas <28439359+thoda-dev@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:15:53 +0200 Subject: [PATCH 7/7] Type-check against Node.js 22 types --- package.json | 2 +- yarn.lock | 15 +++++++++++---- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/package.json b/package.json index f816a48..ddd3c97 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "@babel/preset-typescript": "^7.18.6", "@jest/globals": "^29.3.1", "@types/jest": "^29.5.3", - "@types/node": "^18.15.11", + "@types/node": "^22.20.5", "@types/nodemailer": "^6.4.9", "@types/qs": "^6.15.0", "@typescript-eslint/eslint-plugin": "^5.57.1", diff --git a/yarn.lock b/yarn.lock index b615ffd..169f490 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1465,10 +1465,12 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.15.tgz#de0e1fbd2b22b962d45971431e2ae696643d3f5d" integrity sha512-VkhBbVo2+2oozlkdHXLrb3zjsRkpdnaU2bXmX8Wgle3PUi569eLRaHGlgETQHR7lLL1w7GiG3h9SnePhxNDecw== -"@types/node@^18.15.11": - version "18.15.11" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.15.11.tgz#b3b790f09cb1696cffcec605de025b088fa4225f" - integrity sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q== +"@types/node@^22.20.5": + version "22.20.5" + resolved "https://registry.yarnpkg.com/@types/node/-/node-22.20.5.tgz#afa7bde6da81b8b83ea4e2f020487e9b148c11fd" + integrity sha512-U2+DNr+wSjpsTS/wZGYHq7GcwfuSmKiKvoPvK22zwTlRhU91yOniN4qRR5KhIjvif7ysw/dz/hKmfDH0Ris4aA== + dependencies: + undici-types "~6.21.0" "@types/nodemailer@^6.4.9": version "6.4.9" @@ -4893,6 +4895,11 @@ unbox-primitive@^1.0.2: has-symbols "^1.0.3" which-boxed-primitive "^1.0.2" +undici-types@~6.21.0: + version "6.21.0" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-6.21.0.tgz#691d00af3909be93a7faa13be61b3a5b50ef12cb" + integrity sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ== + unicode-canonical-property-names-ecmascript@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unicode-canonical-property-names-ecmascript/-/unicode-canonical-property-names-ecmascript-2.0.0.tgz#301acdc525631670d39f6146e0e77ff6bbdebddc"