Merge leon/node-updates: upgrade to Node 24 with all compatibility changes

Combines node-version-update (Node 22, babel.config.js importAttributesKeyword)
with leon/node-updates (Node 24, Webpack 5 native workers, WASM fetch polyfill,
dependency updates). Conflicts resolved in favour of Node 24 throughout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Leon Zandman 2026-03-22 19:35:34 +01:00
commit d4d49b438e
22 changed files with 2189 additions and 2303 deletions

View File

@ -3,7 +3,7 @@
{
"name": "CyberChef",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/javascript-node:22-trixie",
"image": "mcr.microsoft.com/devcontainers/javascript-node:24-trixie",
// Features to add to the dev container. More info: https://containers.dev/features.
"features": {

View File

@ -21,7 +21,7 @@ jobs:
- name: Set node version
uses: actions/setup-node@v6
with:
node-version: '20.x'
node-version: 24
registry-url: "https://registry.npmjs.org"
- name: Install

View File

@ -17,7 +17,7 @@ jobs:
- name: Set node version
uses: actions/setup-node@v6
with:
node-version: '20.x'
node-version: 24
registry-url: "https://registry.npmjs.org"
- name: Install

View File

@ -27,7 +27,7 @@ jobs:
- name: Set node version
uses: actions/setup-node@v6
with:
node-version: '20.x'
node-version: 24
registry-url: "https://registry.npmjs.org"
- name: Install
@ -107,7 +107,7 @@ jobs:
- name: Set node version
uses: actions/setup-node@v6
with:
node-version: 18
node-version: 24
registry-url: "https://registry.npmjs.org"
- name: Install
@ -116,11 +116,5 @@ jobs:
- name: Create machine generated files
run: npm run node
- name: Reset node version ready for publish
uses: actions/setup-node@v6
with:
node-version: ^24.5
registry-url: "https://registry.npmjs.org"
- name: Publish to NPM
run: npm publish

2
.nvmrc
View File

@ -1 +1 @@
22
24

View File

@ -4,7 +4,7 @@
# Modifier --platform=$BUILDPLATFORM limits the platform to "BUILDPLATFORM" during buildx multi-platform builds
# This is because npm "chromedriver" package is not compatiable with all platforms
# For more info see: https://docs.docker.com/build/building/multi-platform/#cross-compilation
FROM --platform=$BUILDPLATFORM node:22-alpine AS builder
FROM --platform=$BUILDPLATFORM node:24-alpine AS builder
WORKDIR /app

View File

@ -6,7 +6,7 @@ const BundleAnalyzerPlugin = require("webpack-bundle-analyzer").BundleAnalyzerPl
const glob = require("glob");
const path = require("path");
const nodeFlags = "--experimental-modules --experimental-json-modules --experimental-specifier-resolution=node --no-warnings --no-deprecation";
const nodeFlags = "--no-warnings --no-deprecation";
/**
* Grunt configuration for building the app in various formats.

View File

@ -120,7 +120,7 @@ CyberChef is built to support
## Node.js support
CyberChef is built to fully support Node.js `v22`. For more information, see the ["Node API" wiki page](https://github.com/gchq/CyberChef/wiki/Node-API)
CyberChef is built to fully support Node.js `v24`. For more information, see the ["Node API" wiki page](https://github.com/gchq/CyberChef/wiki/Node-API)
## Contributing

4361
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -36,7 +36,7 @@
"browserslist": [
"Chrome >= 50",
"Firefox >= 38",
"node >= 22"
"node >= 24"
],
"devDependencies": {
"@babel/eslint-parser": "^7.28.6",
@ -52,17 +52,17 @@
"autoprefixer": "^10.4.27",
"babel-loader": "^10.1.1",
"base64-loader": "^1.0.0",
"chromedriver": "^130.0.4",
"chromedriver": "^146.0.5",
"cli-progress": "^3.12.0",
"colors": "^1.4.0",
"compression-webpack-plugin": "^11.1.0",
"copy-webpack-plugin": "^13.0.1",
"compression-webpack-plugin": "^12.0.0",
"copy-webpack-plugin": "^14.0.0",
"core-js": "^3.49.0",
"cspell": "^8.19.4",
"css-loader": "7.1.4",
"cspell": "^9.7.0",
"css-loader": "^7.1.4",
"eslint": "^9.39.4",
"eslint-plugin-jsdoc": "^50.8.0",
"globals": "^15.15.0",
"globals": "^17.4.0",
"grunt": "^1.6.1",
"grunt-chmod": "~1.1.1",
"grunt-concurrent": "^3.0.0",
@ -76,7 +76,7 @@
"grunt-zip": "^1.0.0",
"html-webpack-plugin": "^5.6.6",
"imports-loader": "^5.0.0",
"mini-css-extract-plugin": "2.10.1",
"mini-css-extract-plugin": "^2.10.1",
"modify-source-webpack-plugin": "^4.1.0",
"nightwatch": "^3.15.0",
"postcss": "^8.5.8",
@ -84,13 +84,12 @@
"postcss-import": "^16.1.1",
"postcss-loader": "^8.2.1",
"prompt": "^1.3.0",
"sitemap": "^8.0.3",
"sitemap": "^9.0.1",
"terser": "^5.46.1",
"webpack": "^5.105.4",
"webpack-bundle-analyzer": "^4.10.2",
"webpack-dev-server": "5.0.4",
"webpack-node-externals": "^3.0.0",
"worker-loader": "^3.0.8"
"webpack-bundle-analyzer": "^5.0.0",
"webpack-dev-server": "^5.0.4",
"webpack-node-externals": "^3.0.0"
},
"dependencies": {
"@alexaltea/capstone-js": "^3.0.5",
@ -136,7 +135,7 @@
"highlight.js": "^11.11.1",
"ieee754": "^1.2.1",
"jimp": "^1.6.0",
"jq-web": "^0.5.1",
"jq-web": "^0.6.2",
"jquery": "3.7.1",
"js-sha3": "^0.9.3",
"jsesc": "^3.1.0",
@ -164,7 +163,7 @@
"notepack.io": "^3.0.1",
"ntlm": "^0.1.3",
"nwmatcher": "^1.4.4",
"otpauth": "9.3.6",
"otpauth": "^9.5.0",
"path": "^0.12.7",
"popper.js": "^1.16.1",
"process": "^0.11.10",
@ -194,18 +193,21 @@
"start": "npx grunt dev",
"build": "npx grunt prod",
"node": "npx grunt node",
"repl": "node --experimental-modules --experimental-json-modules --experimental-specifier-resolution=node --no-experimental-fetch --no-warnings src/node/repl.mjs",
"test": "npx grunt configTests && node --experimental-modules --experimental-json-modules --no-warnings --no-deprecation --openssl-legacy-provider --no-experimental-fetch tests/node/index.mjs && node --experimental-modules --experimental-json-modules --no-warnings --no-deprecation --openssl-legacy-provider --no-experimental-fetch --trace-uncaught tests/operations/index.mjs",
"repl": "node --no-warnings src/node/repl.mjs",
"test": "npx grunt configTests && node --no-warnings --no-deprecation --openssl-legacy-provider tests/node/index.mjs && node --no-warnings --no-deprecation --openssl-legacy-provider --trace-uncaught tests/operations/index.mjs",
"testnodeconsumer": "npx grunt testnodeconsumer",
"testui": "npx grunt testui",
"testuidev": "npx nightwatch --env=dev",
"lint": "npx grunt lint",
"lint:grammar": "cspell ./src",
"postinstall": "npx grunt exec:fixCryptoApiImports && npx grunt exec:fixSnackbarMarkup",
"newop": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newOperation.mjs",
"minor": "node --experimental-modules --experimental-json-modules src/core/config/scripts/newMinorVersion.mjs && npm version minor --git-tag-version=false && echo \"Updated to version v$(npm pkg get version | xargs), please create a pull request and once merged use 'npm run tag'\"",
"newop": "node src/core/config/scripts/newOperation.mjs",
"minor": "node src/core/config/scripts/newMinorVersion.mjs && npm version minor --git-tag-version=false && echo \"Updated to version v$(npm pkg get version | xargs), please create a pull request and once merged use 'npm run tag'\"",
"tag": "git tag -s \"v$(npm pkg get version | xargs)\" -m \"$(npm pkg get version | xargs)\" && echo \"Created v$(npm pkg get version | xargs), now check and push the tag\"",
"getheapsize": "node -e 'console.log(`node heap limit = ${require(\"v8\").getHeapStatistics().heap_size_limit / (1024 * 1024)} Mb`)'",
"setheapsize": "export NODE_OPTIONS=--max_old_space_size=2048"
},
"engines": {
"node": ">=24 <25"
}
}

View File

@ -7,7 +7,7 @@
*/
import Chef from "./Chef.mjs";
import OperationConfig from "./config/OperationConfig.json" with { type: "json" };
import OperationConfig from "./config/OperationConfig.json" with {type: "json"};
import OpModules from "./config/modules/OpModules.mjs";
import loglevelMessagePrefix from "loglevel-message-prefix";

View File

@ -4,7 +4,7 @@
* @license Apache-2.0
*/
import OperationConfig from "./config/OperationConfig.json" with { type: "json" };
import OperationConfig from "./config/OperationConfig.json" with {type: "json"};
import OperationError from "./errors/OperationError.mjs";
import Operation from "./Operation.mjs";
import DishError from "./errors/DishError.mjs";

View File

@ -1,4 +1,4 @@
import OperationConfig from "../config/OperationConfig.json" with { type: "json" };
import OperationConfig from "../config/OperationConfig.json" with {type: "json"};
import Utils, { isWorkerEnvironment } from "../Utils.mjs";
import Recipe from "../Recipe.mjs";
import Dish from "../Dish.mjs";

View File

@ -10,7 +10,7 @@
import NodeDish from "./NodeDish.mjs";
import NodeRecipe from "./NodeRecipe.mjs";
import OperationConfig from "../core/config/OperationConfig.json" with { type: "json" };
import OperationConfig from "../core/config/OperationConfig.json" with {type: "json"};
import { sanitise, removeSubheadingsFromArray, sentenceToCamelCase } from "./apiUtils.mjs";
import ExcludedOperationError from "../core/errors/ExcludedOperationError.mjs";

View File

@ -17,8 +17,8 @@ import * as CanvasComponents from "../core/lib/CanvasComponents.mjs";
// CyberChef
import App from "./App.mjs";
import Categories from "../core/config/Categories.json" with { type: "json" };
import OperationConfig from "../core/config/OperationConfig.json" with { type: "json" };
import Categories from "../core/config/Categories.json" with {type: "json"};
import OperationConfig from "../core/config/OperationConfig.json" with {type: "json"};
/**

View File

@ -4,8 +4,6 @@
* @license Apache-2.0
*/
import ChefWorker from "worker-loader?inline=no-fallback!../../core/ChefWorker.js";
/**
* Waiter to handle conversations with a ChefWorker in the background.
*/
@ -33,7 +31,7 @@ class BackgroundWorkerWaiter {
*/
registerChefWorker() {
log.debug("Registering new background ChefWorker");
this.chefWorker = new ChefWorker();
this.chefWorker = new Worker(new URL("../../core/ChefWorker.js", import.meta.url));
this.chefWorker.addEventListener("message", this.handleChefMessage.bind(this));
this.chefWorker.postMessage({
action: "setLogPrefix",

View File

@ -5,8 +5,6 @@
* @license Apache-2.0
*/
import LoaderWorker from "worker-loader?inline=no-fallback!../workers/LoaderWorker.js";
import InputWorker from "worker-loader?inline=no-fallback!../workers/InputWorker.mjs";
import Utils, {debounce} from "../../core/Utils.mjs";
import {toBase64} from "../../core/lib/Base64.mjs";
import cptable from "codepage";
@ -359,7 +357,7 @@ class InputWaiter {
}
log.debug("Adding new InputWorker");
this.inputWorker = new InputWorker();
this.inputWorker = new Worker(new URL("../workers/InputWorker.mjs", import.meta.url));
this.inputWorker.postMessage({
action: "setLogLevel",
data: log.getLevel()
@ -405,7 +403,7 @@ class InputWaiter {
return -1;
}
log.debug(`Adding new LoaderWorker (${this.loaderWorkers.length + 1}/${this.maxWorkers}).`);
const newWorker = new LoaderWorker();
const newWorker = new Worker(new URL("../workers/LoaderWorker.js", import.meta.url));
const workerId = this.workerId++;
newWorker.addEventListener("message", this.handleLoaderMessage.bind(this));
newWorker.postMessage({

View File

@ -10,7 +10,6 @@ import Dish from "../../core/Dish.mjs";
import {isUTF8, CHR_ENC_SIMPLE_REVERSE_LOOKUP} from "../../core/lib/ChrEnc.mjs";
import {detectFileType} from "../../core/lib/FileType.mjs";
import FileSaver from "file-saver";
import ZipWorker from "worker-loader?inline=no-fallback!../workers/ZipWorker.mjs";
import {
EditorView,
@ -987,7 +986,7 @@ class OutputWaiter {
downloadButton.firstElementChild.innerHTML = "autorenew";
log.debug("Creating ZipWorker");
this.zipWorker = new ZipWorker();
this.zipWorker = new Worker(new URL("../workers/ZipWorker.mjs", import.meta.url));
this.zipWorker.postMessage({
action: "setLogLevel",
data: log.getLevel()

View File

@ -5,8 +5,6 @@
* @license Apache-2.0
*/
import ChefWorker from "worker-loader?inline=no-fallback!../../core/ChefWorker.js";
import DishWorker from "worker-loader?inline=no-fallback!../workers/DishWorker.mjs";
import { debounce } from "../../core/Utils.mjs";
/**
@ -70,7 +68,7 @@ class WorkerWaiter {
}
log.debug("Adding new DishWorker");
this.dishWorker.worker = new DishWorker();
this.dishWorker.worker = new Worker(new URL("../workers/DishWorker.mjs", import.meta.url));
this.dishWorker.worker.addEventListener("message", this.handleDishMessage.bind(this));
this.dishWorker.worker.postMessage({
action: "setLogLevel",
@ -96,7 +94,7 @@ class WorkerWaiter {
log.debug(`Adding new ChefWorker (${this.chefWorkers.length + 1}/${this.maxWorkers})`);
// Create a new ChefWorker and send it the docURL
const newWorker = new ChefWorker();
const newWorker = new Worker(new URL("../../core/ChefWorker.js", import.meta.url));
newWorker.addEventListener("message", this.handleChefMessage.bind(this));
newWorker.postMessage({
action: "setLogPrefix",

View File

@ -0,0 +1,31 @@
/**
* Polyfill for Node.js 22+ where globalThis.fetch is built-in but rejects
* bare filesystem paths. WASM libraries like argon2-browser call fetch() with
* an absolute path (e.g. "/path/to/argon2.wasm") expecting a browser-style
* fallback, but Node.js 22's fetch throws synchronously for non-URL strings.
*
* This wrapper intercepts such calls and serves the file via Node's fs module,
* returning a synthetic Response so the WASM module loads correctly.
*/
import { readFile } from "fs/promises";
if (globalThis.fetch) {
const originalFetch = globalThis.fetch;
globalThis.fetch = async function patchedFetch(url, options) {
const urlStr = typeof url === "string" ?
url :
url instanceof URL ?
url.href :
String(url);
// Intercept bare filesystem paths (absolute POSIX or Windows)
if (urlStr.startsWith("/") || /^[A-Za-z]:[/\\]/.test(urlStr)) {
const buffer = await readFile(urlStr);
return new Response(buffer, {
status: 200,
headers: { "Content-Type": "application/wasm" },
});
}
return originalFetch(url, options);
};
}

View File

@ -1,6 +1,6 @@
import TestRegister from "../../lib/TestRegister.mjs";
import Categories from "../../../src/core/config/Categories.json" with { type: "json" };
import OperationConfig from "../../../src/core/config/OperationConfig.json" with { type: "json" };
import Categories from "../../../src/core/config/Categories.json" with {type: "json"};
import OperationConfig from "../../../src/core/config/OperationConfig.json" with {type: "json"};
import it from "../assertionHandler.mjs";
import assert from "assert";

View File

@ -11,6 +11,7 @@
* @license Apache-2.0
*/
import "../lib/wasmFetchPolyfill.mjs";
import { setLongTestFailure, logTestReport } from "../lib/utils.mjs";
import TestRegister from "../lib/TestRegister.mjs";