/** * The suite as one graph, in the artifact. * * Every other part of a run report is *one subject against its baseline* — two * revisions, one thing. This is the other axis, and it is the one a suite of * examples has always implied and never written down: * * > A visual-regression example is a component built from components. The example * > *is* a component, at a boundary; the same component appears again, with the * > same or different props, inside larger examples. * * Once the boundaries are addressable, the run can say which of its examples are * watching literally the same bytes, which of them disagree at one commit, and — * for anything that moved — what in the run explains it. None of that needs a * second render, a second image, or a store: it is a fold over digests the * collection already produced. * * ## Why the shapes here are smaller than the ones that produced them * * `@variance-authority/core`'s `Composition` carries every site of every * rendering, which is one entry per component boundary per subject — tens of * thousands of objects on a real suite, and a report is a file people open. What * survives into the artifact is what a *sentence* needs: the names, the counts, * and the subject lists that let a reader go and look. A consumer that wants the * full graph recomputes it from the snapshots, which is the same bargain the * report already makes about masks and PNGs. * * The one thing kept in full is the movement list, because it is short by * construction — only components that moved — and because it is the part somebody * acts on. */ /** A component's census entry: where it is, what it is inside, what it reads. */ export interface ComponentRecord { readonly component: string; /** Subjects holding at least one boundary of it, in plan order. */ readonly subjects: readonly string[]; /** Boundaries summed across every subject. Distinct from `subjects.length`. */ readonly instances: number; /** * Subjects whose shallowest attributed boundary is this component. * * The narrow example — the subject that exists to *show* this thing rather than * a page that happens to contain it. Empty is a real answer and a common one: a * component that appears only inside pages has no example, which is the gap a * reviewer is usually looking for. */ readonly examples: readonly string[]; /** Components that enclose it somewhere, sorted. The graph, upwards. */ readonly within: readonly string[]; /** * Components that **mounted** it somewhere, sorted. Usually the useful edge. * * `within` is where the boundary sits and this is who wrote the element, and in * a real application they are mostly different: measured on * `examples/todomvc`, every `Chip` is `within: ["Stack"]` and * `createdBy: ["TodoFooter"]`: the `Stack` that encloses a chip is a layout * primitive that knows nothing about it, and the footer that wrote the element * stands one boundary further out. A component that renders nothing but other * components owns no DOM node at all, is a boundary nowhere, and would be * absent from this graph entirely if only `within` were recorded — while being * the file a reviewer has to open. * * Empty on a production build, where React's `_debugOwner` is gone. Empty is * *not* "nothing mounted it". */ readonly createdBy: readonly string[]; /** Components it encloses somewhere, sorted. The graph, downwards. */ readonly renders: readonly string[]; /** Custom properties its own nodes resolve through, anywhere in the suite. */ readonly tokens: readonly string[]; /** * Distinct props digests it was rendered with, and distinct renderings it * produced. * * Two numbers rather than the classes themselves, because the classes are * digests and a digest tells a human nothing. What the pair says is worth a * line: `variants: 4, renderings: 4` is a component whose output is a function * of its input, and `variants: 1, renderings: 3` is one whose output is not — * which is what `divergences` is about. * * `variants` counts the unknown-props class as one when it occurred, because * instances with no provenance are a group this report can describe and not one * it may join on. */ readonly variants: number; readonly renderings: number; } /** * One rendering, several subjects. The dots, connected. * * The finding is a rendering that survives being mounted somewhere else — three * identical chips in one list say nothing, and the same chip in a chip story and * in a page footer says that a reviewer looking at two diffs is looking at one. */ export interface EchoRecord { readonly component: string; /** The joining digest: the four content digests of the boundary, together. */ readonly rendering: string; /** At least two, in plan order. */ readonly subjects: readonly string[]; /** Boundaries, which is at least `subjects.length`. */ readonly sites: number; /** The narrow example among them, when the suite has one. */ readonly example?: string; } /** * The same component, the same **inputs**, more than one rendering. At one commit. * * Not a regression — there is no baseline anywhere in it. It says the component's * own inputs do not determine its output, which is either a fact about the design * (a token, a theme, an ancestor's cascade) or a reading that is not repeatable. * `bands` says which kind, in the vocabulary a sensitivity absorbs, so a * divergence entirely inside a relaxed band can be dismissed without opening it. * * *Inputs*, not *props*: a props digest excludes `children`, and `divergencesOf` * in `core` refuses every pair the exclusion could explain. An empty list is a * real and common answer — `examples/todomvc` produces exactly zero — and it * means nothing in the suite rendered two ways from one input. */ export interface DivergenceRecord { readonly component: string; readonly bands: readonly string[]; /** * The subjects behind each distinct rendering, widest group first. At least two. * * Grouped rather than counted, and that is the whole record. A divergence says * one input produced more than one output, and the only sentence a reader can * act on is *which* subjects sit on each side of it — `price` and `receipt` * render one way, `promo` renders another, so `promo` is the one to open. A * count with a flat subject list beside it, which is what this was, names every * subject involved and withholds the only thing distinguishing them: the reader * has three names, knows two of them agree, and cannot tell which two. * * Deduplicated per group, because a rendering reached twice in one subject is * one subject to go and look at. `renderings.length` is the count this replaced. */ readonly renderings: readonly (readonly string[])[]; /** * Why each rendering after the first parted from it. * * The sentence, under the split. `renderings` says *promo is the odd one out*; * this says *`Price` inherited a different `color` — an ancestor declared it*, * which is the difference between a reader knowing where to look and knowing * what they will find when they get there. * * Absent when the run kept no documents to read. Empty is never written. */ readonly partings?: readonly PartingRecord[]; } /** One rendering of a divergence, spoken. */ export interface PartingRecord { /** Index into {@link DivergenceRecord.renderings}, always at least 1. */ readonly rendering: number; /** Already phrased for a reader; the first line is the triage slice. */ readonly lines: readonly string[]; } /** * Why one component moved in one subject, and what the suite held against it. * * The ladder is `edited` → `token` → `upstream` → `contradicted` → `unexplained`, * and the last one is the finding. See * [`flakiness.md`](https://variance-authority.dev/docs/flakiness) for what it is and is not allowed * to conclude. */ export interface MovementRecord { readonly subject: string; readonly component: string; /** Empty means *not known* — a name-only comparison — never *no band*. */ readonly bands: readonly string[]; readonly cause: 'edited' | 'token' | 'upstream' | 'contradicted' | 'unexplained'; /** One sentence, naming the evidence rather than the category. */ readonly because: string; readonly file?: string; readonly tokens?: readonly string[]; readonly upstream?: string; /** * Components between `upstream` and this one, outermost first. * * Present only on the `upstream` rung, and absent when the edited component * draws this one directly. A reviewer sent to `ProductCard` because a * `CardFooter` moved has exactly one question next — *how does that reach * this* — and the answer is a couple of names the run already walked. */ readonly through?: readonly string[]; /** Other subjects this same component moved in. The "one cause, N subjects" fold. */ readonly alsoIn: readonly string[]; /** * Subjects where the same component, with the same props, did **not** move. * * The control group — the *stable states to refer to*. Empty means the suite * offered no control, which weakens an unexplained movement rather than * strengthening it, and is why this is a list and not a flag. */ readonly held: readonly string[]; /** * Renders of this component with the same props, other than this one, that the * run read. * * The denominator `held` is a numerator of. Without it an empty control group * is two opposite findings wearing one shape — nothing to compare against, or a * comparison made in every render and answering the same way — and a reader * that guesses picks the wrong one on any suite where the change reached * everything. * * Optional because a report written before this was carried has no answer, and * `0` is an answer. */ readonly compared?: number; /** * For an unexplained movement only: whether the subject was already proven * unstable in this run. * * `flake` means the subject failed to read the same way twice *and* nothing in * the run explains what moved — the two halves of the sentence, together, for * the first time. `suspect` means nobody has read it twice yet, so it is a * shortlist entry and not a verdict. * * Absent on every explained movement, where the question does not arise. */ readonly standing?: 'flake' | 'suspect'; } /** * One subject's boundaries, in document order, identical rows folded together. * * Document order is what makes the indentation a tree: every row's enclosing * boundary is the nearest row above it with a smaller depth. Nothing about the * order is a rank. */ export interface SubjectStructureRecord { readonly subject: string; readonly rows: readonly BoundaryRow[]; /** * The narrower subjects inside it and the larger ones holding it. * * Absent from a report written before it was carried. */ readonly pieces?: SubjectPiecesRecord; } /** * One subject read as the narrower subjects inside it, over renderings. * * Its footprint is the distinct renderings it holds — a component and the * digest of what it rendered — outside structure. A piece is a smaller subject * with at least nine tenths of its footprint inside this one, a whole a larger * one holding at least nine tenths of this one's. What no piece renders is * split by component: its own, where no piece mounts the component, and in * context, where a piece mounts it and renders it otherwise. */ export interface SubjectPiecesRecord { readonly footprint: number; /** Structural components it mounts. */ readonly structure: number; /** Subjects with exactly its footprint. */ readonly alike: readonly string[]; /** Smaller subjects inside it, most shared first. */ readonly pieces: readonly SubjectShareRecord[]; /** Larger subjects holding it, smallest first. */ readonly wholes: readonly SubjectShareRecord[]; /** Renderings of its footprint some piece holds. */ readonly explained: number; readonly own: readonly ResidueRecord[]; readonly inContext: readonly ResidueRecord[]; } /** Another subject one subject shares renderings with: a piece inside it, or a whole holding it. */ export interface SubjectShareRecord { readonly subject: string; readonly footprint: number; /** Renderings of the subject read that it holds too. */ readonly shared: number; } /** Renderings of one component that no piece holds. */ export interface ResidueRecord { readonly component: string; readonly renderings: number; /** The pieces that mount the component and render it otherwise. Absent on a component no piece mounts. */ readonly pieces?: readonly string[]; } /** Boundaries of one component at one place in one subject's tree. */ export interface BoundaryRow { readonly component: string; /** Boundaries between this one and the subject root. The root is `0`. */ readonly depth: number; /** The enclosing boundary's component. Absent on the subject root. */ readonly within?: string; /** Who wrote the element. Absent on a production build, which is not *nobody*. */ readonly createdBy?: string; /** Boundaries folded into this row. At least 1. */ readonly count: number; /** * Distinct props digests among them. `count: 3, variants: 2` is three chips * of which one received different inputs — the selected one — said without * printing a digest nobody can read. */ readonly variants: number; } /** * What one run learned by comparing its subjects to each other. * * Absent from a report whose collection produced no semantic snapshots — a * raster-only tier has no boundaries to join, and an empty graph would read as * "this suite shares nothing", which is a different and false claim. */ export interface CompositionReport { /** Subjects that contributed a snapshot, in plan order. The denominator. */ readonly subjects: readonly string[]; /** Sorted by name, code-unit order. */ readonly components: readonly ComponentRecord[]; /** Widest first. Capped — see `truncated`. */ readonly echoes: readonly EchoRecord[]; /** Sorted by component name. */ readonly divergences: readonly DivergenceRecord[]; /** Every component the run found moved, in the order the observations came. */ readonly movements: readonly MovementRecord[]; /** * Each subject's boundaries, as a tree collapsed to its distinct rows. * * The graph above is component-first and loses *which subject* holds what * under what; this is the same walk kept subject-first, so a reader can recall * one subject's composition without the snapshot. Rows rather than boundaries * — three chips under one `Stack` from one `TodoFooter` are one row counted * three — because that is what keeps it the size of a report on a suite with * hundreds of subjects. * * Absent from a report written before it was carried. Never empty when present. */ readonly structure?: readonly SubjectStructureRecord[]; /** * What was left out of the lists above, when anything was. * * A cap that says nothing is a cap that reads as coverage. Present only when a * list was shortened, and counts what did not make it rather than what did. */ readonly truncated?: { readonly echoes: number; }; } //# sourceMappingURL=composition.d.ts.map