Skip to content

Complete the public type surface and narrow closed-union output fields - #172

Merged
jantimon merged 1 commit into
mainfrom
fix/type-surface-completeness
Jul 30, 2026
Merged

Complete the public type surface and narrow closed-union output fields#172
jantimon merged 1 commit into
mainfrom
fix/type-surface-completeness

Conversation

@jantimon

@jantimon jantimon commented Jul 30, 2026

Copy link
Copy Markdown
Owner

What

Two additive / output-narrowing changes to the public type surface (src/index.ts), shipping as a minor. No runtime behaviour changes.

1. Export the transitively-referenced fact types

The AGENTS.md promise is "import them, do not hand-roll": every type named inside an exported type should itself be importable. It was not. This re-exports the full transitive closure from the package root:

  • Core barrel: CaptureMode, TargetLane, WorkloadLane, WorkloadIdentity, Measured, CpuBreakdown/CpuSlice/CpuJsSlice, LayoutShift(+Source/Rect), EngineSoftNav, SoftNavRoute(+Lcp), ThrashReport/ThrashStep/DirtiedByWrite/DirtiedByWriteRollup/FirefoxDirtiedByReport, SourceMapDiagnostics/SourceMapFailure.
  • View homes: SpanCountsEntry (query), SoftNavVerdict/SoftNavAgreement (soft-nav), FrameFloorMatch (frame-floor).
  • Raw profile: RawProfileNode/RawCallFrame/GeckoSlice.
  • Driver: StepOpts/Until (StepOpts gains export).

Eight of these (Measured, WorkloadLane, SoftNavAgreement, RawProfileNode, RawCallFrame, GeckoSlice, StepOpts, Until) were surfaced by the guard test below beyond the initial list.

2. Narrow the closed-union output fields off string

  • RecordingMeta.capture and GroupSpanMember.mode -> the CaptureMode union, extended in record/capture.ts with the node-cpu/node-alloc node lanes (no precise-wall arm; the schema-5 epoch gate rejects any pre-5 recording that could carry it).
  • SpansResult.target / SpanAnatomy.target / GroupSpanStitch.target -> "chrome" | "firefox" | "node" (a new TargetLane, now the return type of recordingLane).

Reading these fields now yields the exact literal type instead of a bare string. The spec-open fields (LoafScript.invokerType, EngineSoftNav.navigationTypes, SoftNavRoute.navigationType, meta.target) are deliberately left as string.

RecordingMeta gains a doc note that it carries no kind discriminant (the field GroupMeta stamps "run-group" to mark the sibling manifest).

3. Guard test

test/unit/export-completeness.test.mjs walks the emitted dist/*.d.ts with the TypeScript compiler API and fails if any type reachable from a public export is not itself exported. Browser-free, no new dependency (uses the TypeScript already used to build). This is what a dropped or forgotten re-export trips on, instead of a consumer's red build.

Notes

  • The GroupSpanMember.mode narrowing needs a boundary cast (member.mode as CaptureMode): the run-group manifest's GroupMember.mode stays the wider string (out of scope here), but its value is the member recording's meta.capture verbatim, which is now a CaptureMode.
  • Model files take a type-only import { CaptureMode } from record/capture.ts; it erases in emit (verified: no runtime require in dist/model/meta.js), so there is no runtime cycle.

Gates

build, lint, format:check, knip, unit (686 tests, incl. the new guard), e2e (Chrome), and measurement all pass.

Every type named inside an exported type is now itself exported from the
package root, so a consumer can import the type of any field instead of
hand-rolling its shape. Adds the transitively-referenced fact types
(CaptureMode, TargetLane, WorkloadLane, Measured, the CPU-breakdown slices,
the layout-shift / soft-nav / thrash / dirtied-by / sourcemap shapes, the raw
profile node/frame types, and the driver StepOpts/Until) as named re-exports.

Narrow the closed-union output fields off `string`: RecordingMeta.capture and
GroupSpanMember.mode to the capture-mode union (extended in record/capture.ts
with the node-cpu/node-alloc lanes), and the query views' `target` to
"chrome" | "firefox" | "node" (a new TargetLane, the return of recordingLane).
RecordingMeta gains a doc note that it carries no `kind` discriminant, the
field that marks the sibling run-group manifest.

A new unit guard (test/unit/export-completeness.test.mjs) walks the emitted
declarations with the TypeScript API and fails if any type reachable from a
public export is not itself exported, so this cannot silently regress.
@jantimon
jantimon merged commit dc97fda into main Jul 30, 2026
4 checks passed
@jantimon
jantimon deleted the fix/type-surface-completeness branch July 30, 2026 06:17
@github-actions github-actions Bot mentioned this pull request Jul 30, 2026
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.

1 participant