SnagBadgerA little help for every snag.

Built consumer checks#

Run pnpm build && pnpm check:consumers, or the complete pnpm verify. The consumer gate runs after all production builds and existing checks. It uses the pinned Vite 8.3.4 / TypeScript 6.0.3 / Node 24.21.0 toolchain, with no new external dependency. Root workspace links expose capture and embedding as well as reporting to the consumer fixtures. No source aliases are passed to the bundler.

Evidence and invariants#

tooling/consumers/check.mts creates isolated input files under dist/, builds real ESM output, and checks the bundler's emitted module/chunk graph and CSS metadata. It records the loaded module IDs separately from emitted chunks, follows static chunk imports for the inactive closure, and preserves dynamic imports as optional edges. Node built-ins remain external imports, making cross-environment dependencies explicit instead of accepting a browser shim. Unexpected external edges fail closed.

FixtureRequired behavior
InactiveAll three portable entries resolve sdk/*/dist/index.js; no SDK source alias, eager React/UI, CSS, or external dependency
Optional UIComponent code/React occur in deferred chunks; inactive closure remains portable and no stylesheet is imported
Optional styled UILayout and default skin assets exist only behind deferred edges; eager CSS is rejected even when deferred styles are allowed
Selective adaptersSelected controlled adapter survives the build; an unselected adapter is not loaded; a both-adapters control proves detection
Plain hostReal examples/plain-html/index.html build contains no React/React DOM module or eager UI dependency
HarvestersSupported built package export initializes without browser/Node globals, consumes strict browser declarations, omits unselected socket diagnostics against an all-adapters control, and rejects eager browser activation

The inactive TypeScript consumer is compiled with strict repository options, lib: [ES2022], and types: []. Its generated JavaScript and both inactive optional loaders are evaluated in a fresh Node process and in an isolated JavaScript VM context that supplies no browser or Node globals. The latter catches accidental reliance on globals such as navigator, which Node itself may expose. Static chunk imports are linked only inside the consumer output directory. This tests module initialization, not arbitrary future function calls or dynamic activation. Existing browser journeys test real UI activation and styles.

Five intentionally invalid builds prove the machinery rejects eager React, eager CSS, node:fs, top-level document, and ambient navigator use. These are generated fixture sources, not waived repository files or intentionally failing committed suites. Both valid and invalid controls use the same builder/evaluator. Invalid React/CSS assertions check specific diagnostic categories. All controls must produce the expected result for the gate to succeed.

The controlled adapter fixture verifies generic selection mechanics. The concrete harvester case additionally consumes @snagbadger/harvesters through its built export; its browser-specific declarations require DOM types, while importing the module remains inert in the no-globals evaluator. The all-harvesters control retains the socket diagnostic omission field, which must disappear from the HTTP-only artifact; selected HTTP diagnostic semantics must remain. These artifact checks complement actual host/lifecycle tests rather than settling product usefulness or performance budgets.

Measurements#

dist/consumers/measurements.json records emitted raw/gzip bytes, module IDs, static/dynamic edges, and CSS ownership for each fixture. Temporary input paths are normalized; production minification removes unstable source-location comments. The report and built artifacts are ignored; temporary input trees are removed even on failure. Re-run with the exact commit/toolchain/lockfile to reproduce values.

Initial measurements on this pinned branding snapshot:

FixtureEager JS bytesTotal JS bytesCSS bytes
Inactive shells2232230
Optional component2,60270,9860
Optional styled component2,80071,18424,111
Selected controlled adapter47470

These counts include consumer glue. Gzip counts are available per emitted file in the report. Sizes are descriptive observations, not pass/fail product budgets. The enforced thresholds are structural: zero forbidden eager dependencies, zero unexpected external edges, and zero CSS for functions-only/component-only fixtures. Product latency/memory/transfer budgets remain with the performance decision.

Extend the checks#

Add a case to tooling/consumers/check.mts that imports a new integration's supported package export, then asserts its intended active/inactive closure. Keep package names resolvable through workspace dependencies and build the package first. Include an unselected integration and a failing control for the promised boundary. Do not alias source, rely on names in emitted JavaScript, or replace these artifact checks with source import spelling. If a framework or dependency requires a different runtime classification, update the explicit graph policy and consumer rules together with passing/failing evidence.

Bundler behavior follows the Vite plugin API and build options. The source architecture gate remains responsible for unsupported/private imports and dependency direction; consumer checks complement it with actual compiler, bundler and runtime evidence.