From dcfbc24625de447cdb5a9c809e7d0d6ab47f8f62 Mon Sep 17 00:00:00 2001 From: Dave Cox <113376598+dcox79@users.noreply.github.com> Date: Mon, 17 Aug 2026 06:59:52 -0400 Subject: [PATCH] fix(deps): pin onnxruntime-node to the exact version @huggingface/transformers requires (#10543) `@huggingface/transformers` 4.2.0 hard-pins `onnxruntime-node` to "1.24.3". The production-group bump in #10403 raised the root range from "~1.24.3" to "~1.27.0", so npm stopped deduping and nested a second copy under `node_modules/@huggingface/transformers/node_modules/onnxruntime-node`. Both copies ship a native `libonnxruntime.so.1` under the SAME SONAME, so glibc binds whichever is dlopen()ed first and the other addon dies. The Dockerfile post-build verification imports `@huggingface/transformers` and `onnxruntime-node` in one process, so `docker build` has failed on every commit since #10403: Error: .../transformers/node_modules/onnxruntime-node/bin/napi-v6/linux/x64/libonnxruntime.so.1: version `VERS_1.27.0' not found (required by .../onnxruntime-node/bin/napi-v6/linux/x64/onnxruntime_binding.node) Restore the root range to "~1.24.3" so a single hoisted copy is resolved again. Copying the nested native binaries into the standalone bundle is NOT a workaround: it makes both `.so` files present, which is precisely what triggers the SONAME clash above (verified against a real image build). Regression guard: tests/unit/onnxruntime-single-copy.test.ts asserts the lockfile resolves exactly one onnxruntime-node and that it matches the version transformers pins. Confirmed failing on the pre-fix lockfile (two copies, 1.27.0 vs 1.24.3) and passing after. Validated with a full `docker build --target runner-base`: the post-build verification step now passes (#19 DONE 156.9s) and the image boots healthy (/api/monitoring/health 200, migrations 134-148 applied). --- package-lock.json | 151 ++++----------------- package.json | 2 +- tests/unit/onnxruntime-single-copy.test.ts | 72 ++++++++++ 3 files changed, 102 insertions(+), 123 deletions(-) create mode 100644 tests/unit/onnxruntime-single-copy.test.ts diff --git a/package-lock.json b/package-lock.json index f08d7a25ec..4769372f63 100644 --- a/package-lock.json +++ b/package-lock.json @@ -61,7 +61,7 @@ "next-themes": "^0.4.6", "node-machine-id": "^1.1.12", "omniglyph": "^1.0.2", - "onnxruntime-node": "~1.27.0", + "onnxruntime-node": "~1.24.3", "open": "^11.0.0", "ora": "^9.4.1", "parse5": "^8.0.1", @@ -88,7 +88,6 @@ "undici": "^8.10.0", "update-notifier": "^7.3.1", "uuid": "^14.0.0", - "wreq-js": "3.0.0", "ws": "^8.21.3", "xxhash-wasm": "^1.1.0", "yazl": "^3.3.1", @@ -110,7 +109,7 @@ "@testing-library/jest-dom": "^7.0.0", "@testing-library/react": "^16.3.2", "@types/better-sqlite3": "^9.6.0", - "@types/bun": "*", + "@types/bun": "latest", "@types/node": "^26.2.0", "@types/react": "^19.2.18", "@types/react-dom": "^19.2.4", @@ -3506,97 +3505,6 @@ "sharp": "^0.34.5" } }, - "node_modules/@huggingface/transformers/node_modules/global-agent": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", - "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", - "license": "BSD-3-Clause", - "dependencies": { - "boolean": "^3.0.1", - "es6-error": "^4.1.1", - "matcher": "^3.0.0", - "roarr": "^2.15.3", - "semver": "^7.3.2", - "serialize-error": "^7.0.1" - }, - "engines": { - "node": ">=10.0" - } - }, - "node_modules/@huggingface/transformers/node_modules/matcher": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", - "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", - "license": "MIT", - "dependencies": { - "escape-string-regexp": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@huggingface/transformers/node_modules/onnxruntime-common": { - "version": "1.24.3", - "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.24.3.tgz", - "integrity": "sha512-GeuPZO6U/LBJXvwdaqHbuUmoXiEdeCjWi/EG7Y1HNnDwJYuk6WUbNXpF6luSUY8yASul3cmUlLGrCCL1ZgVXqA==", - "license": "MIT" - }, - "node_modules/@huggingface/transformers/node_modules/onnxruntime-node": { - "version": "1.24.3", - "resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.24.3.tgz", - "integrity": "sha512-JH7+czbc8ALA819vlTgcV+Q214/+VjGeBHDjX81+ZCD0PCVCIFGFNtT0V4sXG/1JXypKPgScQcB3ij/hk3YnTg==", - "hasInstallScript": true, - "license": "MIT", - "os": [ - "win32", - "darwin", - "linux" - ], - "dependencies": { - "adm-zip": "^0.5.16", - "global-agent": "^3.0.0", - "onnxruntime-common": "1.24.3" - } - }, - "node_modules/@huggingface/transformers/node_modules/semver": { - "version": "7.8.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", - "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/@huggingface/transformers/node_modules/serialize-error": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", - "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", - "license": "MIT", - "dependencies": { - "type-fest": "^0.13.1" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@huggingface/transformers/node_modules/type-fest": { - "version": "0.13.1", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", - "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", - "license": "(MIT OR CC0-1.0)", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -20557,15 +20465,17 @@ } }, "node_modules/global-agent": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-4.1.3.tgz", - "integrity": "sha512-KUJEViiuFT3I97t+GYMikLPJS2Lfo/S2F+DQuBWzuzaMPnvt5yyZePzArx36fBzpGTxZjIpDbXLeySLgh+k76g==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-agent/-/global-agent-3.0.0.tgz", + "integrity": "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q==", "license": "BSD-3-Clause", "dependencies": { - "globalthis": "^1.0.2", - "matcher": "^4.0.0", - "semver": "^7.3.5", - "serialize-error": "^8.1.0" + "boolean": "^3.0.1", + "es6-error": "^4.1.1", + "matcher": "^3.0.0", + "roarr": "^2.15.3", + "semver": "^7.3.2", + "serialize-error": "^7.0.1" }, "engines": { "node": ">=10.0" @@ -26020,18 +25930,15 @@ } }, "node_modules/matcher": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/matcher/-/matcher-4.0.0.tgz", - "integrity": "sha512-S6x5wmcDmsDRRU/c2dkccDwQPXoFczc5+HpQ2lON8pnvHlnvHAHj5WlLVvw6n6vNyHuVugYrFohYxbS+pvFpKQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz", + "integrity": "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng==", "license": "MIT", "dependencies": { "escape-string-regexp": "^4.0.0" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/material-symbols": { @@ -29061,15 +28968,15 @@ } }, "node_modules/onnxruntime-common": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.27.0.tgz", - "integrity": "sha512-3KxL5wIVqa8Ex08jxSzncm9CMgw8CjOFyOQ7SxvG9o0cVLlhTNKXyIQuTbtX4tGPJEf73OER2xrjt4HJSBL4ow==", + "version": "1.24.3", + "resolved": "https://registry.npmjs.org/onnxruntime-common/-/onnxruntime-common-1.24.3.tgz", + "integrity": "sha512-GeuPZO6U/LBJXvwdaqHbuUmoXiEdeCjWi/EG7Y1HNnDwJYuk6WUbNXpF6luSUY8yASul3cmUlLGrCCL1ZgVXqA==", "license": "MIT" }, "node_modules/onnxruntime-node": { - "version": "1.27.0", - "resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.27.0.tgz", - "integrity": "sha512-QEzGwrvNBgv4uPVdnbHsOGG4G6T96mdlcFI8aAKPjMU8wOPpVocPXb6k3QGkaZagVTv2G9Bnnbo6Z3JdXr1fQw==", + "version": "1.24.3", + "resolved": "https://registry.npmjs.org/onnxruntime-node/-/onnxruntime-node-1.24.3.tgz", + "integrity": "sha512-JH7+czbc8ALA819vlTgcV+Q214/+VjGeBHDjX81+ZCD0PCVCIFGFNtT0V4sXG/1JXypKPgScQcB3ij/hk3YnTg==", "hasInstallScript": true, "license": "MIT", "os": [ @@ -29079,8 +28986,8 @@ ], "dependencies": { "adm-zip": "^0.5.16", - "global-agent": "^4.1.3", - "onnxruntime-common": "1.27.0" + "global-agent": "^3.0.0", + "onnxruntime-common": "1.24.3" } }, "node_modules/onnxruntime-web": { @@ -33011,12 +32918,12 @@ } }, "node_modules/serialize-error": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-8.1.0.tgz", - "integrity": "sha512-3NnuWfM6vBYoy5gZFvHiYsVbafvI9vZv/+jlIigFn4oP4zjNPK3LhcY0xSCgeb1a5L8jO71Mit9LlNoi2UfDDQ==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz", + "integrity": "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw==", "license": "MIT", "dependencies": { - "type-fest": "^0.20.2" + "type-fest": "^0.13.1" }, "engines": { "node": ">=10" @@ -33026,9 +32933,9 @@ } }, "node_modules/serialize-error/node_modules/type-fest": { - "version": "0.20.2", - "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", - "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz", + "integrity": "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg==", "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" diff --git a/package.json b/package.json index 50c57e2aaf..a4998e690f 100644 --- a/package.json +++ b/package.json @@ -337,7 +337,7 @@ "zod": "^4.4.3", "zustand": "^5.0.13", "@huggingface/transformers": "^4.2.0", - "onnxruntime-node": "~1.27.0" + "onnxruntime-node": "~1.24.3" }, "optionalDependencies": { "@atjsh/llmlingua-2": "2.0.3", diff --git a/tests/unit/onnxruntime-single-copy.test.ts b/tests/unit/onnxruntime-single-copy.test.ts new file mode 100644 index 0000000000..d988cdaeb0 --- /dev/null +++ b/tests/unit/onnxruntime-single-copy.test.ts @@ -0,0 +1,72 @@ +/** + * Regression guard — the dependency tree must resolve exactly ONE + * `onnxruntime-node` (and one `onnxruntime-common`). + * + * `@huggingface/transformers` pins `onnxruntime-node` to an EXACT version + * (4.2.0 → "1.24.3"). Whenever the root range in package.json drifts off that + * pin, npm nests a second copy under + * `node_modules/@huggingface/transformers/node_modules/onnxruntime-node`. + * + * Two copies cannot coexist in one Node process: both ship a native + * `libonnxruntime.so.1` under the SAME SONAME, so glibc's loader binds + * whichever was dlopen()ed first and the other addon dies with + * + * Error: .../libonnxruntime.so.1: version `VERS_1.27.0' not found + * (required by .../onnxruntime_binding.node) + * + * That is exactly what a production-group dependabot bump did on 2026-08-16 + * (root `onnxruntime-node` "~1.24.3" → "~1.27.0"): it broke the Docker image + * build at the Dockerfile's post-build standalone verification step, which + * imports `@huggingface/transformers` and `onnxruntime-node` in one process. + * + * Keep the root range compatible with whatever `@huggingface/transformers` + * pins — do not "fix" a future recurrence by copying the nested native + * binaries into the bundle; the SONAME clash makes that impossible. + */ +import test from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; + +const repoRoot = join(dirname(fileURLToPath(import.meta.url)), "..", ".."); + +const lockfile = JSON.parse(readFileSync(join(repoRoot, "package-lock.json"), "utf8")) as { + packages: Record }>; +}; + +function copiesOf(pkg: string): string[] { + return Object.keys(lockfile.packages).filter( + (key) => key === `node_modules/${pkg}` || key.endsWith(`/node_modules/${pkg}`) + ); +} + +// Scoped to `onnxruntime-node` on purpose. `onnxruntime-common` is types/interfaces +// only and `onnxruntime-web` is WASM — neither dlopen()s anything, so their nested +// duplicates (onnxruntime-web carries its own onnxruntime-common) are harmless. +// `onnxruntime-node` is the sole package shipping the native libonnxruntime.so.1. +test("package-lock.json resolves exactly one copy of onnxruntime-node", () => { + assert.deepEqual( + copiesOf("onnxruntime-node"), + ["node_modules/onnxruntime-node"], + "onnxruntime-node must resolve to a single hoisted copy — a nested duplicate ships a " + + "second libonnxruntime.so.1 under the same SONAME and breaks the standalone/Docker build" + ); +}); + +test("root onnxruntime-node matches the exact version @huggingface/transformers pins", () => { + const transformers = lockfile.packages["node_modules/@huggingface/transformers"]; + assert.ok(transformers, "@huggingface/transformers must be present in the lockfile"); + + const pinned = transformers.dependencies?.["onnxruntime-node"]; + assert.ok(pinned, "@huggingface/transformers must declare an onnxruntime-node dependency"); + + const resolved = lockfile.packages["node_modules/onnxruntime-node"]?.version; + assert.equal( + resolved, + pinned, + `the hoisted onnxruntime-node (${resolved}) must equal the version ` + + `@huggingface/transformers pins (${pinned}); otherwise npm nests a second, ` + + `ABI-incompatible native copy` + ); +});