diff --git a/.changeset/fiery-swans-happen.md b/.changeset/fiery-swans-happen.md new file mode 100644 index 00000000..73fa018e --- /dev/null +++ b/.changeset/fiery-swans-happen.md @@ -0,0 +1,5 @@ +--- +"@livekit/rtc-node": patch +--- + +fix: avoid dual package hazard by building single CJS version with an ESM wrapper diff --git a/packages/livekit-rtc/package.json b/packages/livekit-rtc/package.json index e428c337..bff67d35 100644 --- a/packages/livekit-rtc/package.json +++ b/packages/livekit-rtc/package.json @@ -4,9 +4,8 @@ "license": "Apache-2.0", "author": "LiveKit", "version": "0.13.32", - "main": "dist/index.js", - "require": "dist/index.cjs", - "types": "dist/index.d.ts", + "main": "dist/index.cjs", + "types": "dist/index.d.cts", "exports": { ".": { "import": { @@ -51,7 +50,7 @@ }, "scripts": { "prebuild": "node -p \"'export const SDK_VERSION = ' + JSON.stringify(require('./package.json').version) + ';'\" > src/version.ts", - "build": "pnpm prebuild && tsup --onSuccess \"tsc --declaration --emitDeclarationOnly\"", + "build": "pnpm prebuild && tsup && node scripts/postbuild.mjs", "lint": "eslint -f unix \"src/**/*.ts\" --ignore-pattern \"src/proto/*\"", "test": "vitest run src", "test:e2e": "node scripts/run-e2e.mjs" diff --git a/packages/livekit-rtc/scripts/assertPackageEquality.mjs b/packages/livekit-rtc/scripts/assertPackageEquality.mjs new file mode 100644 index 00000000..dc655809 --- /dev/null +++ b/packages/livekit-rtc/scripts/assertPackageEquality.mjs @@ -0,0 +1,12 @@ +// SPDX-FileCopyrightText: 2026 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +import assert from 'node:assert/strict'; +import { createRequire } from 'node:module'; +import * as esm from '../dist/index.js'; + +const require = createRequire(import.meta.url); +const cjs = require('../dist/index.cjs'); + +assert.equal(esm.Room, cjs.Room); +assert.equal(esm.AudioFrame, cjs.AudioFrame); diff --git a/packages/livekit-rtc/scripts/postbuild.mjs b/packages/livekit-rtc/scripts/postbuild.mjs new file mode 100644 index 00000000..5705c735 --- /dev/null +++ b/packages/livekit-rtc/scripts/postbuild.mjs @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: 2026 LiveKit, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// Runs after `tsup` (which emits the single CJS build + `.d.cts` declarations). +// +// 1. Emit the ESM-facing `.d.ts` declarations via tsc (tsup only produces the +// `.d.cts` files for the `require` condition). +// 2. Write the ESM entry point as a thin wrapper that re-exports the single CJS +// build. esbuild emits a cjs-module-lexer-friendly `module.exports` marker, +// so Node's ESM loader statically resolves every named export from +// `index.cjs` and `export *` needs no per-export maintenance. +// +// Keeping a single physical implementation (index.cjs) means `import` and +// `require` share one module instance, avoiding the dual-package hazard. +import { execSync } from 'node:child_process'; +import { writeFileSync } from 'node:fs'; + +execSync('tsc --declaration --emitDeclarationOnly', { stdio: 'inherit' }); + +writeFileSync(new URL('../dist/index.js', import.meta.url), "export * from './index.cjs';\n"); + +await import('./assertPackageEquality.mjs'); diff --git a/packages/livekit-rtc/tsup.config.ts b/packages/livekit-rtc/tsup.config.ts index 753158ca..0b7cd481 100644 --- a/packages/livekit-rtc/tsup.config.ts +++ b/packages/livekit-rtc/tsup.config.ts @@ -4,5 +4,8 @@ import defaults from '../../tsup.config'; export default defineConfig({ ...defaults, + // Emit a single CJS implementation only with a thin ESM wrapper + // to avoid dual-package hazard + format: ['cjs'], external: [/\.\/.*\.cjs/, /\.\/.*.node/], });