Skip to content

fix: republish @amritk/mini-lynx-native with its dist, and remove the development export condition - #34

Merged
amritk merged 2 commits into
mainfrom
claude/mini-lynx-native-dist-exports-bv3iux
Aug 5, 2026
Merged

fix: republish @amritk/mini-lynx-native with its dist, and remove the development export condition#34
amritk merged 2 commits into
mainfrom
claude/mini-lynx-native-dist-exports-bv3iux

Conversation

@amritk

@amritk amritk commented Aug 5, 2026

Copy link
Copy Markdown
Owner

Summary

@amritk/mini-lynx-native@0.2.0 reached npm as a src-only tarball — files declares dist, the exports map declares ./dist/index.js, ./dist/background/index.js and ./dist/testing/index.js, and none of it is in the published package.

It was not a packing bug. The package was published by hand rather than through the release workflow, so none of the three things release:publish does before changeset publish ran. The registry metadata is unambiguous:

mini-lynx-native@0.2.0 lynx-dialogs@0.2.0
published 05:08 04:52 (the release run)
publisher amritk, node 26.3.0 GitHub Actions, trusted publisher
provenance none SLSA attestation
tarball 19 files, src only dist + LICENSE

Three misses at once: no bun run build, so no dist/; no strip-development-exports, so the development condition survived into the published manifest; no copy-license, so the tarball carries no LICENSE.

