diff --git a/.docker/app/Dockerfile b/.docker/app/Dockerfile index 93e755ce52..63a0cb4f2d 100644 --- a/.docker/app/Dockerfile +++ b/.docker/app/Dockerfile @@ -433,6 +433,7 @@ COPY --from=build-preview-proxy /runtime-deps/node_modules ./apps/preview-proxy/ COPY --from=github-cli /usr/bin/gh /usr/local/bin/gh RUN command -v git >/dev/null && command -v gh >/dev/null && \ command -v opencode >/dev/null && \ + test -f /roomote/apps/bullmq/dist/xhr-sync-worker.js && \ cd /roomote/apps/bullmq && node -e "require.resolve('zod/package.json')" && \ ls -d /roomote/node_modules/.pnpm/zod@*/node_modules/zod >/dev/null diff --git a/apps/bullmq/package.json b/apps/bullmq/package.json index 6113f884eb..54e0979f81 100644 --- a/apps/bullmq/package.json +++ b/apps/bullmq/package.json @@ -37,6 +37,7 @@ "bullmq": "^5.78.0", "hono": "4.13.5", "ioredis": "^5.10.1", + "jsdom": "26.1.0", "zod": "^3.25.76" }, "devDependencies": { diff --git a/apps/bullmq/src/build-artifact.test.ts b/apps/bullmq/src/build-artifact.test.ts new file mode 100644 index 0000000000..ef4b2a1767 --- /dev/null +++ b/apps/bullmq/src/build-artifact.test.ts @@ -0,0 +1,43 @@ +import { execFileSync } from 'node:child_process'; +import { mkdtempSync, readFileSync, rmSync } from 'node:fs'; +import { createRequire } from 'node:module'; +import { tmpdir } from 'node:os'; +import { dirname, join } from 'node:path'; + +const nodeRequire = createRequire(import.meta.url); + +describe('BullMQ build artifact', () => { + it('ships the JSDOM synchronous XHR worker beside the bundled entrypoint', () => { + const packageDirectory = join(import.meta.dirname, '..'); + const outputDirectory = mkdtempSync( + join(tmpdir(), 'roomote-bullmq-build-'), + ); + + try { + execFileSync( + process.execPath, + [ + join( + dirname(nodeRequire.resolve('tsup/package.json')), + 'dist/cli-default.js', + ), + '--config', + join(packageDirectory, 'tsup.config.ts'), + '--out-dir', + outputDirectory, + ], + { cwd: packageDirectory, stdio: 'pipe' }, + ); + + const bundlePath = join(outputDirectory, 'index.js'); + expect(readFileSync(bundlePath, 'utf8')).toContain( + 'require.resolve("./xhr-sync-worker.js")', + ); + expect(createRequire(bundlePath).resolve('./xhr-sync-worker.js')).toBe( + join(dirname(bundlePath), 'xhr-sync-worker.js'), + ); + } finally { + rmSync(outputDirectory, { recursive: true, force: true }); + } + }); +}); diff --git a/apps/bullmq/src/docker-runtime-deps.test.ts b/apps/bullmq/src/docker-runtime-deps.test.ts index 1d4ca52bc7..8074be3d8d 100644 --- a/apps/bullmq/src/docker-runtime-deps.test.ts +++ b/apps/bullmq/src/docker-runtime-deps.test.ts @@ -30,4 +30,16 @@ describe('BullMQ image runtime dependencies', () => { 'cd /roomote/apps/bullmq && node -e "require.resolve(\'zod/package.json\')"', ); }); + + it('verifies the JSDOM synchronous XHR worker reaches the final image', () => { + const runtimeStage = dockerfile + .split(/^FROM /mu) + .find((stage) => + stage.startsWith('runtime-inference-base AS runtime-app'), + ); + + expect(runtimeStage).toContain( + 'test -f /roomote/apps/bullmq/dist/xhr-sync-worker.js', + ); + }); }); diff --git a/apps/bullmq/tsup.config.ts b/apps/bullmq/tsup.config.ts index 40138c5e70..6d8f4aaa21 100644 --- a/apps/bullmq/tsup.config.ts +++ b/apps/bullmq/tsup.config.ts @@ -1,7 +1,21 @@ +import { createRequire } from 'node:module'; +import { dirname, join } from 'node:path'; + import { defineConfig } from 'tsup'; +const nodeRequire = createRequire(import.meta.url); +const jsdomEntry = nodeRequire.resolve('jsdom'); +const jsdomSyncWorkerEntry = join( + dirname(jsdomEntry), + 'jsdom/living/xhr/xhr-sync-worker.js', +); + export default defineConfig({ - entry: ['src/index.ts'], + entry: { + index: 'src/index.ts', + // JSDOM resolves this helper relative to the bundle at runtime. + 'xhr-sync-worker': jsdomSyncWorkerEntry, + }, format: ['esm'], target: 'node22', platform: 'node', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 3091b36d9b..6a6863024e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -323,6 +323,9 @@ importers: ioredis: specifier: 5.10.1 version: 5.10.1 + jsdom: + specifier: 26.1.0 + version: 26.1.0(bufferutil@4.1.0)(utf-8-validate@6.0.6) zod: specifier: ^3.25.76 version: 3.25.76