hard-cut batch D: enforce worker bundle isolation and CI guard

This commit is contained in:
Richard R 2026-05-26 16:00:26 -06:00
parent f77ee8cfd4
commit fb0c95dfb1
4 changed files with 59 additions and 35 deletions

View file

@ -36,6 +36,10 @@ jobs:
weed version
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build app and enforce bundle guard
run: |
pnpm build
pnpm build:bundle-guard
- name: Verify ffprobe
run: ffprobe -version
- name: Install Playwright Browsers

View file

@ -1,10 +1,4 @@
import type { NextConfig } from "next";
import path from "node:path";
import { createRequire } from "node:module";
type DefinePluginCtor = new (defs: Record<string, string>) => unknown;
const require = createRequire(import.meta.url);
const { DefinePlugin } = require('webpack') as { DefinePlugin: DefinePluginCtor };
const securityHeaders = [
{ key: 'X-Content-Type-Options', value: 'nosniff' },
@ -21,11 +15,7 @@ const securityHeaders = [
},
];
const computeModeRaw = (process.env.COMPUTE_MODE || 'local').trim().toLowerCase();
const computeMode = computeModeRaw === 'none' || computeModeRaw === 'worker' || computeModeRaw === 'local'
? computeModeRaw
: 'local';
const bundleWorkerCompute = computeMode === 'worker';
const bundleWorkerCompute = true;
const serverExternalPackages = [
'@napi-rs/canvas',
'better-sqlite3',
@ -48,7 +38,7 @@ const nextConfig: NextConfig = {
canvas: '@napi-rs/canvas',
},
},
transpilePackages: !bundleWorkerCompute ? ['@openreader/compute-core'] : [],
transpilePackages: [],
serverExternalPackages,
outputFileTracingIncludes: {
'/api/audiobook': [
@ -73,35 +63,14 @@ const nextConfig: NextConfig = {
outputFileTracingExcludes: {
'/*': [
'./docstore/**/*',
...(bundleWorkerCompute
? [
'./node_modules/onnxruntime-node/**/*',
'./node_modules/@huggingface/tokenizers/**/*',
]
: []),
'./node_modules/onnxruntime-node/**/*',
'./node_modules/@huggingface/tokenizers/**/*',
],
},
webpack: (config, { isServer }) => {
if (isServer && bundleWorkerCompute) {
config.plugins = config.plugins || [];
config.plugins.push(
new DefinePlugin({
__OPENREADER_COMPUTE_MODE__: JSON.stringify('worker'),
}),
);
}
if (isServer && bundleWorkerCompute) {
const workerComputeEntry = path.resolve(__dirname, 'src/lib/server/compute/index.worker.ts');
const computeIndexTs = path.resolve(__dirname, 'src/lib/server/compute/index.ts');
const computeIndexNoExt = path.resolve(__dirname, 'src/lib/server/compute/index');
const computeDir = path.resolve(__dirname, 'src/lib/server/compute');
config.resolve.alias = {
...(config.resolve.alias || {}),
'@/lib/server/compute$': workerComputeEntry,
'@/lib/server/compute/index$': workerComputeEntry,
[`${computeIndexTs}$`]: workerComputeEntry,
[`${computeIndexNoExt}$`]: workerComputeEntry,
[`${computeDir}$`]: workerComputeEntry,
'@openreader/compute-core/local-runtime$': false,
'onnxruntime-node$': false,
'@huggingface/tokenizers$': false,

View file

@ -7,6 +7,7 @@
"dev": "node scripts/openreader-entrypoint.mjs -- pnpm dev:raw",
"dev:raw": "next dev --turbopack -p 3003",
"build": "next build",
"build:bundle-guard": "node scripts/check-next-server-bundle.mjs",
"start": "node scripts/openreader-entrypoint.mjs -- pnpm start:raw",
"start:raw": "next start -p 3003",
"lint": "next lint",

View file

@ -0,0 +1,50 @@
import fs from 'node:fs';
import path from 'node:path';
const root = process.cwd();
const serverDir = path.join(root, '.next', 'server');
if (!fs.existsSync(serverDir)) {
console.error('[bundle-guard] Missing .next/server. Run `pnpm build` first.');
process.exit(1);
}
const forbidden = [
'onnxruntime-node',
'@huggingface/tokenizers',
'@openreader/compute-core/local-runtime',
];
const includeExt = new Set(['.js', '.mjs', '.cjs']);
const failures = [];
function walk(dir) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
const fullPath = path.join(dir, entry.name);
if (entry.isDirectory()) {
walk(fullPath);
continue;
}
if (!entry.isFile()) continue;
const ext = path.extname(entry.name).toLowerCase();
if (!includeExt.has(ext)) continue;
const text = fs.readFileSync(fullPath, 'utf8');
for (const pattern of forbidden) {
if (text.includes(pattern)) {
failures.push({ file: fullPath, pattern });
}
}
}
}
walk(serverDir);
if (failures.length > 0) {
console.error('[bundle-guard] Forbidden compute deps detected in Next server bundle:');
for (const failure of failures) {
console.error(`- ${failure.pattern} in ${path.relative(root, failure.file)}`);
}
process.exit(1);
}
console.info('[bundle-guard] OK: no forbidden compute deps in .next/server');