The surviving development condition is why a broken package stayed usable enough that this went unnoticed for a release. It resolves to ./src/*.ts, and src does ship, so anything honouring that condition got raw TypeScript and looked healthy. Anything that did not — plain Node, a bundler on default conditions, tsc reading types — got a resolution failure against a manifest naming files that were not in the tarball. Consumers have been working around it by forcing the condition on: a customConditions entry in tsconfig.json, a resolve condition in the bundler config, a --conditions development flag on the test command.

The repo's existing guards were never wrong about this. scripts/dist-smoke.test.ts and scripts/consumer-e2e.test.ts both cover this package and both pass. Neither runs when someone types npm publish in a package directory.

Changes

  • .changeset/mini-lynx-native-dist.md — patch bump for @amritk/mini-lynx-native, republishing the same code through the release workflow so 0.2.1 carries dist/ and its type declarations. The four @amritk/lynx-* packages pin the bridge at an exact version ("@amritk/mini-lynx-native": "0.2.0"), so their 0.2.0 releases still resolve the broken tarball; changesets bumps them alongside it and they re-pin to 0.2.1. Confirmed with a changeset version dry run.
  • scripts/check-publishable.mjs — the gate the hand-publish went through. Fails when an exports map points at a dist/ file that is not on disk, when a development condition is still present, or when the package directory has no LICENSE. Plain node and node builtins only, so it cannot be skipped by an environment without bun.
  • prepublishOnly in all eight publishable packagesmini, mini-lynx, mini-lynx-native, mini-helpers and the four lynx-*. prepublishOnly rather than prepack deliberately: consumer-e2e packs synthetic copies with npm pack from a scratch directory, and a prepack hook would fire there against a tree that has no ../../scripts.
  • scripts/check-publishable.test.ts — seven cases, including the exact shape that shipped (a manifest promising dist against a tree with none) and the vacuous-pass case (an exports map with no dist target at all).

Testing

Beyond the suites below, the publish path was simulated end to end: resolve-workspace-protocolstrip-development-exportscopy-license, then npm publish --dry-run in packages/mini-lynx-native. It packs 52 files including all of dist/ and LICENSE, with prepublishOnly running and passing. With dist/ moved aside, the same command fails and names all six missing export targets.

  • bun run test passes (932 tests)
  • bun run check passes
  • bun run check:reactivity passes
  • bun run types:check passes
  • bun run build passes
  • Changeset added (bunx changeset) if this affects a published package

Also run: bun run test:dist (57 tests, up from 50), bun run check:ai-docs, and the generate-llms diff.

Related issues


Generated by Claude Code

📦 Bundle-size delta vs main (9c6b6bc6516dd0)

Package Entry main PR Δ
mini core (.) 3,156 B 3,156 B +0 B (+0.0%) ⚪
mini flow 4,083 B 4,083 B +0 B (+0.0%) ⚪
mini router 4,259 B 4,259 B +0 B (+0.0%) ⚪
mini forms 4,259 B 4,259 B +0 B (+0.0%) ⚪
mini query 1,988 B 1,988 B +0 B (+0.0%) ⚪
mini-lynx core (.) 5,605 B 5,605 B +0 B (+0.0%) ⚪
mini-lynx flow 4,981 B 4,981 B +0 B (+0.0%) ⚪
mini-lynx router 6,130 B 6,130 B +0 B (+0.0%) ⚪
mini-lynx forms 5,400 B 5,400 B +0 B (+0.0%) ⚪
mini-lynx query 1,990 B 1,990 B +0 B (+0.0%) ⚪
mini-lynx testing 2,464 B 2,464 B +0 B (+0.0%) ⚪

Gzipped bytes of each bundled entry (esbuild, minified, browser/es2022, optional peer deps external). Bundling is deterministic, so unlike a timed benchmark these numbers carry no noise and every Δ is exact. ⚪ within ±0.5% · 🟢 smaller · 🔴 larger. Each package's core (.) must stay flat as subpath features land — that is the whole charter, and src/core-size-budget.test.ts holds the absolute ceiling.

claude added 2 commits August 5, 2026 08:04
…shing on it

`@amritk/mini-lynx-native@0.2.0` reached npm as a src-only tarball. It was
published by hand rather than through the release workflow, so none of the
three things that workflow does before `changeset publish` ran: no
`bun run build`, so no `dist/`; no `strip-development-exports`, so the
`development` condition survived; no `copy-license`, so no LICENSE. The
manifest still declared `./dist/index.js`, `./dist/background/index.js` and
`./dist/testing/index.js`, and `files` still listed `dist` — all of it
pointing at nothing.

The surviving `development` condition is why that was survivable rather than
fatal, and why it went unnoticed for a release: it resolves to `./src/*.ts`,
and `src` does ship, so anything honouring the condition got raw TypeScript
and looked healthy. Plain Node, a bundler on default conditions and `tsc`
reading `types` all got a resolution failure instead.

The patch changeset republishes the same code through the release workflow.
The four `@amritk/lynx-*` packages pin the bridge at an exact version, so
their 0.2.0 releases still resolve the broken tarball; changesets bumps them
alongside it and they re-pin to 0.2.1.

`scripts/check-publishable.mjs` closes the hole the hand-publish went
through. It runs as `prepublishOnly` in every publishable package and fails
the publish when an exports map points at a `dist/` file that is not on disk,
when a `development` condition is still present, or when the package
directory has no LICENSE — the three misses above, checked at the one moment
that catches them regardless of who is publishing or from where. The repo
already checks this ground in dist-smoke and consumer-e2e; neither runs on a
laptop typing `npm publish`.
…checking

The condition let the workspace resolve its own packages to source with no
prior build. It did that by living in each package's `exports` map — the one
file that ships — so it was reachable from a published tarball, and
`@amritk/mini-lynx-native@0.2.0` shipped one. Pointing at `./src/*.ts` inside
a tarball that also ships `src`, it resolved consumers to raw TypeScript and
made a package with no `dist/` at all look healthy.

Packages now resolve each other through `types`/`import` like any consumer,
and CI builds before it type-checks. That is the whole trade: `types:check`
and editor go-to-definition need `bun run build` first, and nothing that
cannot ship is left inside a manifest.

Everything the condition was load-bearing for keeps working through
repo-local mechanisms:

- Tests never used it. `vitest.config.ts` already aliases every workspace
  package to its `src/`, which is why `bun run test` is untouched here.
- `scripts/ai-docs.ts` read it to find each subpath's source. It now derives
  the source from the built target — `./dist/x/index.js` is `./src/x/index.ts`
  by construction, since every package builds `rootDir: src` to `outDir: dist`.
  Verified to map the same 30 sources the condition named.
- `scripts/bench-compare.ts` bundled with `conditions: ['development']` so a
  baseline checkout could be measured without a build. An esbuild resolver
  plugin does it by path instead, and produces byte-identical sizes on all 11
  entries.
- The playgrounds pinned it in `vite.config.ts` and `tsconfig.json`. They
  resolve `dist` now, so `deploy-playgrounds.yml` builds the packages first.

`scripts/strip-development-exports.ts` and its test are deleted, and the step
is gone from `release:publish` — there is nothing left to strip.
`resolve-workspace-protocol` and `copy-license` stay: npm cannot install a
`workspace:*` specifier, and it only bundles a LICENSE from inside the package
directory. `check-publishable.mjs` keeps its check for the condition, now as a
regression guard — reintroducing one would restore exactly the failure above,
and the publish is the last place to catch it.
@amritk amritk changed the title fix: republish @amritk/mini-lynx-native with its dist, and gate publishing on it fix: republish @amritk/mini-lynx-native with its dist, and remove the development export condition Aug 5, 2026
@amritk
amritk merged commit 15bc459 into main Aug 5, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants