Skip to content

Require Node 20 - #3719

Merged
jodeleeuw merged 1 commit into
mainfrom
chore/drop-node-18
Sep 15, 2026
Merged

jodeleeuw merged 1 commit into
mainfrom
chore/drop-node-18

Conversation

@jodeleeuw

Copy link
Copy Markdown
Member

Node 18 reached end of life in April 2025. This raises jsPsych's floor to Node 20 and moves the test matrix from [18, 20] to [20, 22].

Why now

The immediate reason is concrete rather than housekeeping. Every subpackage of the Firebase v12 SDK declares node >= 20, so @jspsych/extension-pipe — which reaches Firebase through datapipe-client — cannot be installed in a repository that still claims Node 18 support. The 18 leg of the matrix fails at install:

npm error code EBADENGINE
npm error engine Not compatible with your version of node/npm: @firebase/ai@2.16.0
npm error notsup Required: {"node":">=20.0.0"}
npm error notsup Actual:   {"npm":"10.8.2","node":"v18.20.8"}

Narrowing the dependency does not avoid it — @firebase/app and @firebase/database declare the same floor as the umbrella package, so importing only the two modules the client uses makes no difference.

The alternative, and why not

Pinning datapipe-client back to Firebase v11, whose subpackages still allow Node 18, works — I verified it: 48 tests pass and the bundle grows by 2 KB. It was rejected because a repository's supported runtime is a more honest place to make this decision than a transitive dependency of one extension, and because the pin would have to be carried until jsPsych raised its floor anyway.

What changes

  • Root package.json: node >=18.0.0 → >=20.0.0, npm >=9.0.0 → >=10.0.0 (npm 10 is what Node 20 ships).
  • packages/config/package.json: same. These were the only two packages declaring engines.
  • .github/workflows/build.yml: node: [18, 20] → [20, 22]. Dropping 18 without adding 22 would leave a single version under test; every other workflow already pins 20.

Two judgement calls worth a look

The changeset is minor, not major. Raising a runtime floor is breaking in the strict sense, but @jspsych/config is a build-time dependency of jsPsych's own packages that never reaches an experiment, and a major would churn every package in the monorepo for an advisory field. Say the word if you would rather it be major.

Adding 22 to the matrix is my initiative, not strictly "drop 18". If it surfaces unrelated failures I am happy to make it [20] alone.

Unrelated but noticed: root packageManager is pinned to npm@8.3.1, which contradicts the npm >=9 that was already declared. Left alone.

🤖 Generated with Claude Code

https://claude.ai/code/session_01SgDfss2zhjwMky2mBKbcBX

Node 18 reached end of life in April 2025, and the ecosystem has moved
past it. The immediate reason is concrete: every subpackage of the
Firebase v12 SDK declares node >= 20, so @jspsych/extension-pipe -- which
reaches it through datapipe-client -- cannot be installed in a repository
that still claims Node 18 support. `npm ci` fails with EBADENGINE on the
18 leg of the matrix before a single test runs.

Narrowing the dependency does not help: @firebase/app and
@firebase/database declare the same floor as the umbrella package.
Pinning back to Firebase v11, whose subpackages still allow 18, was the
alternative -- rejected in favour of this, since a repository's supported
runtime is the more honest place to make the decision than a transitive
dependency of one extension.

Changes the root and @jspsych/config engines fields, which were the only
two declaring one, and moves the test matrix from [18, 20] to [20, 22] so
two supported versions are still covered rather than one.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SgDfss2zhjwMky2mBKbcBX
@changeset-bot

changeset-bot Bot commented Sep 15, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 7a2bee7

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@jspsych/config Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@github-actions

Copy link
Copy Markdown
Contributor

📦 Preview build ready

Built from PR head 7a2bee7 and published at f81aa9c on branch preview/pr-3719.
URLs below are pinned to an immutable commit SHA, so they are safe to share and are cached permanently by jsDelivr.

Changed packages: config

Quick-start HTML:

