Skip to content

test: guard the published tarball, and index examples/ from the README - #24

Merged
Hugoer merged 2 commits into
mainfrom
test/tarball-smoke-and-examples-index
Sep 4, 2026
Merged

test: guard the published tarball, and index examples/ from the README#24
Hugoer merged 2 commits into
mainfrom
test/tarball-smoke-and-examples-index

Conversation

@Hugoer

@Hugoer Hugoer commented Sep 4, 2026

Copy link
Copy Markdown
Owner

Closes #23.

type-tests/ guards the published .d.ts at compile time; examples/ is the only thing that
exercises the package at runtime. They are complementary — one never executes, the other never
type-checks — so this keeps both and fixes what was actually wrong with each.

lib/package-tarball.test.js — the tarball is now checked

Both existing consumer guards read the working tree, so neither consults package.json "files":

  • examples/ installs with file:.., which symlinks the repo root rather than packing it
    (examples/node_modules/@hugoer/web-perf-cli -> ../../..).
  • type-tests/ resolves the package through self-reference. That honours the "exports" map,
    but against the working tree, not a tarball.

lib/package-exports.test.js does not cover it either: require('.') from inside lib/ lands on
lib/index.js directly and never consults the root "exports" map or any subpath.

So a lib/ module left out of "files" resolved everywhere, passed CI, and would have broken only
for a real consumer after npm publish. The new test packs the tarball, extracts it, and requires
all ten published subpaths by name from a directory where the extracted copy is the installed
dependency, so resolution runs through "exports" as a consumer's would. It also asserts every
"exports"/"main"/"types"/"bin" target ships, and that every dependency the packed runtime code
requires is declared.

Dependencies are symlinked from node_modules rather than installed, which keeps it offline and
at ~2s rather than a full network install. The trade-off is that an undeclared dependency would
still resolve at load time, which is why the declaration check reads the packed source separately.

It was verified to fail, not just to pass — three mutations, each caught:

Mutation Caught by
types/ dropped from "files" ships every file "exports" points at; ships the entrypoints
./variance pointed at a missing file ships every file "exports" points at; …/variance loads
require('left-pad') added to lib/logger.js declares every dependency; three subpaths fail to load

README — examples/ is reachable at last

21 scripts, ~1,180 lines, all tracked in git, and grep -n "examples/" README.md returned
nothing. A new "Runnable examples" section under Library API links each script to what it
demonstrates, grouped by command family, and records the two conventions that make the directory
navigable: -audit prints and writes nothing, -save writes JSON under results/.

Behaviour changes

None. No lib/ source file is touched, no export, signature or return shape moves, and the
generated types/ are unchanged (git diff --exit-code -- types/ is clean after a regen). The
change is one new test file, one new README section, and a paragraph in CLAUDE.md recording why
the tarball guard exists so it does not get deleted as redundant.

Verification

  • npm run lint clean; npm test 578 passed across 16 files (13 new)
  • npm run generate-types produces no drift; npm run check-types compiles
  • examples/crux-audit.js and examples/crux-save.js run against the live CrUX API
  • Every link in the new section resolves, and all 21 scripts appear exactly once

Both consumer-facing guards read the working tree, so neither consults
package.json "files": examples/ installs with `file:..`, which symlinks the
repo root rather than packing it, and type-tests/ resolves the package by
self-reference. A lib/ module left out of "files" resolves in both, passes
CI, and breaks only for a real consumer after publish.

package-exports.test.js does not close the gap either — `require('.')` from
inside lib/ lands on lib/index.js directly and never consults the root
"exports" map or any subpath.

This packs the tarball, extracts it, and requires all ten published subpaths
by name from a directory where the extracted copy is the installed
dependency, so resolution runs through "exports" as a consumer's would. The
package's own dependencies are symlinked from node_modules rather than
installed, keeping the test offline and under two seconds; the trade-off is
that an undeclared dependency would still resolve, so a separate assertion
reads the packed source for that.

Verified to fail on three mutations: types/ dropped from "files", an exports
subpath pointed at a missing file, and an undeclared require added to
runtime code.

Refs #23
examples/ holds 21 scripts, ~1,180 lines, all tracked in git, and the README
never mentioned them once — `grep -n "examples/" README.md` returned nothing,
so no reader was ever pointed at them and their documentation value was zero.

Adds a "Runnable examples" section under Library API that links every script
to what it demonstrates, grouped by command family. It also writes down the
two conventions that make the directory navigable rather than a matrix of
21 similar filenames: `-audit` prints and writes nothing, `-save` writes JSON
under results/. Prerequisites are stated per family — Chrome for lab, an API
key for psi/crux/crux-history.

Verified by running examples/crux-audit.js and examples/crux-save.js against
the live API, and by checking every link resolves and every one of the 21
scripts appears exactly once.
@Hugoer
Hugoer merged commit 71d1a70 into main Sep 4, 2026
6 checks passed
@Hugoer
Hugoer deleted the test/tarball-smoke-and-examples-index branch September 4, 2026 07:57
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.

examples/ is unreachable from the README, and nothing verifies the published tarball

1 participant