From b81f2b646a9f7a6eab43acce050ecdb728ff1b6d Mon Sep 17 00:00:00 2001 From: Diego Rodrigues de Sa e Souza Date: Sun, 23 Aug 2026 14:15:14 -0300 Subject: [PATCH] fix(build): align pack-boot sql.js expectations with dependency-based packaging (#11242) (#11266) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit check:pack-artifact and check:pack-boot have been self-contradictory since 05/08, blocking the v3.8.50 publish in ci.yml (build:cli job) and npm-publish.yml: - check:pack-artifact FAILS any tarball path containing a node_modules segment (PACK_ARTIFACT_NEVER_ALLOWED_SEGMENTS; files[] also excludes "!**/node_modules/**"). - check:pack-boot REQUIRED sql.js under the vendored dist/node_modules/sql.js location — a path the tarball can never carry, so both gates could never be green at once. The packaging model is now dependency-based: sql.js and node-machine-id are declared `dependencies` (a clean install places them under /node_modules/), and better-sqlite3 is an optionalDependency installed natively per platform (^13.0.2 — which also covers the darwin-arm64 prebuild gap from #11242 by construction). The runtime already resolves the WASM at /node_modules/sql.js/dist/sql-wasm.wasm (src/lib/db/adapters/sqljsAdapter.ts). Changes: - scripts/check/check-pack-boot.mjs: REQUIRED_SQLJS_RUNTIME_FILES now points at node_modules/sql.js/{package.json,dist/sql-wasm.js, dist/sql-wasm.wasm} — the dependency-installed location the clean-prefix install actually produces. REQUIRED_MACHINE_TOKEN_RUNTIME_FILES was already correct and is unchanged. - bin/cli/runtime/sqliteRuntime.mjs: BETTER_SQLITE3_VERSION bumped ^12.10.1 -> ^13.0.2 to match optionalDependencies (the lazy runtime install was pulling the wrong major), and exported for the guard. - tests/unit/pack-boot-runtime-paths.test.ts (new, TDD: RED -> GREEN): pins that (a) no pack-boot required path references a never-publishable vendored dist/ location (driven by PACK_ARTIFACT_NEVER_ALLOWED_SEGMENTS), (b) sql.js/node-machine-id stay declared dependencies, (c) the lazy-install spec stays on the declared optionalDependency major. - tests/unit/check-pack-boot.test.ts: the sql.js contract test pinned the old vendored path; updated to node_modules/sql.js/dist/sql-wasm.wasm. This is alignment to the real new contract (vendoring ended), not masking — the same test still asserts the find-missing behavior. Electron is unaffected: the vendored dist/node_modules bundle still exists for Electron packaging (postinstall.mjs and assembleStandalone.mjs untouched). Refs #11242 Refs #10296 Co-authored-by: Xiangzhe --- bin/cli/runtime/sqliteRuntime.mjs | 4 +- scripts/check/check-pack-boot.mjs | 12 +++- tests/unit/check-pack-boot.test.ts | 7 +- tests/unit/pack-boot-runtime-paths.test.ts | 78 ++++++++++++++++++++++ 4 files changed, 95 insertions(+), 6 deletions(-) create mode 100644 tests/unit/pack-boot-runtime-paths.test.ts diff --git a/bin/cli/runtime/sqliteRuntime.mjs b/bin/cli/runtime/sqliteRuntime.mjs index 506481a353..e8ca615bbf 100644 --- a/bin/cli/runtime/sqliteRuntime.mjs +++ b/bin/cli/runtime/sqliteRuntime.mjs @@ -6,7 +6,9 @@ import { pathToFileURL } from "node:url"; import { validateBinaryMagic, platformBinaryLabel } from "./magicBytes.mjs"; const RUNTIME_DIR = join(homedir(), ".omniroute", "runtime"); -const BETTER_SQLITE3_VERSION = "better-sqlite3@^12.10.1"; +// Exported so the packaging coherence guard (tests/unit/pack-boot-runtime-paths.test.ts) +// can assert this stays on the same major as optionalDependencies.better-sqlite3 (#11242). +export const BETTER_SQLITE3_VERSION = "better-sqlite3@^13.0.2"; let resolvedCached = null; diff --git a/scripts/check/check-pack-boot.mjs b/scripts/check/check-pack-boot.mjs index 673decdd21..2ca4097a0a 100644 --- a/scripts/check/check-pack-boot.mjs +++ b/scripts/check/check-pack-boot.mjs @@ -26,10 +26,16 @@ const MAX_SERVER_OUTPUT_CHARS = 1_000_000; const SQLJS_STARTUP_MARKER = "Pre-initializing sql.js WASM"; const DEFAULT_CLI_SALT = "omniroute-cli-auth-v1"; +// Dependency-based packaging (#11242): the tarball can never contain a node_modules +// path (files[] has "!**/node_modules/**" and check:pack-artifact fails on the +// segment), so sql.js must be required where a clean `npm install` of the declared +// `dependencies` places it — /node_modules/sql.js — NOT under the old +// vendored dist/node_modules location. The runtime resolves the WASM the same way +// (src/lib/db/adapters/sqljsAdapter.ts → /node_modules/sql.js/dist/sql-wasm.wasm). export const REQUIRED_SQLJS_RUNTIME_FILES = Object.freeze([ - "dist/node_modules/sql.js/package.json", - "dist/node_modules/sql.js/dist/sql-wasm.js", - "dist/node_modules/sql.js/dist/sql-wasm.wasm", + "node_modules/sql.js/package.json", + "node_modules/sql.js/dist/sql-wasm.js", + "node_modules/sql.js/dist/sql-wasm.wasm", ]); export const REQUIRED_MACHINE_TOKEN_RUNTIME_FILES = Object.freeze([ diff --git a/tests/unit/check-pack-boot.test.ts b/tests/unit/check-pack-boot.test.ts index 56abe03176..2a7e0edf07 100644 --- a/tests/unit/check-pack-boot.test.ts +++ b/tests/unit/check-pack-boot.test.ts @@ -71,10 +71,13 @@ test("installed package contract requires sql.js metadata, entrypoint, and WASM" [] ); - present.delete(path.join("/pkg", "dist/node_modules/sql.js/dist/sql-wasm.wasm")); + // Dependency-based packaging (#11242): sql.js is a declared dependency, so the + // contract path is the npm-installed /node_modules/sql.js location, + // never the old vendored dist/node_modules one (banned from the tarball). + present.delete(path.join("/pkg", "node_modules/sql.js/dist/sql-wasm.wasm")); assert.deepEqual( findMissingSqlJsRuntimeFiles("/pkg", (file) => present.has(file)), - ["dist/node_modules/sql.js/dist/sql-wasm.wasm"] + ["node_modules/sql.js/dist/sql-wasm.wasm"] ); }); diff --git a/tests/unit/pack-boot-runtime-paths.test.ts b/tests/unit/pack-boot-runtime-paths.test.ts new file mode 100644 index 0000000000..8b9016a245 --- /dev/null +++ b/tests/unit/pack-boot-runtime-paths.test.ts @@ -0,0 +1,78 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { readFileSync } from "node:fs"; +import path from "node:path"; +import { fileURLToPath } from "node:url"; + +import { + REQUIRED_MACHINE_TOKEN_RUNTIME_FILES, + REQUIRED_SQLJS_RUNTIME_FILES, +} from "../../scripts/check/check-pack-boot.mjs"; +import { PACK_ARTIFACT_NEVER_ALLOWED_SEGMENTS } from "../../scripts/build/pack-artifact-policy.ts"; +import * as sqliteRuntime from "../../bin/cli/runtime/sqliteRuntime.mjs"; + +// Coherence guard for the v3.8.50 publish blocker (#11242): check:pack-artifact +// FAILS any tarball path containing a node_modules segment (files[] excludes them +// via "!**/node_modules/**"), while check:pack-boot REQUIRED sql.js under the +// vendored dist/node_modules/ location — a path the tarball can never contain, +// so the two gates could never be green at the same time. The npm packaging +// model is now dependency-based: sql.js and node-machine-id are declared +// `dependencies` that a clean install places under /node_modules/, +// and better-sqlite3 is an optionalDependency installed natively per platform. +// These tests pin that contract so neither gate can drift back into conflict. + +const REPO_ROOT = path.join(path.dirname(fileURLToPath(import.meta.url)), "..", ".."); +const PKG = JSON.parse(readFileSync(path.join(REPO_ROOT, "package.json"), "utf8")) as { + dependencies?: Record; + optionalDependencies?: Record; +}; + +test("pack-boot required runtime files never reference a never-publishable vendored path", () => { + const requiredFiles = [...REQUIRED_SQLJS_RUNTIME_FILES, ...REQUIRED_MACHINE_TOKEN_RUNTIME_FILES]; + assert.ok(requiredFiles.length > 0, "pack-boot must require at least one runtime file"); + for (const requiredPath of requiredFiles) { + for (const segment of PACK_ARTIFACT_NEVER_ALLOWED_SEGMENTS) { + const vendoredPrefix = `dist/${segment}/`; + assert.ok( + !requiredPath.includes(vendoredPrefix), + `"${requiredPath}" lives under ${vendoredPrefix} — check:pack-artifact bans any ` + + `tarball path with a "${segment}" segment, so check:pack-boot must require the ` + + `dependency-installed location (node_modules/) instead (#11242)` + ); + } + } +}); + +test("sql.js and node-machine-id are declared runtime dependencies (npm installs them)", () => { + assert.ok( + PKG.dependencies?.["sql.js"], + "sql.js must stay in dependencies so a clean install provides node_modules/sql.js" + ); + assert.ok( + PKG.dependencies?.["node-machine-id"], + "node-machine-id must stay in dependencies so a clean install provides node_modules/node-machine-id" + ); +}); + +test("the lazy better-sqlite3 runtime install targets the declared optionalDependency major", () => { + const spec = (sqliteRuntime as Record).BETTER_SQLITE3_VERSION; + assert.equal( + typeof spec, + "string", + "bin/cli/runtime/sqliteRuntime.mjs must export BETTER_SQLITE3_VERSION" + ); + const declared = PKG.optionalDependencies?.["better-sqlite3"]; + assert.ok(declared, "package.json must declare better-sqlite3 as an optionalDependency"); + + const majorOf = (versionSpec: string): number => { + const match = versionSpec.match(/(\d+)\./); + assert.ok(match, `"${versionSpec}" must contain a semver major`); + return Number(match[1]); + }; + assert.equal( + majorOf(spec as string), + majorOf(declared), + `lazy runtime install "${spec}" drifted from optionalDependencies.better-sqlite3 ` + + `"${declared}" — the fallback install must track the same major (#11242)` + ); +});