<script src="https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/jspsych/dist/index.browser.min.js"></script>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/jspsych/css/jspsych.css">
<script src="https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-html-keyboard-response/dist/index.browser.min.js"></script>
All package URLs
  • @jspsych/extension-mouse-tracking → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/extension-mouse-tracking/dist/index.browser.min.js
  • @jspsych/extension-record-video → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/extension-record-video/dist/index.browser.min.js
  • @jspsych/extension-webgazer → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/extension-webgazer/dist/index.browser.min.js
  • jspsych → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/jspsych/dist/index.browser.min.js
  • @jspsych/plugin-animation → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-animation/dist/index.browser.min.js
  • @jspsych/plugin-audio-button-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-audio-button-response/dist/index.browser.min.js
  • @jspsych/plugin-audio-keyboard-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-audio-keyboard-response/dist/index.browser.min.js
  • @jspsych/plugin-audio-slider-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-audio-slider-response/dist/index.browser.min.js
  • @jspsych/plugin-browser-check → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-browser-check/dist/index.browser.min.js
  • @jspsych/plugin-call-function → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-call-function/dist/index.browser.min.js
  • @jspsych/plugin-canvas-button-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-canvas-button-response/dist/index.browser.min.js
  • @jspsych/plugin-canvas-keyboard-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-canvas-keyboard-response/dist/index.browser.min.js
  • @jspsych/plugin-canvas-slider-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-canvas-slider-response/dist/index.browser.min.js
  • @jspsych/plugin-categorize-animation → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-categorize-animation/dist/index.browser.min.js
  • @jspsych/plugin-categorize-html → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-categorize-html/dist/index.browser.min.js
  • @jspsych/plugin-categorize-image → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-categorize-image/dist/index.browser.min.js
  • @jspsych/plugin-cloze → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-cloze/dist/index.browser.min.js
  • @jspsych/plugin-external-html → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-external-html/dist/index.browser.min.js
  • @jspsych/plugin-free-sort → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-free-sort/dist/index.browser.min.js
  • @jspsych/plugin-fullscreen → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-fullscreen/dist/index.browser.min.js
  • @jspsych/plugin-html-audio-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-html-audio-response/dist/index.browser.min.js
  • @jspsych/plugin-html-button-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-html-button-response/dist/index.browser.min.js
  • @jspsych/plugin-html-keyboard-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-html-keyboard-response/dist/index.browser.min.js
  • @jspsych/plugin-html-slider-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-html-slider-response/dist/index.browser.min.js
  • @jspsych/plugin-html-video-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-html-video-response/dist/index.browser.min.js
  • @jspsych/plugin-iat-html → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-iat-html/dist/index.browser.min.js
  • @jspsych/plugin-iat-image → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-iat-image/dist/index.browser.min.js
  • @jspsych/plugin-image-button-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-image-button-response/dist/index.browser.min.js
  • @jspsych/plugin-image-keyboard-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-image-keyboard-response/dist/index.browser.min.js
  • @jspsych/plugin-image-slider-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-image-slider-response/dist/index.browser.min.js
  • @jspsych/plugin-initialize-camera → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-initialize-camera/dist/index.browser.min.js
  • @jspsych/plugin-initialize-microphone → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-initialize-microphone/dist/index.browser.min.js
  • @jspsych/plugin-instructions → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-instructions/dist/index.browser.min.js
  • @jspsych/plugin-maxdiff → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-maxdiff/dist/index.browser.min.js
  • @jspsych/plugin-mirror-camera → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-mirror-camera/dist/index.browser.min.js
  • @jspsych/plugin-preload → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-preload/dist/index.browser.min.js
  • @jspsych/plugin-reconstruction → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-reconstruction/dist/index.browser.min.js
  • @jspsych/plugin-resize → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-resize/dist/index.browser.min.js
  • @jspsych/plugin-same-different-html → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-same-different-html/dist/index.browser.min.js
  • @jspsych/plugin-same-different-image → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-same-different-image/dist/index.browser.min.js
  • @jspsych/plugin-serial-reaction-time-mouse → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-serial-reaction-time-mouse/dist/index.browser.min.js
  • @jspsych/plugin-serial-reaction-time → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-serial-reaction-time/dist/index.browser.min.js
  • @jspsych/plugin-sketchpad → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-sketchpad/dist/index.browser.min.js
  • @jspsych/plugin-survey-html-form → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-survey-html-form/dist/index.browser.min.js
  • @jspsych/plugin-survey-likert → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-survey-likert/dist/index.browser.min.js
  • @jspsych/plugin-survey-multi-choice → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-survey-multi-choice/dist/index.browser.min.js
  • @jspsych/plugin-survey-multi-select → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-survey-multi-select/dist/index.browser.min.js
  • @jspsych/plugin-survey-text → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-survey-text/dist/index.browser.min.js
  • @jspsych/plugin-survey → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-survey/dist/index.browser.min.js
  • @jspsych/plugin-video-button-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-video-button-response/dist/index.browser.min.js
  • @jspsych/plugin-video-keyboard-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-video-keyboard-response/dist/index.browser.min.js
  • @jspsych/plugin-video-slider-response → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-video-slider-response/dist/index.browser.min.js
  • @jspsych/plugin-virtual-chinrest → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-virtual-chinrest/dist/index.browser.min.js
  • @jspsych/plugin-visual-search-circle → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-visual-search-circle/dist/index.browser.min.js
  • @jspsych/plugin-webgazer-calibrate → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-webgazer-calibrate/dist/index.browser.min.js
  • @jspsych/plugin-webgazer-init-camera → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-webgazer-init-camera/dist/index.browser.min.js
  • @jspsych/plugin-webgazer-validate → https://cdn.jsdelivr.net/gh/jspsych/jsPsych@f81aa9ca3b738012412f4a8cbd91e17b4a26c987/packages/plugin-webgazer-validate/dist/index.browser.min.js

Last updated 2026-09-15 20:10 UTC for PR head 7a2bee7.

@jodeleeuw
jodeleeuw merged commit d4f33be into main Sep 15, 2026
7 checks passed
@jodeleeuw
jodeleeuw deleted the chore/drop-node-18 branch September 15, 2026 20:16
@github-actions github-actions Bot mentioned this pull request Sep 15, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant