Findings No. 72
A smoke test that imports `dist/index.js` by path never checks the package's `exports` map
Node applies a package's exports map only to bare specifiers, so a smoke test that imports dist by path can pass while a subpath export is broken.
barmark's verify:package script builds each package and imports its dist/index.js under Node, to check what an npm user will get. Adding @barmark/sync exposed a gap. The package has a second entry point, @barmark/sync/timing, declared in the exports map of its package.json. A typo in that map would have passed the check and shipped, and import "@barmark/sync/timing" would then fail for every user.
Node applies a package's exports map only when it resolves a bare specifier such as @barmark/sync/timing. An import by file path goes straight to the file and never reads the map, so a smoke test written that way cannot catch a mistake in it.
The fix is scripts/smoke-sync.mjs, which runs in two modes. Locally, it reads exports["./timing"].import from the package.json beside dist/, resolves that path, and imports the result, so a wrong path in the map fails the check before release. After publish, the release workflow's confirm job runs the same script against the copy installed from npm, where it imports the bare specifiers @barmark/sync and @barmark/sync/timing and Node's own resolution reads the map. In both modes the script checks that the subpath exports the expected names.
General rule: a test of a package's entry points has to go through its exports map. Before publish, derive each import path from the map. After publish, import the bare specifier from an installed copy.
Receipt: barmark commit 5d0f617 (scripts/smoke-sync.mjs, .github/workflows/release.yml; FINDINGS.md, "A path import never reads exports").