# 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.

| Fixture            | Required behavior                                                                                                                                                                                                        |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Inactive           | All three portable entries resolve `sdk/*/dist/index.js`; no SDK source alias, eager React/UI, CSS, or external dependency                                                                                               |
| Optional UI        | Component code/React occur in deferred chunks; inactive closure remains portable and no stylesheet is imported                                                                                                           |
| Optional styled UI | Layout and default skin assets exist only behind deferred edges; eager CSS is rejected even when deferred styles are allowed                                                                                             |
| Selective adapters | Selected controlled adapter survives the build; an unselected adapter is not loaded; a both-adapters control proves detection                                                                                            |
| Plain host         | Real `examples/plain-html/index.html` build contains no React/React DOM module or eager UI dependency                                                                                                                    |
| Harvesters         | Supported 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:

| Fixture                     | Eager JS bytes | Total JS bytes | CSS bytes |
| --------------------------- | -------------: | -------------: | --------: |
| Inactive shells             |            223 |            223 |         0 |
| Optional component          |          2,602 |         70,986 |         0 |
| Optional styled component   |          2,800 |         71,184 |    24,111 |
| Selected controlled adapter |             47 |             47 |         0 |

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](https://vite.dev/guide/api-plugin)
and [build options](https://vite.dev/config/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.
