From c924b3fccc863a9a2b56a04b9f34492893b90811 Mon Sep 17 00:00:00 2001 From: Paul <72733450+paul1995tu@users.noreply.github.com> Date: Sat, 5 Sep 2026 19:24:05 +0200 Subject: [PATCH] fix(simplelog): repair the /otel entry, which threw on import in 2.0.0 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SyntaxError: Export named 'setSpanContextReader' not found in module .../@murky-web/simplelog/dist/logger_factory.js The build runs two tsdown config blocks, one `platform: "node"` and one `platform: "neutral"`, writing to the same `outDir`. `logger_factory` was an entry in neither, so each block emitted it keeping only the exports its own entries used, and whichever finished last decided the file. The neutral block's entries — deno, hono, web — never call `setSpanContextReader`, so when it won, the export was gone and `dist/otel.js` imported a binding that did not exist. The order is not fixed. Rebuilding the broken config five times produced a working file once and a broken one four times, which is how this reached npm: the release build happened to land on the losing order. Naming `logger_factory` as an entry in both blocks preserves its full export surface regardless of order. Verified stable over repeated builds. `smoke:entries` imports every subpath in the `exports` map from the built output and fails when one will not load or exports nothing. It runs in `release:verify:simplelog`. Nothing already in place could have caught this: lint and typecheck read the source, and the generated `otel.d.ts` described the very export the JavaScript lacked, so the declaration agreed with the code that was never emitted. Only loading the built files shows it. Bisected: the new check fails on the unfixed build and passes on the fixed one. Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_015zGgcXsDCDPNCSzhAWn1Zm --- .changeset/simplelog-otel-entry-fix.md | 31 +++++++++ package.json | 86 ++++++++++++------------- packages/simplelog/package.json | 1 + packages/simplelog/smoke/entries/run.ts | 65 +++++++++++++++++++ packages/simplelog/tsdown.config.ts | 11 ++++ 5 files changed, 151 insertions(+), 43 deletions(-) create mode 100644 .changeset/simplelog-otel-entry-fix.md create mode 100644 packages/simplelog/smoke/entries/run.ts diff --git a/.changeset/simplelog-otel-entry-fix.md b/.changeset/simplelog-otel-entry-fix.md new file mode 100644 index 0000000..333662d --- /dev/null +++ b/.changeset/simplelog-otel-entry-fix.md @@ -0,0 +1,31 @@ +--- +"@murky-web/simplelog": patch +--- + +Fix `@murky-web/simplelog/otel`, which threw on import in 2.0.0. + +`dist/otel.js` imported `setSpanContextReader` from `dist/logger_factory.js`, and +that file did not export it: + +``` +SyntaxError: Export named 'setSpanContextReader' not found in module + .../@murky-web/simplelog/dist/logger_factory.js +``` + +The build runs two tsdown config blocks — one `platform: "node"`, one +`platform: "neutral"` — that write to the same `outDir`. `logger_factory` was an +entry in neither, so each block emitted it keeping only the exports its own +entries used, and the block that finished last decided the file. The neutral +block's entries (`deno`, `hono`, `web`) never call `setSpanContextReader`, so +when it won, the export was gone. The order is not fixed: rebuilding the broken +config five times produced a working file once and a broken one four times, +which is how this reached npm. + +Naming `logger_factory` as an entry in both blocks preserves its full export +surface either way. Verified stable across repeated builds. + +A new `smoke:entries` check imports every subpath in the `exports` map from the +built output and fails if one does not load or exports nothing. It runs as part +of `release:verify:simplelog`. Nothing existing could have caught this: lint and +typecheck read the source, and the generated `otel.d.ts` described the export +that the JavaScript was missing — only loading the built files reveals it. diff --git a/package.json b/package.json index df19762..b0be472 100644 --- a/package.json +++ b/package.json @@ -1,45 +1,45 @@ { - "name": "@murky-web/workspace", - "private": true, - "bin": { - "web-dev-config": "./bin/web-dev-config.mjs" - }, - "packageManager": "bun@1.4.0", - "workspaces": [ - "packages/*" - ], - "scripts": { - "build": "bun run --workspaces --if-present build", - "changeset": "changeset", - "clean": "bun run --workspaces --if-present clean", - "config:init": "bun ./packages/config/bin/web-dev-config.mjs init", - "config:init:solid": "bun ./packages/config/bin/web-dev-config.mjs init --frontend-solid", - "release:verify": "bun run release:verify:typebuddy && bun run release:verify:simplelog && bun run release:verify:oxlint-plugin-solid", - "release:verify:oxlint-plugin-solid": "bun run --cwd ./packages/oxlint-plugin-solid test", - "release:verify:simplelog": "bun run --cwd ./packages/simplelog lint && bun run --cwd ./packages/simplelog typecheck && bun run --cwd ./packages/simplelog test && bun run --cwd ./packages/simplelog smoke:oxlint && bun run --cwd ./packages/simplelog build", - "release:verify:typebuddy": "bun run --cwd ./packages/typebuddy lint && bun run --cwd ./packages/typebuddy typecheck && bun run --cwd ./packages/typebuddy test && bun run --cwd ./packages/typebuddy smoke:globals && bun run --cwd ./packages/typebuddy smoke:oxlint && bun run --cwd ./packages/typebuddy smoke:oxlint:fix && bun run --cwd ./packages/typebuddy smoke:treeshake", - "test": "bun run --workspaces --if-present test", - "typecheck": "bun run --workspaces --if-present typecheck", - "lint": "oxlint -c ./packages/config/oxc/.oxlintrc.jsonc --type-aware ./packages", - "lint:fix": "oxlint -c ./packages/config/oxc/.oxlintrc.jsonc --type-aware --fix ./packages", - "format": "oxfmt -c ./packages/config/oxc/.oxfmtrc.jsonc ./packages", - "format:check": "oxfmt -c ./packages/config/oxc/.oxfmtrc.jsonc --check ./packages", - "publish:jsr": "bun ./scripts/publish-jsr.mjs", - "publish:jsr:dry": "bun ./scripts/publish-jsr.mjs --dry-run", - "publish:npm:dry": "bun ./scripts/publish-npm-dry.mjs", - "release": "bun run release:verify && bun run publish:npm", - "release:dry": "bun run release:verify && bun run publish:npm:dry", - "publish:npm": "changeset publish", - "version-packages": "changeset version && bun ./scripts/sync-jsr-manifests.mjs" - }, - "devDependencies": { - "@changesets/changelog-git": "^0.2.1", - "@changesets/cli": "^2.30.0", - "oxfmt": "^0.66.0", - "oxlint": "^1.81.0", - "oxlint-tsgolint": "^7.0.2001", - "tsdown": "0.22.14", - "typescript": "^7.0.2", - "vitest": "3.2.4" - } + "name": "@murky-web/workspace", + "private": true, + "bin": { + "web-dev-config": "./bin/web-dev-config.mjs" + }, + "packageManager": "bun@1.4.0", + "workspaces": [ + "packages/*" + ], + "scripts": { + "build": "bun run --workspaces --if-present build", + "changeset": "changeset", + "clean": "bun run --workspaces --if-present clean", + "config:init": "bun ./packages/config/bin/web-dev-config.mjs init", + "config:init:solid": "bun ./packages/config/bin/web-dev-config.mjs init --frontend-solid", + "release:verify": "bun run release:verify:typebuddy && bun run release:verify:simplelog && bun run release:verify:oxlint-plugin-solid", + "release:verify:oxlint-plugin-solid": "bun run --cwd ./packages/oxlint-plugin-solid test", + "release:verify:simplelog": "bun run --cwd ./packages/simplelog lint && bun run --cwd ./packages/simplelog typecheck && bun run --cwd ./packages/simplelog test && bun run --cwd ./packages/simplelog smoke:oxlint && bun run --cwd ./packages/simplelog smoke:entries", + "release:verify:typebuddy": "bun run --cwd ./packages/typebuddy lint && bun run --cwd ./packages/typebuddy typecheck && bun run --cwd ./packages/typebuddy test && bun run --cwd ./packages/typebuddy smoke:globals && bun run --cwd ./packages/typebuddy smoke:oxlint && bun run --cwd ./packages/typebuddy smoke:oxlint:fix && bun run --cwd ./packages/typebuddy smoke:treeshake", + "test": "bun run --workspaces --if-present test", + "typecheck": "bun run --workspaces --if-present typecheck", + "lint": "oxlint -c ./packages/config/oxc/.oxlintrc.jsonc --type-aware ./packages", + "lint:fix": "oxlint -c ./packages/config/oxc/.oxlintrc.jsonc --type-aware --fix ./packages", + "format": "oxfmt -c ./packages/config/oxc/.oxfmtrc.jsonc ./packages", + "format:check": "oxfmt -c ./packages/config/oxc/.oxfmtrc.jsonc --check ./packages", + "publish:jsr": "bun ./scripts/publish-jsr.mjs", + "publish:jsr:dry": "bun ./scripts/publish-jsr.mjs --dry-run", + "publish:npm:dry": "bun ./scripts/publish-npm-dry.mjs", + "release": "bun run release:verify && bun run publish:npm", + "release:dry": "bun run release:verify && bun run publish:npm:dry", + "publish:npm": "changeset publish", + "version-packages": "changeset version && bun ./scripts/sync-jsr-manifests.mjs" + }, + "devDependencies": { + "@changesets/changelog-git": "^0.2.1", + "@changesets/cli": "^2.30.0", + "oxfmt": "^0.66.0", + "oxlint": "^1.81.0", + "oxlint-tsgolint": "^7.0.2001", + "tsdown": "0.22.14", + "typescript": "^7.0.2", + "vitest": "3.2.4" + } } diff --git a/packages/simplelog/package.json b/packages/simplelog/package.json index 76c0820..962b010 100644 --- a/packages/simplelog/package.json +++ b/packages/simplelog/package.json @@ -69,6 +69,7 @@ "format": "oxfmt -c ../config/oxc/.oxfmtrc.jsonc src oxlint smoke tsdown.config.ts", "format:check": "oxfmt -c ../config/oxc/.oxfmtrc.jsonc --check src oxlint smoke tsdown.config.ts", "smoke:oxlint": "bun ./smoke/oxlint/run-smoke.ts", + "smoke:entries": "bun run build && bun ./smoke/entries/run.ts", "test": "vitest run", "typecheck": "tsc --project ./tsconfig.json --noEmit" }, diff --git a/packages/simplelog/smoke/entries/run.ts b/packages/simplelog/smoke/entries/run.ts new file mode 100644 index 0000000..82d446f --- /dev/null +++ b/packages/simplelog/smoke/entries/run.ts @@ -0,0 +1,65 @@ +import { readFileSync } from "node:fs"; +import path from "node:path"; + +/** + * Imports every published entry point for real. + * + * 2.0.0 shipped a `/otel` entry that threw on import: `dist/otel.js` imported + * `setSpanContextReader` from `dist/logger_factory.js`, which did not export it. + * Nothing caught that, because every other check reads the source. The build emits + * `logger_factory.js` from two tsdown config blocks writing the same `outDir`, and a + * module reached only as a dependency keeps just the exports its own block's entries + * use — so whichever block finished last decided the export list, and that order is + * not fixed. Type-checking the source cannot see it; only loading the built files can. + * + * A `d.ts` next to a `.js` is no proof either: the declaration is generated from the + * source, so it described an export the JavaScript did not have. + */ +const packageRoot = path.resolve(import.meta.dir, "../.."); +const pkg = JSON.parse( + readFileSync(path.join(packageRoot, "package.json"), "utf8"), +) as { name: string; exports: Record }; + +const failures: string[] = []; +let loaded = 0; + +for (const [subpath, target] of Object.entries(pkg.exports)) { + if (subpath === "./package.json") { + continue; + } + + const file = typeof target === "string" ? target : target.import; + if (file === undefined) { + failures.push(`exports["${subpath}"] has no import condition`); + continue; + } + + const resolved = path.join(packageRoot, file); + try { + const module: Record = await import(resolved); + const names = Object.keys(module).filter((name) => name !== "default"); + if (names.length === 0) { + failures.push(`${subpath} -> ${file} loaded but exports nothing`); + continue; + } + loaded += 1; + console.log(` ok ${subpath.padEnd(12)} ${names.join(", ")}`); + } catch (error) { + // An unresolved import between two built files surfaces here and nowhere else. + failures.push( + `${subpath} -> ${file} failed to load: ${ + error instanceof Error ? error.message : String(error) + }`, + ); + } +} + +if (failures.length > 0) { + console.error(`\n${failures.length} entry point(s) failed:`); + for (const failure of failures) { + console.error(` ${failure}`); + } + process.exit(1); +} + +console.log(`\nEntry smoke passed (${loaded} entry points loaded).`); diff --git a/packages/simplelog/tsdown.config.ts b/packages/simplelog/tsdown.config.ts index 57e3b11..b73291f 100644 --- a/packages/simplelog/tsdown.config.ts +++ b/packages/simplelog/tsdown.config.ts @@ -5,6 +5,14 @@ export default defineConfig([ entry: { bun_logger: "./src/bun_logger.ts", logger: "./src/logger.ts", + // `logger_factory` is shared by entries in both config blocks below, and both + // blocks write it to the same `dist/logger_factory.js`. A module that is only + // reached as a dependency keeps just the exports its own block's entries use, + // so whichever block finished last decided the file's export list — and the + // neutral block does not use `setSpanContextReader`, which left + // `dist/otel.js` importing a binding that was not there. Naming it as an entry + // in both blocks preserves its full surface either way. + logger_factory: "./src/logger_factory.ts", otel: "./src/otel.ts", }, dts: true, @@ -21,6 +29,9 @@ export default defineConfig([ entry: { deno_logger: "./src/deno_logger.ts", hono_middleware: "./src/hono_middleware.ts", + // See the note in the block above: both blocks emit this file, so both have to + // treat it as an entry or the loser truncates it. + logger_factory: "./src/logger_factory.ts", web_logger: "./src/web_logger.ts", }, dts: true,