@variance-authority/core 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +79 -0
- package/LICENSE +21 -0
- package/README.md +169 -0
- package/dist/artifact.d.ts +47 -0
- package/dist/artifact.js +2 -0
- package/dist/artifact.js.map +1 -0
- package/dist/attribute/because.d.ts +43 -0
- package/dist/attribute/because.js +64 -0
- package/dist/attribute/because.js.map +1 -0
- package/dist/attribute/boundary.d.ts +191 -0
- package/dist/attribute/boundary.js +328 -0
- package/dist/attribute/boundary.js.map +1 -0
- package/dist/attribute/call-site.d.ts +96 -0
- package/dist/attribute/call-site.js +183 -0
- package/dist/attribute/call-site.js.map +1 -0
- package/dist/attribute/component-hash.d.ts +188 -0
- package/dist/attribute/component-hash.js +312 -0
- package/dist/attribute/component-hash.js.map +1 -0
- package/dist/attribute/composition-fixture.d.ts +18 -0
- package/dist/attribute/composition-fixture.js +53 -0
- package/dist/attribute/composition-fixture.js.map +1 -0
- package/dist/attribute/composition.d.ts +182 -0
- package/dist/attribute/composition.js +169 -0
- package/dist/attribute/composition.js.map +1 -0
- package/dist/attribute/control.d.ts +43 -0
- package/dist/attribute/control.js +54 -0
- package/dist/attribute/control.js.map +1 -0
- package/dist/attribute/coverage.d.ts +99 -0
- package/dist/attribute/coverage.js +86 -0
- package/dist/attribute/coverage.js.map +1 -0
- package/dist/attribute/divergence.d.ts +102 -0
- package/dist/attribute/divergence.js +152 -0
- package/dist/attribute/divergence.js.map +1 -0
- package/dist/attribute/index.d.ts +43 -0
- package/dist/attribute/index.js +29 -0
- package/dist/attribute/index.js.map +1 -0
- package/dist/attribute/instability.d.ts +89 -0
- package/dist/attribute/instability.js +183 -0
- package/dist/attribute/instability.js.map +1 -0
- package/dist/attribute/instances.d.ts +145 -0
- package/dist/attribute/instances.js +140 -0
- package/dist/attribute/instances.js.map +1 -0
- package/dist/attribute/locate.d.ts +22 -0
- package/dist/attribute/locate.js +100 -0
- package/dist/attribute/locate.js.map +1 -0
- package/dist/attribute/mask.d.ts +156 -0
- package/dist/attribute/mask.js +201 -0
- package/dist/attribute/mask.js.map +1 -0
- package/dist/attribute/movement.d.ts +211 -0
- package/dist/attribute/movement.js +181 -0
- package/dist/attribute/movement.js.map +1 -0
- package/dist/attribute/region.d.ts +188 -0
- package/dist/attribute/region.js +144 -0
- package/dist/attribute/region.js.map +1 -0
- package/dist/attribute/source-map.d.ts +98 -0
- package/dist/attribute/source-map.js +264 -0
- package/dist/attribute/source-map.js.map +1 -0
- package/dist/attribute/source.d.ts +77 -0
- package/dist/attribute/source.js +95 -0
- package/dist/attribute/source.js.map +1 -0
- package/dist/attribute/stack.d.ts +80 -0
- package/dist/attribute/stack.js +108 -0
- package/dist/attribute/stack.js.map +1 -0
- package/dist/compare/band.d.ts +85 -0
- package/dist/compare/band.js +70 -0
- package/dist/compare/band.js.map +1 -0
- package/dist/compare/cascade.d.ts +45 -0
- package/dist/compare/cascade.js +71 -0
- package/dist/compare/cascade.js.map +1 -0
- package/dist/compare/derive.d.ts +78 -0
- package/dist/compare/derive.js +39 -0
- package/dist/compare/derive.js.map +1 -0
- package/dist/compare/diff/attribution.d.ts +23 -0
- package/dist/compare/diff/attribution.js +244 -0
- package/dist/compare/diff/attribution.js.map +1 -0
- package/dist/compare/diff/compare-nodes.d.ts +15 -0
- package/dist/compare/diff/compare-nodes.js +227 -0
- package/dist/compare/diff/compare-nodes.js.map +1 -0
- package/dist/compare/diff/components.d.ts +20 -0
- package/dist/compare/diff/components.js +76 -0
- package/dist/compare/diff/components.js.map +1 -0
- package/dist/compare/diff/delta.d.ts +165 -0
- package/dist/compare/diff/delta.js +12 -0
- package/dist/compare/diff/delta.js.map +1 -0
- package/dist/compare/diff/index.d.ts +69 -0
- package/dist/compare/diff/index.js +101 -0
- package/dist/compare/diff/index.js.map +1 -0
- package/dist/compare/diff/match.d.ts +37 -0
- package/dist/compare/diff/match.js +167 -0
- package/dist/compare/diff/match.js.map +1 -0
- package/dist/compare/explain.d.ts +23 -0
- package/dist/compare/explain.js +190 -0
- package/dist/compare/explain.js.map +1 -0
- package/dist/compare/holding-diff.d.ts +25 -0
- package/dist/compare/holding-diff.js +74 -0
- package/dist/compare/holding-diff.js.map +1 -0
- package/dist/compare/impact.d.ts +56 -0
- package/dist/compare/impact.js +124 -0
- package/dist/compare/impact.js.map +1 -0
- package/dist/compare/index.d.ts +30 -0
- package/dist/compare/index.js +22 -0
- package/dist/compare/index.js.map +1 -0
- package/dist/compare/instance.d.ts +30 -0
- package/dist/compare/instance.js +145 -0
- package/dist/compare/instance.js.map +1 -0
- package/dist/compare/observability.d.ts +49 -0
- package/dist/compare/observability.js +57 -0
- package/dist/compare/observability.js.map +1 -0
- package/dist/compare/parting.d.ts +179 -0
- package/dist/compare/parting.js +244 -0
- package/dist/compare/parting.js.map +1 -0
- package/dist/compare/slice.d.ts +74 -0
- package/dist/compare/slice.js +79 -0
- package/dist/compare/slice.js.map +1 -0
- package/dist/compare/value.d.ts +60 -0
- package/dist/compare/value.js +111 -0
- package/dist/compare/value.js.map +1 -0
- package/dist/format/accessibility.d.ts +27 -0
- package/dist/format/accessibility.js +17 -0
- package/dist/format/accessibility.js.map +1 -0
- package/dist/format/canonical.d.ts +38 -0
- package/dist/format/canonical.js +65 -0
- package/dist/format/canonical.js.map +1 -0
- package/dist/format/capture.d.ts +212 -0
- package/dist/format/capture.js +2 -0
- package/dist/format/capture.js.map +1 -0
- package/dist/format/document.d.ts +279 -0
- package/dist/format/document.js +65 -0
- package/dist/format/document.js.map +1 -0
- package/dist/format/environment.d.ts +120 -0
- package/dist/format/environment.js +53 -0
- package/dist/format/environment.js.map +1 -0
- package/dist/format/hash.d.ts +45 -0
- package/dist/format/hash.js +41 -0
- package/dist/format/hash.js.map +1 -0
- package/dist/format/holding.d.ts +134 -0
- package/dist/format/holding.js +2 -0
- package/dist/format/holding.js.map +1 -0
- package/dist/format/index.d.ts +39 -0
- package/dist/format/index.js +25 -0
- package/dist/format/index.js.map +1 -0
- package/dist/format/intervention.d.ts +87 -0
- package/dist/format/intervention.js +11 -0
- package/dist/format/intervention.js.map +1 -0
- package/dist/format/profile.d.ts +59 -0
- package/dist/format/profile.js +66 -0
- package/dist/format/profile.js.map +1 -0
- package/dist/format/provenance.d.ts +179 -0
- package/dist/format/provenance.js +222 -0
- package/dist/format/provenance.js.map +1 -0
- package/dist/format/sha256.d.ts +34 -0
- package/dist/format/sha256.js +152 -0
- package/dist/format/sha256.js.map +1 -0
- package/dist/format/snapshot.d.ts +263 -0
- package/dist/format/snapshot.js +2 -0
- package/dist/format/snapshot.js.map +1 -0
- package/dist/format/stabilize.d.ts +190 -0
- package/dist/format/stabilize.js +333 -0
- package/dist/format/stabilize.js.map +1 -0
- package/dist/format/tier.d.ts +19 -0
- package/dist/format/tier.js +11 -0
- package/dist/format/tier.js.map +1 -0
- package/dist/format/value.d.ts +115 -0
- package/dist/format/value.js +182 -0
- package/dist/format/value.js.map +1 -0
- package/dist/format/wiring.d.ts +115 -0
- package/dist/format/wiring.js +60 -0
- package/dist/format/wiring.js.map +1 -0
- package/dist/index.d.ts +35 -0
- package/dist/index.js +35 -0
- package/dist/index.js.map +1 -0
- package/dist/judge/docket.d.ts +85 -0
- package/dist/judge/docket.js +162 -0
- package/dist/judge/docket.js.map +1 -0
- package/dist/judge/fingerprint.d.ts +53 -0
- package/dist/judge/fingerprint.js +144 -0
- package/dist/judge/fingerprint.js.map +1 -0
- package/dist/judge/ignore.d.ts +206 -0
- package/dist/judge/ignore.js +237 -0
- package/dist/judge/ignore.js.map +1 -0
- package/dist/judge/index.d.ts +44 -0
- package/dist/judge/index.js +34 -0
- package/dist/judge/index.js.map +1 -0
- package/dist/judge/inspect.d.ts +148 -0
- package/dist/judge/inspect.js +208 -0
- package/dist/judge/inspect.js.map +1 -0
- package/dist/judge/intent.d.ts +146 -0
- package/dist/judge/intent.js +150 -0
- package/dist/judge/intent.js.map +1 -0
- package/dist/judge/locale.d.ts +111 -0
- package/dist/judge/locale.js +247 -0
- package/dist/judge/locale.js.map +1 -0
- package/dist/judge/scope.d.ts +41 -0
- package/dist/judge/scope.js +47 -0
- package/dist/judge/scope.js.map +1 -0
- package/dist/judge/sensitivity.d.ts +202 -0
- package/dist/judge/sensitivity.js +152 -0
- package/dist/judge/sensitivity.js.map +1 -0
- package/dist/judge/trail.d.ts +122 -0
- package/dist/judge/trail.js +96 -0
- package/dist/judge/trail.js.map +1 -0
- package/dist/judge/verdict.d.ts +39 -0
- package/dist/judge/verdict.js +32 -0
- package/dist/judge/verdict.js.map +1 -0
- package/dist/plan/index.d.ts +12 -0
- package/dist/plan/index.js +11 -0
- package/dist/plan/index.js.map +1 -0
- package/dist/plan/tool.d.ts +85 -0
- package/dist/plan/tool.js +87 -0
- package/dist/plan/tool.js.map +1 -0
- package/dist/relate/graph.d.ts +190 -0
- package/dist/relate/graph.js +234 -0
- package/dist/relate/graph.js.map +1 -0
- package/dist/relate/index.d.ts +16 -0
- package/dist/relate/index.js +16 -0
- package/dist/relate/index.js.map +1 -0
- package/dist/relate/merkle.d.ts +110 -0
- package/dist/relate/merkle.js +246 -0
- package/dist/relate/merkle.js.map +1 -0
- package/dist/relate/reach.d.ts +65 -0
- package/dist/relate/reach.js +111 -0
- package/dist/relate/reach.js.map +1 -0
- package/dist/relate/records.d.ts +129 -0
- package/dist/relate/records.js +112 -0
- package/dist/relate/records.js.map +1 -0
- package/dist/rules/index.d.ts +19 -0
- package/dist/rules/index.js +17 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/normalize/alias.d.ts +50 -0
- package/dist/rules/normalize/alias.js +111 -0
- package/dist/rules/normalize/alias.js.map +1 -0
- package/dist/rules/normalize/cascade.d.ts +85 -0
- package/dist/rules/normalize/cascade.js +324 -0
- package/dist/rules/normalize/cascade.js.map +1 -0
- package/dist/rules/normalize/color.d.ts +30 -0
- package/dist/rules/normalize/color.js +185 -0
- package/dist/rules/normalize/color.js.map +1 -0
- package/dist/rules/normalize/fixture.d.ts +83 -0
- package/dist/rules/normalize/fixture.js +74 -0
- package/dist/rules/normalize/fixture.js.map +1 -0
- package/dist/rules/normalize/index.d.ts +39 -0
- package/dist/rules/normalize/index.js +268 -0
- package/dist/rules/normalize/index.js.map +1 -0
- package/dist/rules/normalize/project.d.ts +42 -0
- package/dist/rules/normalize/project.js +83 -0
- package/dist/rules/normalize/project.js.map +1 -0
- package/dist/rules/normalize/shorthand.d.ts +38 -0
- package/dist/rules/normalize/shorthand.js +388 -0
- package/dist/rules/normalize/shorthand.js.map +1 -0
- package/dist/rules/normalize/value.d.ts +23 -0
- package/dist/rules/normalize/value.js +162 -0
- package/dist/rules/normalize/value.js.map +1 -0
- package/dist/rules/normalize/wrapper.d.ts +16 -0
- package/dist/rules/normalize/wrapper.js +137 -0
- package/dist/rules/normalize/wrapper.js.map +1 -0
- package/dist/rules/ruleset.d.ts +93 -0
- package/dist/rules/ruleset.js +175 -0
- package/dist/rules/ruleset.js.map +1 -0
- package/mark.svg +30 -0
- package/package.json +69 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { canonicalize } from './canonical.js';
|
|
2
|
+
import { sha256Hex, sha256HexBytes } from './sha256.js';
|
|
3
|
+
const PREFIX = 'v1';
|
|
4
|
+
const HEX_LENGTH = 32;
|
|
5
|
+
/**
|
|
6
|
+
* Hash an already-canonical string.
|
|
7
|
+
*
|
|
8
|
+
* Synchronous, and must stay so. `propsDigest` runs inside the rendering page,
|
|
9
|
+
* where a prop may be a function or an element that cannot be serialized out —
|
|
10
|
+
* so the digest is taken where the value still exists. `crypto.subtle` is async
|
|
11
|
+
* and would colour the whole normalizer; see `sha256.ts`.
|
|
12
|
+
*/
|
|
13
|
+
export function digestString(input) {
|
|
14
|
+
return `${PREFIX}:${sha256Hex(input).slice(0, HEX_LENGTH)}`;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Hash raw bytes — an image, a font file, anything whose identity is its octets.
|
|
18
|
+
*
|
|
19
|
+
* Same prefix and same truncation as every other digest here, so an asset hash
|
|
20
|
+
* and a render hash are the same kind of value and neither can be mistaken for
|
|
21
|
+
* the other's domain. What it is for is `EnvironmentInputs.assets`: a URL is not
|
|
22
|
+
* an identity, and the same `url(...)` can resolve to different bytes tomorrow.
|
|
23
|
+
*/
|
|
24
|
+
export function digestBytes(bytes) {
|
|
25
|
+
return `${PREFIX}:${sha256HexBytes(bytes).slice(0, HEX_LENGTH)}`;
|
|
26
|
+
}
|
|
27
|
+
/** Canonicalize then hash. The only correct way to hash a structure. */
|
|
28
|
+
export function digestValue(value) {
|
|
29
|
+
return digestString(canonicalize(value));
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Combine digests under a domain label.
|
|
33
|
+
*
|
|
34
|
+
* The label prevents cross-domain collision: an environment key and a render hash
|
|
35
|
+
* built from identical component digests must not be equal, or an environment
|
|
36
|
+
* change could masquerade as a content match.
|
|
37
|
+
*/
|
|
38
|
+
export function digestCombine(domain, parts) {
|
|
39
|
+
return digestString(`${domain}\u0000${parts.join('\u0000')}`);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/format/hash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAuB,MAAM,gBAAgB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAkBxD,MAAM,MAAM,GAAG,IAAI,CAAC;AACpB,MAAM,UAAU,GAAG,EAAE,CAAC;AAEtB;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,KAAa;IACxC,OAAO,GAAG,MAAM,IAAI,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,CAAC;AAC9D,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,WAAW,CAAC,KAAiB;IAC3C,OAAO,GAAG,MAAM,IAAI,cAAc,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,CAAC;AACnE,CAAC;AAED,wEAAwE;AACxE,MAAM,UAAU,WAAW,CAAC,KAAqB;IAC/C,OAAO,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC,CAAC,CAAC;AAC3C,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,aAAa,CAAC,MAAc,EAAE,KAAwB;IACpE,OAAO,YAAY,CAAC,GAAG,MAAM,SAAS,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC;AAChE,CAAC","sourcesContent":["import { canonicalize, type CanonicalValue } from './canonical.js';\nimport { sha256Hex, sha256HexBytes } from './sha256.js';\n\n/**\n * Content addressing (Principle 4).\n *\n * Hashes are the system's only identity. A baseline is addressed by what it *is*,\n * never by the branch or commit it arrived on, which is why a rebase cannot\n * invalidate one.\n *\n * Digests are prefixed with their algorithm and truncated to 128 bits. The prefix\n * makes a future algorithm change a visible, greppable migration rather than a\n * silent collision domain; 128 bits is far beyond collision risk for a corpus\n * bounded by the number of subjects a repository has, and keeps `variance.lock`\n * readable as a diff — a manifest a human refuses to read is a manifest nobody\n * reviews.\n */\nexport type Digest = string;\n\nconst PREFIX = 'v1';\nconst HEX_LENGTH = 32;\n\n/**\n * Hash an already-canonical string.\n *\n * Synchronous, and must stay so. `propsDigest` runs inside the rendering page,\n * where a prop may be a function or an element that cannot be serialized out —\n * so the digest is taken where the value still exists. `crypto.subtle` is async\n * and would colour the whole normalizer; see `sha256.ts`.\n */\nexport function digestString(input: string): Digest {\n return `${PREFIX}:${sha256Hex(input).slice(0, HEX_LENGTH)}`;\n}\n\n/**\n * Hash raw bytes — an image, a font file, anything whose identity is its octets.\n *\n * Same prefix and same truncation as every other digest here, so an asset hash\n * and a render hash are the same kind of value and neither can be mistaken for\n * the other's domain. What it is for is `EnvironmentInputs.assets`: a URL is not\n * an identity, and the same `url(...)` can resolve to different bytes tomorrow.\n */\nexport function digestBytes(bytes: Uint8Array): Digest {\n return `${PREFIX}:${sha256HexBytes(bytes).slice(0, HEX_LENGTH)}`;\n}\n\n/** Canonicalize then hash. The only correct way to hash a structure. */\nexport function digestValue(value: CanonicalValue): Digest {\n return digestString(canonicalize(value));\n}\n\n/**\n * Combine digests under a domain label.\n *\n * The label prevents cross-domain collision: an environment key and a render hash\n * built from identical component digests must not be equal, or an environment\n * change could masquerade as a content match.\n */\nexport function digestCombine(domain: string, parts: readonly Digest[]): Digest {\n return digestString(`${domain}\\u0000${parts.join('\\u0000')}`);\n}\n"]}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { Digest } from './hash.js';
|
|
2
|
+
/**
|
|
3
|
+
* Holding: what a component was holding, as distinct from how it is attached.
|
|
4
|
+
*
|
|
5
|
+
* {@link Wiring} ends on the sentence that defines this type by exclusion —
|
|
6
|
+
* *wiring carries the shape of the component's attachment and nothing that
|
|
7
|
+
* attachment happened to be holding*. This is the nothing. A `useState` cell's
|
|
8
|
+
* value, a `useSyncExternalStore` snapshot, a context's current value, and the
|
|
9
|
+
* per-key breakdown of the props object a boundary was handed.
|
|
10
|
+
*
|
|
11
|
+
* ## Why the refusal in `Wiring` is not reversed here
|
|
12
|
+
*
|
|
13
|
+
* `Wiring` states, correctly, that state values must never be a band: a hook's
|
|
14
|
+
* value is the thing that *legitimately* differs between two readings of one
|
|
15
|
+
* page — the ticker, the timestamp, the animation frame — so a band carrying one
|
|
16
|
+
* would be a flake generator wearing a band's name. That argument is about
|
|
17
|
+
* **identity**, and it is accepted here without qualification.
|
|
18
|
+
*
|
|
19
|
+
* This type is not identity. It is **evidence**, and it enters no hash: not the
|
|
20
|
+
* render hash, not the structure or style hash, not a component hash, not an
|
|
21
|
+
* environment key. Its precedent is `styleProvenance`, which exists for the same
|
|
22
|
+
* reason and takes the same exemption — normalization discards exactly what
|
|
23
|
+
* attribution needs, so the discarded thing is preserved beside the snapshot
|
|
24
|
+
* rather than inside it. Keeping it outside is the point, twice over: a baseline
|
|
25
|
+
* cannot be invalidated by a value that was always allowed to move, and a
|
|
26
|
+
* differ that could not read *why* a component chose differently is left
|
|
27
|
+
* reporting the choice.
|
|
28
|
+
*
|
|
29
|
+
* The two types therefore answer different questions about one boundary, and the
|
|
30
|
+
* pair is what makes the interesting sentence sayable:
|
|
31
|
+
*
|
|
32
|
+
* | question | answered by |
|
|
33
|
+
* |---|---|
|
|
34
|
+
* | is this the same component? | `Wiring` — hook shape, wrappers, contexts, key |
|
|
35
|
+
* | did it decide the same way? | `Holding` — the values those hooks were holding |
|
|
36
|
+
*
|
|
37
|
+
* ## Digests, never values
|
|
38
|
+
*
|
|
39
|
+
* Every field is a {@link Digest}, on spec 0024's rule and for its reason: a
|
|
40
|
+
* prop value can be a customer record, and a `useState` cell can hold the same
|
|
41
|
+
* record with a session token beside it. The digest is what makes an equality
|
|
42
|
+
* comparison possible without the value leaving the page. Nothing here can be
|
|
43
|
+
* reversed into what a user was looking at, which is the property that lets this
|
|
44
|
+
* travel over the same wire the rest of the document does.
|
|
45
|
+
*
|
|
46
|
+
* The cost is the one `propsDigest` already documents and accepts: shape rather
|
|
47
|
+
* than identity, so a re-created closure does not register and a genuinely
|
|
48
|
+
* rebound anonymous one does not either. That is tolerable for the same reason —
|
|
49
|
+
* this decides *who is responsible* for a difference the semantic diff already
|
|
50
|
+
* found, never *whether* there is one.
|
|
51
|
+
*/
|
|
52
|
+
export interface Holding {
|
|
53
|
+
/**
|
|
54
|
+
* One digest per hook that retains something, in authored call order.
|
|
55
|
+
*
|
|
56
|
+
* Sparse by construction. A hook that retains nothing a later reading could
|
|
57
|
+
* disagree about contributes no cell, so `useContext` (which has no cell at
|
|
58
|
+
* all) and the dispatch half of `useActionState` (a function React rebuilds)
|
|
59
|
+
* are absent rather than present-and-meaningless. {@link HeldCell.index}
|
|
60
|
+
* carries the authored position, so a gap is legible instead of silently
|
|
61
|
+
* renumbering everything after it.
|
|
62
|
+
*/
|
|
63
|
+
readonly cells?: readonly HeldCell[];
|
|
64
|
+
/**
|
|
65
|
+
* Context values this boundary read, by the context's display name, sorted.
|
|
66
|
+
*
|
|
67
|
+
* Separate from `cells` because a context subscription leaves no hook cell —
|
|
68
|
+
* it is recorded on the fiber's dependency list and nowhere else — and because
|
|
69
|
+
* the two fail differently. A changed cell is this component's own decision; a
|
|
70
|
+
* changed context is a decision taken above it by a provider that may not even
|
|
71
|
+
* be in the subject, which is a different sentence and a different fix.
|
|
72
|
+
*/
|
|
73
|
+
readonly contexts?: readonly HeldValue[];
|
|
74
|
+
/**
|
|
75
|
+
* The props object, one digest per key, sorted — spec 0024's map.
|
|
76
|
+
*
|
|
77
|
+
* `OwnerFrame.propsDigest` says *this component was handed something
|
|
78
|
+
* different* and cannot say which thing. This says which thing, under the same
|
|
79
|
+
* projection and with `children` excluded for the same reason: `children` *is*
|
|
80
|
+
* the subtree, and digesting it would make every ancestor's inputs a function
|
|
81
|
+
* of every descendant's edit, which is the condition that makes root
|
|
82
|
+
* attribution work at all.
|
|
83
|
+
*/
|
|
84
|
+
readonly props?: readonly HeldValue[];
|
|
85
|
+
/**
|
|
86
|
+
* The hook whose name this reader does not know, when one was met.
|
|
87
|
+
*
|
|
88
|
+
* Present only on a truncated read, and its presence means `cells` is a
|
|
89
|
+
* *prefix* rather than the whole list. Alignment between React's recorded hook
|
|
90
|
+
* names and its hook cells depends on knowing how many cells each hook builds
|
|
91
|
+
* (see the reader's table), so one unrecognised name makes every cell after it
|
|
92
|
+
* unattributable — not wrong by a little, but attached to the wrong hook.
|
|
93
|
+
*
|
|
94
|
+
* Reported rather than guessed through, on ADR-0002: a prefix that says where
|
|
95
|
+
* it stopped is usable, and a full list silently mislabelled from the fourth
|
|
96
|
+
* entry on is the failure this project exists to refuse. The reader stops, and
|
|
97
|
+
* a React release that adds a hook makes this field appear instead of making
|
|
98
|
+
* an attribution quietly point at the wrong `useState`.
|
|
99
|
+
*/
|
|
100
|
+
readonly unread?: string;
|
|
101
|
+
}
|
|
102
|
+
/** One hook cell's retained value, addressed by the position a person can count to. */
|
|
103
|
+
export interface HeldCell {
|
|
104
|
+
/**
|
|
105
|
+
* Position in the component's own hook call order, zero-based.
|
|
106
|
+
*
|
|
107
|
+
* The number a reader arrives at by counting hook calls down the component,
|
|
108
|
+
* which is the only index anybody can act on — it indexes
|
|
109
|
+
* {@link Wiring.hooks} exactly, and it is *not* an index into React's cell
|
|
110
|
+
* chain, because `useContext` builds no cell and `useTransition` builds two.
|
|
111
|
+
* Converting between the two is the reader's job and the reason it needs a
|
|
112
|
+
* measured table rather than a zip.
|
|
113
|
+
*/
|
|
114
|
+
readonly index: number;
|
|
115
|
+
/** `useState`, `useReducer`, `useSyncExternalStore`, … as React recorded it. */
|
|
116
|
+
readonly hook: string;
|
|
117
|
+
/**
|
|
118
|
+
* Digest of what the cell held, under a projection chosen per hook.
|
|
119
|
+
*
|
|
120
|
+
* Per hook because the cells are not alike and a single rule over them is
|
|
121
|
+
* either useless or fatal. A `useState` cell holds the state; a `useMemo` cell
|
|
122
|
+
* holds `[value, deps]` and the deps are the input the value is derived from;
|
|
123
|
+
* an effect cell holds an object whose `next` pointer closes a ring, and
|
|
124
|
+
* walking one is an out-of-memory kill rather than a bad digest. The reader
|
|
125
|
+
* names the projection it used for each.
|
|
126
|
+
*/
|
|
127
|
+
readonly digest: Digest;
|
|
128
|
+
}
|
|
129
|
+
/** A named value a boundary read: a context, or one prop. */
|
|
130
|
+
export interface HeldValue {
|
|
131
|
+
readonly name: string;
|
|
132
|
+
readonly digest: Digest;
|
|
133
|
+
}
|
|
134
|
+
//# sourceMappingURL=holding.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"holding.js","sourceRoot":"","sources":["../../src/format/holding.ts"],"names":[],"mappings":"","sourcesContent":["import type { Digest } from './hash.js';\n\n/**\n * Holding: what a component was holding, as distinct from how it is attached.\n *\n * {@link Wiring} ends on the sentence that defines this type by exclusion —\n * *wiring carries the shape of the component's attachment and nothing that\n * attachment happened to be holding*. This is the nothing. A `useState` cell's\n * value, a `useSyncExternalStore` snapshot, a context's current value, and the\n * per-key breakdown of the props object a boundary was handed.\n *\n * ## Why the refusal in `Wiring` is not reversed here\n *\n * `Wiring` states, correctly, that state values must never be a band: a hook's\n * value is the thing that *legitimately* differs between two readings of one\n * page — the ticker, the timestamp, the animation frame — so a band carrying one\n * would be a flake generator wearing a band's name. That argument is about\n * **identity**, and it is accepted here without qualification.\n *\n * This type is not identity. It is **evidence**, and it enters no hash: not the\n * render hash, not the structure or style hash, not a component hash, not an\n * environment key. Its precedent is `styleProvenance`, which exists for the same\n * reason and takes the same exemption — normalization discards exactly what\n * attribution needs, so the discarded thing is preserved beside the snapshot\n * rather than inside it. Keeping it outside is the point, twice over: a baseline\n * cannot be invalidated by a value that was always allowed to move, and a\n * differ that could not read *why* a component chose differently is left\n * reporting the choice.\n *\n * The two types therefore answer different questions about one boundary, and the\n * pair is what makes the interesting sentence sayable:\n *\n * | question | answered by |\n * |---|---|\n * | is this the same component? | `Wiring` — hook shape, wrappers, contexts, key |\n * | did it decide the same way? | `Holding` — the values those hooks were holding |\n *\n * ## Digests, never values\n *\n * Every field is a {@link Digest}, on spec 0024's rule and for its reason: a\n * prop value can be a customer record, and a `useState` cell can hold the same\n * record with a session token beside it. The digest is what makes an equality\n * comparison possible without the value leaving the page. Nothing here can be\n * reversed into what a user was looking at, which is the property that lets this\n * travel over the same wire the rest of the document does.\n *\n * The cost is the one `propsDigest` already documents and accepts: shape rather\n * than identity, so a re-created closure does not register and a genuinely\n * rebound anonymous one does not either. That is tolerable for the same reason —\n * this decides *who is responsible* for a difference the semantic diff already\n * found, never *whether* there is one.\n */\nexport interface Holding {\n /**\n * One digest per hook that retains something, in authored call order.\n *\n * Sparse by construction. A hook that retains nothing a later reading could\n * disagree about contributes no cell, so `useContext` (which has no cell at\n * all) and the dispatch half of `useActionState` (a function React rebuilds)\n * are absent rather than present-and-meaningless. {@link HeldCell.index}\n * carries the authored position, so a gap is legible instead of silently\n * renumbering everything after it.\n */\n readonly cells?: readonly HeldCell[];\n\n /**\n * Context values this boundary read, by the context's display name, sorted.\n *\n * Separate from `cells` because a context subscription leaves no hook cell —\n * it is recorded on the fiber's dependency list and nowhere else — and because\n * the two fail differently. A changed cell is this component's own decision; a\n * changed context is a decision taken above it by a provider that may not even\n * be in the subject, which is a different sentence and a different fix.\n */\n readonly contexts?: readonly HeldValue[];\n\n /**\n * The props object, one digest per key, sorted — spec 0024's map.\n *\n * `OwnerFrame.propsDigest` says *this component was handed something\n * different* and cannot say which thing. This says which thing, under the same\n * projection and with `children` excluded for the same reason: `children` *is*\n * the subtree, and digesting it would make every ancestor's inputs a function\n * of every descendant's edit, which is the condition that makes root\n * attribution work at all.\n */\n readonly props?: readonly HeldValue[];\n\n /**\n * The hook whose name this reader does not know, when one was met.\n *\n * Present only on a truncated read, and its presence means `cells` is a\n * *prefix* rather than the whole list. Alignment between React's recorded hook\n * names and its hook cells depends on knowing how many cells each hook builds\n * (see the reader's table), so one unrecognised name makes every cell after it\n * unattributable — not wrong by a little, but attached to the wrong hook.\n *\n * Reported rather than guessed through, on ADR-0002: a prefix that says where\n * it stopped is usable, and a full list silently mislabelled from the fourth\n * entry on is the failure this project exists to refuse. The reader stops, and\n * a React release that adds a hook makes this field appear instead of making\n * an attribution quietly point at the wrong `useState`.\n */\n readonly unread?: string;\n}\n\n/** One hook cell's retained value, addressed by the position a person can count to. */\nexport interface HeldCell {\n /**\n * Position in the component's own hook call order, zero-based.\n *\n * The number a reader arrives at by counting hook calls down the component,\n * which is the only index anybody can act on — it indexes\n * {@link Wiring.hooks} exactly, and it is *not* an index into React's cell\n * chain, because `useContext` builds no cell and `useTransition` builds two.\n * Converting between the two is the reader's job and the reason it needs a\n * measured table rather than a zip.\n */\n readonly index: number;\n\n /** `useState`, `useReducer`, `useSyncExternalStore`, … as React recorded it. */\n readonly hook: string;\n\n /**\n * Digest of what the cell held, under a projection chosen per hook.\n *\n * Per hook because the cells are not alike and a single rule over them is\n * either useless or fatal. A `useState` cell holds the state; a `useMemo` cell\n * holds `[value, deps]` and the deps are the input the value is derived from;\n * an effect cell holds an object whose `next` pointer closes a ring, and\n * walking one is an out-of-memory kill rather than a bad digest. The reader\n * names the projection it used for each.\n */\n readonly digest: Digest;\n}\n\n/** A named value a boundary read: a context, or one prop. */\nexport interface HeldValue {\n readonly name: string;\n readonly digest: Digest;\n}\n"]}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@variance-authority/core/format` — what a subject *is*, as a value.
|
|
3
|
+
*
|
|
4
|
+
* The shapes every other group reads and none of them may redefine: a raw
|
|
5
|
+
* capture as a collector hands it over, the normalized snapshot it becomes, the
|
|
6
|
+
* document a renderer paints, the identity that addresses the result, and the
|
|
7
|
+
* hashing that makes any of it comparable.
|
|
8
|
+
*
|
|
9
|
+
* A consumer writing their own collector needs exactly this and nothing else.
|
|
10
|
+
* That is the entrypoint's reason to exist: the format is a contract other people
|
|
11
|
+
* implement, and implementing it should not require reading the rules that
|
|
12
|
+
* interpret it or the verdicts drawn from it.
|
|
13
|
+
*/
|
|
14
|
+
export type { CanonicalValue } from './canonical.js';
|
|
15
|
+
export { canonicalize, canonicalNumber } from './canonical.js';
|
|
16
|
+
export type { CapturedValue, ShapeValueOptions, ValueShaping } from './value.js';
|
|
17
|
+
export { shapeValue, pointerShape, pointerToken, DROPPED, VALUE_RECIPE } from './value.js';
|
|
18
|
+
export type { Digest } from './hash.js';
|
|
19
|
+
export { digestString, digestBytes, digestValue, digestCombine } from './hash.js';
|
|
20
|
+
export type { AccessibilitySnapshot } from './accessibility.js';
|
|
21
|
+
export { accessibilitySnapshot } from './accessibility.js';
|
|
22
|
+
export type { ObservationProfile, ProfileId } from './profile.js';
|
|
23
|
+
export { JSDOM_PROFILE, CHROMIUM_PROFILE, profileById, tierOfProfile } from './profile.js';
|
|
24
|
+
export type { Tier } from './tier.js';
|
|
25
|
+
export { tierReaches } from './tier.js';
|
|
26
|
+
export type { EnvironmentInputs, EnvironmentKey, EnvironmentDelta, EnvironmentField, Viewport, } from './environment.js';
|
|
27
|
+
export { environmentKey, diffEnvironments } from './environment.js';
|
|
28
|
+
export { INTERVENTIONS, SEMANTIC_RECIPE, COLLECT_RECIPE, LAYOUT_RECIPE, RASTER_RECIPE, holdAnimations, pinAnimations, hideCaret, hideScrollbars, hidePresentationalImages, waitForFonts, waitForImages, forTier, conflicts, interventionById, recipeOf, recipeCss, recipeScreenshot, recipeDigest, settleRecipe, describeRecipe, } from './stabilize.js';
|
|
29
|
+
export type { Intervention, Recipe, Trick, ScreenshotOptions, SettleTarget, } from './stabilize.js';
|
|
30
|
+
export type { Provenance, OwnerFrame, SourceLocation, StackFrame } from './provenance.js';
|
|
31
|
+
export { propsDigest, heldDigest, jsxSourceOf, relativizeSource, JSX_SOURCE } from './provenance.js';
|
|
32
|
+
export type { Holding, HeldCell, HeldValue } from './holding.js';
|
|
33
|
+
export type { Wiring } from './wiring.js';
|
|
34
|
+
export { keyedByPosition } from './wiring.js';
|
|
35
|
+
export type { RawCapture, RawNode, RawAria, MatchedRule, Declaration, Rect, SubjectRef, Diagnostic, } from './capture.js';
|
|
36
|
+
export type { SemanticSnapshot, SemanticNode, NodePath, StyleProvenanceEntry, IgnoreSite, ComponentHash, } from './snapshot.js';
|
|
37
|
+
export { documentDigest, identityDigest } from './document.js';
|
|
38
|
+
export type { RenderDocument, RenderFrame, FrameElement, RenderResource, RenderIdentity, Raster, } from './document.js';
|
|
39
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@variance-authority/core/format` — what a subject *is*, as a value.
|
|
3
|
+
*
|
|
4
|
+
* The shapes every other group reads and none of them may redefine: a raw
|
|
5
|
+
* capture as a collector hands it over, the normalized snapshot it becomes, the
|
|
6
|
+
* document a renderer paints, the identity that addresses the result, and the
|
|
7
|
+
* hashing that makes any of it comparable.
|
|
8
|
+
*
|
|
9
|
+
* A consumer writing their own collector needs exactly this and nothing else.
|
|
10
|
+
* That is the entrypoint's reason to exist: the format is a contract other people
|
|
11
|
+
* implement, and implementing it should not require reading the rules that
|
|
12
|
+
* interpret it or the verdicts drawn from it.
|
|
13
|
+
*/
|
|
14
|
+
export { canonicalize, canonicalNumber } from './canonical.js';
|
|
15
|
+
export { shapeValue, pointerShape, pointerToken, DROPPED, VALUE_RECIPE } from './value.js';
|
|
16
|
+
export { digestString, digestBytes, digestValue, digestCombine } from './hash.js';
|
|
17
|
+
export { accessibilitySnapshot } from './accessibility.js';
|
|
18
|
+
export { JSDOM_PROFILE, CHROMIUM_PROFILE, profileById, tierOfProfile } from './profile.js';
|
|
19
|
+
export { tierReaches } from './tier.js';
|
|
20
|
+
export { environmentKey, diffEnvironments } from './environment.js';
|
|
21
|
+
export { INTERVENTIONS, SEMANTIC_RECIPE, COLLECT_RECIPE, LAYOUT_RECIPE, RASTER_RECIPE, holdAnimations, pinAnimations, hideCaret, hideScrollbars, hidePresentationalImages, waitForFonts, waitForImages, forTier, conflicts, interventionById, recipeOf, recipeCss, recipeScreenshot, recipeDigest, settleRecipe, describeRecipe, } from './stabilize.js';
|
|
22
|
+
export { propsDigest, heldDigest, jsxSourceOf, relativizeSource, JSX_SOURCE } from './provenance.js';
|
|
23
|
+
export { keyedByPosition } from './wiring.js';
|
|
24
|
+
export { documentDigest, identityDigest } from './document.js';
|
|
25
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/format/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAGH,OAAO,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAG/D,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,YAAY,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG3F,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAGlF,OAAO,EAAE,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAG3D,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAG3F,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AASxC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEpE,OAAO,EACL,aAAa,EACb,eAAe,EACf,cAAc,EACd,aAAa,EACb,aAAa,EACb,cAAc,EACd,aAAa,EACb,SAAS,EACT,cAAc,EACd,wBAAwB,EACxB,YAAY,EACZ,aAAa,EACb,OAAO,EACP,SAAS,EACT,gBAAgB,EAChB,QAAQ,EACR,SAAS,EACT,gBAAgB,EAChB,YAAY,EACZ,YAAY,EACZ,cAAc,GACf,MAAM,gBAAgB,CAAC;AAUxB,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAGrG,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAsB9C,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC","sourcesContent":["/**\n * `@variance-authority/core/format` — what a subject *is*, as a value.\n *\n * The shapes every other group reads and none of them may redefine: a raw\n * capture as a collector hands it over, the normalized snapshot it becomes, the\n * document a renderer paints, the identity that addresses the result, and the\n * hashing that makes any of it comparable.\n *\n * A consumer writing their own collector needs exactly this and nothing else.\n * That is the entrypoint's reason to exist: the format is a contract other people\n * implement, and implementing it should not require reading the rules that\n * interpret it or the verdicts drawn from it.\n */\n\nexport type { CanonicalValue } from './canonical.js';\nexport { canonicalize, canonicalNumber } from './canonical.js';\n\nexport type { CapturedValue, ShapeValueOptions, ValueShaping } from './value.js';\nexport { shapeValue, pointerShape, pointerToken, DROPPED, VALUE_RECIPE } from './value.js';\n\nexport type { Digest } from './hash.js';\nexport { digestString, digestBytes, digestValue, digestCombine } from './hash.js';\n\nexport type { AccessibilitySnapshot } from './accessibility.js';\nexport { accessibilitySnapshot } from './accessibility.js';\n\nexport type { ObservationProfile, ProfileId } from './profile.js';\nexport { JSDOM_PROFILE, CHROMIUM_PROFILE, profileById, tierOfProfile } from './profile.js';\n\nexport type { Tier } from './tier.js';\nexport { tierReaches } from './tier.js';\n\nexport type {\n EnvironmentInputs,\n EnvironmentKey,\n EnvironmentDelta,\n EnvironmentField,\n Viewport,\n} from './environment.js';\nexport { environmentKey, diffEnvironments } from './environment.js';\n\nexport {\n INTERVENTIONS,\n SEMANTIC_RECIPE,\n COLLECT_RECIPE,\n LAYOUT_RECIPE,\n RASTER_RECIPE,\n holdAnimations,\n pinAnimations,\n hideCaret,\n hideScrollbars,\n hidePresentationalImages,\n waitForFonts,\n waitForImages,\n forTier,\n conflicts,\n interventionById,\n recipeOf,\n recipeCss,\n recipeScreenshot,\n recipeDigest,\n settleRecipe,\n describeRecipe,\n} from './stabilize.js';\nexport type {\n Intervention,\n Recipe,\n Trick,\n ScreenshotOptions,\n SettleTarget,\n} from './stabilize.js';\n\nexport type { Provenance, OwnerFrame, SourceLocation, StackFrame } from './provenance.js';\nexport { propsDigest, heldDigest, jsxSourceOf, relativizeSource, JSX_SOURCE } from './provenance.js';\nexport type { Holding, HeldCell, HeldValue } from './holding.js';\nexport type { Wiring } from './wiring.js';\nexport { keyedByPosition } from './wiring.js';\n\nexport type {\n RawCapture,\n RawNode,\n RawAria,\n MatchedRule,\n Declaration,\n Rect,\n SubjectRef,\n Diagnostic,\n} from './capture.js';\n\nexport type {\n SemanticSnapshot,\n SemanticNode,\n NodePath,\n StyleProvenanceEntry,\n IgnoreSite,\n ComponentHash,\n} from './snapshot.js';\n\nexport { documentDigest, identityDigest } from './document.js';\nexport type {\n RenderDocument,\n RenderFrame,\n FrameElement,\n RenderResource,\n RenderIdentity,\n Raster,\n} from './document.js';\n"]}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an intervention is, as a shape.
|
|
3
|
+
*
|
|
4
|
+
* Beside [`stabilize.ts`](./stabilize.ts), which argues why the set is open and
|
|
5
|
+
* then fills it. This file is the declaration that argument depends on: an id, a
|
|
6
|
+
* tier, a property it claims, and the three ways it can reach a page. A trick
|
|
7
|
+
* from another project is one of these and nothing more, so the shape is worth
|
|
8
|
+
* reading without reading the tricks.
|
|
9
|
+
*/
|
|
10
|
+
import type { Tier } from './tier.js';
|
|
11
|
+
/** What kind of move it is, for reading a recipe at a glance. */
|
|
12
|
+
export type Trick =
|
|
13
|
+
/** Puts the page into a known state. */
|
|
14
|
+
'reset'
|
|
15
|
+
/** Stops something that would otherwise keep moving. */
|
|
16
|
+
| 'hold'
|
|
17
|
+
/** Blocks until something outside the page's control has landed. */
|
|
18
|
+
| 'wait'
|
|
19
|
+
/** Removes something that is present but must not be measured. */
|
|
20
|
+
| 'support';
|
|
21
|
+
/** Screenshot options a trick contributes. Narrow on purpose. */
|
|
22
|
+
export interface ScreenshotOptions {
|
|
23
|
+
readonly animations?: 'disabled';
|
|
24
|
+
readonly caret?: 'hide';
|
|
25
|
+
}
|
|
26
|
+
/** The page surface a `settle` step may use. Structural, so no driver leaks in. */
|
|
27
|
+
export interface SettleTarget {
|
|
28
|
+
evaluate<T>(fn: () => T | Promise<T>): Promise<T>;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The page's globals, as the little of them a settle step needs.
|
|
32
|
+
*
|
|
33
|
+
* A settle closure is the one place in `core` that reads a live document, and it
|
|
34
|
+
* gets away with it because it never runs here: it is handed to
|
|
35
|
+
* {@link SettleTarget.evaluate}, which ships it into a page. ADR-0001's
|
|
36
|
+
* enforcement is `core`'s `tsconfig`, which has no `lib.dom` — so writing
|
|
37
|
+
* `window.document.fonts` is a compile error and this shape is what remains.
|
|
38
|
+
*
|
|
39
|
+
* That is a stricter arrangement than the convention it replaced, not a
|
|
40
|
+
* loophole. Everything a settle step may touch is enumerated here, in one
|
|
41
|
+
* declaration a reader can check, and a trick that wants more of the DOM has to
|
|
42
|
+
* widen it in public rather than reach for an ambient global.
|
|
43
|
+
*/
|
|
44
|
+
export interface PageGlobals {
|
|
45
|
+
readonly document: {
|
|
46
|
+
readonly fonts?: {
|
|
47
|
+
readonly ready: Promise<unknown>;
|
|
48
|
+
};
|
|
49
|
+
readonly images: ArrayLike<{
|
|
50
|
+
readonly complete: boolean;
|
|
51
|
+
readonly currentSrc: string;
|
|
52
|
+
readonly src: string;
|
|
53
|
+
loading: string;
|
|
54
|
+
addEventListener(type: string, listener: () => void, options?: {
|
|
55
|
+
once?: boolean;
|
|
56
|
+
}): void;
|
|
57
|
+
}>;
|
|
58
|
+
};
|
|
59
|
+
setTimeout(handler: () => void, ms: number): unknown;
|
|
60
|
+
clearTimeout(handle: unknown): void;
|
|
61
|
+
}
|
|
62
|
+
export interface Intervention {
|
|
63
|
+
/**
|
|
64
|
+
* Stable identifier, and part of the recipe's digest.
|
|
65
|
+
*
|
|
66
|
+
* Renaming one is therefore a new identity, which is correct: a reader
|
|
67
|
+
* comparing two runs has no way to know that `hold-animations` and
|
|
68
|
+
* `pause-animations` were the same trick.
|
|
69
|
+
*/
|
|
70
|
+
readonly id: string;
|
|
71
|
+
readonly trick: Trick;
|
|
72
|
+
readonly needs: Tier;
|
|
73
|
+
/** What it does and what it costs, in one sentence a report can print. */
|
|
74
|
+
readonly because: string;
|
|
75
|
+
/**
|
|
76
|
+
* The property this trick governs.
|
|
77
|
+
*
|
|
78
|
+
* Two tricks claiming one subject are in conflict — pinning animations in CSS
|
|
79
|
+
* while asking the compositor to fast-forward them applies both, and the CSS
|
|
80
|
+
* wins silently. See {@link conflicts}.
|
|
81
|
+
*/
|
|
82
|
+
readonly governs: string;
|
|
83
|
+
readonly css?: string;
|
|
84
|
+
readonly screenshot?: ScreenshotOptions;
|
|
85
|
+
readonly settle?: (target: SettleTarget) => Promise<void>;
|
|
86
|
+
}
|
|
87
|
+
//# sourceMappingURL=intervention.d.ts.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What an intervention is, as a shape.
|
|
3
|
+
*
|
|
4
|
+
* Beside [`stabilize.ts`](./stabilize.ts), which argues why the set is open and
|
|
5
|
+
* then fills it. This file is the declaration that argument depends on: an id, a
|
|
6
|
+
* tier, a property it claims, and the three ways it can reach a page. A trick
|
|
7
|
+
* from another project is one of these and nothing more, so the shape is worth
|
|
8
|
+
* reading without reading the tricks.
|
|
9
|
+
*/
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=intervention.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"intervention.js","sourceRoot":"","sources":["../../src/format/intervention.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG","sourcesContent":["/**\n * What an intervention is, as a shape.\n *\n * Beside [`stabilize.ts`](./stabilize.ts), which argues why the set is open and\n * then fills it. This file is the declaration that argument depends on: an id, a\n * tier, a property it claims, and the three ways it can reach a page. A trick\n * from another project is one of these and nothing more, so the shape is worth\n * reading without reading the tricks.\n */\n\nimport type { Tier } from './tier.js';\n\n/** What kind of move it is, for reading a recipe at a glance. */\nexport type Trick =\n /** Puts the page into a known state. */\n | 'reset'\n /** Stops something that would otherwise keep moving. */\n | 'hold'\n /** Blocks until something outside the page's control has landed. */\n | 'wait'\n /** Removes something that is present but must not be measured. */\n | 'support';\n\n/** Screenshot options a trick contributes. Narrow on purpose. */\nexport interface ScreenshotOptions {\n readonly animations?: 'disabled';\n readonly caret?: 'hide';\n}\n\n/** The page surface a `settle` step may use. Structural, so no driver leaks in. */\nexport interface SettleTarget {\n evaluate<T>(fn: () => T | Promise<T>): Promise<T>;\n}\n\n/**\n * The page's globals, as the little of them a settle step needs.\n *\n * A settle closure is the one place in `core` that reads a live document, and it\n * gets away with it because it never runs here: it is handed to\n * {@link SettleTarget.evaluate}, which ships it into a page. ADR-0001's\n * enforcement is `core`'s `tsconfig`, which has no `lib.dom` — so writing\n * `window.document.fonts` is a compile error and this shape is what remains.\n *\n * That is a stricter arrangement than the convention it replaced, not a\n * loophole. Everything a settle step may touch is enumerated here, in one\n * declaration a reader can check, and a trick that wants more of the DOM has to\n * widen it in public rather than reach for an ambient global.\n */\nexport interface PageGlobals {\n readonly document: {\n readonly fonts?: { readonly ready: Promise<unknown> };\n readonly images: ArrayLike<{\n readonly complete: boolean;\n readonly currentSrc: string;\n readonly src: string;\n loading: string;\n addEventListener(type: string, listener: () => void, options?: { once?: boolean }): void;\n }>;\n };\n setTimeout(handler: () => void, ms: number): unknown;\n clearTimeout(handle: unknown): void;\n}\n\nexport interface Intervention {\n /**\n * Stable identifier, and part of the recipe's digest.\n *\n * Renaming one is therefore a new identity, which is correct: a reader\n * comparing two runs has no way to know that `hold-animations` and\n * `pause-animations` were the same trick.\n */\n readonly id: string;\n readonly trick: Trick;\n readonly needs: Tier;\n /** What it does and what it costs, in one sentence a report can print. */\n readonly because: string;\n\n /**\n * The property this trick governs.\n *\n * Two tricks claiming one subject are in conflict — pinning animations in CSS\n * while asking the compositor to fast-forward them applies both, and the CSS\n * wins silently. See {@link conflicts}.\n */\n readonly governs: string;\n\n readonly css?: string;\n readonly screenshot?: ScreenshotOptions;\n readonly settle?: (target: SettleTarget) => Promise<void>;\n}\n"]}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observation profiles.
|
|
3
|
+
*
|
|
4
|
+
* A profile declares what a collector was *capable* of observing, independent of
|
|
5
|
+
* what it found. This distinction is load-bearing: see ADR-0002. Without it, a
|
|
6
|
+
* JSDOM snapshot (no layout engine) compared against a Chromium baseline reports
|
|
7
|
+
* either every rect as "moved to 0x0" or — far worse — reports a real geometry
|
|
8
|
+
* regression as `unchanged`, because both sides omitted geometry.
|
|
9
|
+
*
|
|
10
|
+
* A false `unchanged` is the one failure mode this product must never produce.
|
|
11
|
+
*/
|
|
12
|
+
import type { Tier } from './tier.js';
|
|
13
|
+
/** Dimensions a collector may or may not be able to observe. */
|
|
14
|
+
export interface ObservationProfile {
|
|
15
|
+
readonly id: ProfileId;
|
|
16
|
+
/** Roles, accessible names, and ARIA states are resolvable. */
|
|
17
|
+
readonly ariaTree: boolean;
|
|
18
|
+
/** Author-declared style is readable (stylesheet rules, inline style). */
|
|
19
|
+
readonly declaredStyle: boolean;
|
|
20
|
+
/**
|
|
21
|
+
* The cascade is fully resolved by a real engine, including relative units,
|
|
22
|
+
* percentages, and inherited values that depend on layout.
|
|
23
|
+
*/
|
|
24
|
+
readonly computedStyle: boolean;
|
|
25
|
+
/** Real box geometry: positions and sizes from a layout engine. */
|
|
26
|
+
readonly layout: boolean;
|
|
27
|
+
/** Pixels can be produced. */
|
|
28
|
+
readonly raster: boolean;
|
|
29
|
+
}
|
|
30
|
+
export type ProfileId = 'jsdom' | 'chromium';
|
|
31
|
+
/**
|
|
32
|
+
* jest / vitest. Structure, ARIA, and declared style only.
|
|
33
|
+
*
|
|
34
|
+
* JSDOM has no layout engine: `getBoundingClientRect()` returns zeros and
|
|
35
|
+
* `getComputedStyle()` resolves only what was declared. It is not a cheap
|
|
36
|
+
* approximation of a browser — it is an earlier, different gate that decides
|
|
37
|
+
* structural and token-band questions in milliseconds, in the unit-test process.
|
|
38
|
+
*/
|
|
39
|
+
export declare const JSDOM_PROFILE: ObservationProfile;
|
|
40
|
+
/** playwright / agent-browser. Everything. */
|
|
41
|
+
export declare const CHROMIUM_PROFILE: ObservationProfile;
|
|
42
|
+
export declare function profileById(id: ProfileId): ObservationProfile;
|
|
43
|
+
/**
|
|
44
|
+
* The rung a collector with this profile actually stands on.
|
|
45
|
+
*
|
|
46
|
+
* Derived rather than declared, because a profile already says what it can
|
|
47
|
+
* observe and a second field naming a tier is one more thing that can disagree
|
|
48
|
+
* with the first. Used to charge a collection only for the stabilization tricks
|
|
49
|
+
* it could see the effect of: jsdom has no layout engine and no animation clock,
|
|
50
|
+
* so pinning animations and waiting for fonts there is a per-subject cost that
|
|
51
|
+
* buys a rung that exists to be cheap exactly nothing.
|
|
52
|
+
*
|
|
53
|
+
* `raster` is never returned. Whether pixels *can* be produced is a property of
|
|
54
|
+
* the machine, not of the reading being taken — and a collection is a reading,
|
|
55
|
+
* so charging it for a caret it will never photograph would be the same mistake
|
|
56
|
+
* in the other direction.
|
|
57
|
+
*/
|
|
58
|
+
export declare function tierOfProfile(profile: ObservationProfile): Tier;
|
|
59
|
+
//# sourceMappingURL=profile.d.ts.map
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Observation profiles.
|
|
3
|
+
*
|
|
4
|
+
* A profile declares what a collector was *capable* of observing, independent of
|
|
5
|
+
* what it found. This distinction is load-bearing: see ADR-0002. Without it, a
|
|
6
|
+
* JSDOM snapshot (no layout engine) compared against a Chromium baseline reports
|
|
7
|
+
* either every rect as "moved to 0x0" or — far worse — reports a real geometry
|
|
8
|
+
* regression as `unchanged`, because both sides omitted geometry.
|
|
9
|
+
*
|
|
10
|
+
* A false `unchanged` is the one failure mode this product must never produce.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* jest / vitest. Structure, ARIA, and declared style only.
|
|
14
|
+
*
|
|
15
|
+
* JSDOM has no layout engine: `getBoundingClientRect()` returns zeros and
|
|
16
|
+
* `getComputedStyle()` resolves only what was declared. It is not a cheap
|
|
17
|
+
* approximation of a browser — it is an earlier, different gate that decides
|
|
18
|
+
* structural and token-band questions in milliseconds, in the unit-test process.
|
|
19
|
+
*/
|
|
20
|
+
export const JSDOM_PROFILE = {
|
|
21
|
+
id: 'jsdom',
|
|
22
|
+
ariaTree: true,
|
|
23
|
+
declaredStyle: true,
|
|
24
|
+
computedStyle: false,
|
|
25
|
+
layout: false,
|
|
26
|
+
raster: false,
|
|
27
|
+
};
|
|
28
|
+
/** playwright / agent-browser. Everything. */
|
|
29
|
+
export const CHROMIUM_PROFILE = {
|
|
30
|
+
id: 'chromium',
|
|
31
|
+
ariaTree: true,
|
|
32
|
+
declaredStyle: true,
|
|
33
|
+
computedStyle: true,
|
|
34
|
+
layout: true,
|
|
35
|
+
raster: true,
|
|
36
|
+
};
|
|
37
|
+
const PROFILES = {
|
|
38
|
+
jsdom: JSDOM_PROFILE,
|
|
39
|
+
chromium: CHROMIUM_PROFILE,
|
|
40
|
+
};
|
|
41
|
+
export function profileById(id) {
|
|
42
|
+
return PROFILES[id];
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* The rung a collector with this profile actually stands on.
|
|
46
|
+
*
|
|
47
|
+
* Derived rather than declared, because a profile already says what it can
|
|
48
|
+
* observe and a second field naming a tier is one more thing that can disagree
|
|
49
|
+
* with the first. Used to charge a collection only for the stabilization tricks
|
|
50
|
+
* it could see the effect of: jsdom has no layout engine and no animation clock,
|
|
51
|
+
* so pinning animations and waiting for fonts there is a per-subject cost that
|
|
52
|
+
* buys a rung that exists to be cheap exactly nothing.
|
|
53
|
+
*
|
|
54
|
+
* `raster` is never returned. Whether pixels *can* be produced is a property of
|
|
55
|
+
* the machine, not of the reading being taken — and a collection is a reading,
|
|
56
|
+
* so charging it for a caret it will never photograph would be the same mistake
|
|
57
|
+
* in the other direction.
|
|
58
|
+
*/
|
|
59
|
+
export function tierOfProfile(profile) {
|
|
60
|
+
if (profile.layout || profile.computedStyle)
|
|
61
|
+
return 'layout';
|
|
62
|
+
if (profile.declaredStyle || profile.ariaTree)
|
|
63
|
+
return 'semantic';
|
|
64
|
+
return 'reachability';
|
|
65
|
+
}
|
|
66
|
+
//# sourceMappingURL=profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"profile.js","sourceRoot":"","sources":["../../src/format/profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AA8BH;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,aAAa,GAAuB;IAC/C,EAAE,EAAE,OAAO;IACX,QAAQ,EAAE,IAAI;IACd,aAAa,EAAE,IAAI;IACnB,aAAa,EAAE,KAAK;IACpB,MAAM,EAAE,KAAK;IACb,MAAM,EAAE,KAAK;CACd,CAAC;AAEF,8CAA8C;AAC9C,MAAM,CAAC,MAAM,gBAAgB,GAAuB;IAClD,EAAE,EAAE,UAAU;IACd,QAAQ,EAAE,IAAI;IACd,aAAa,EAAE,IAAI;IACnB,aAAa,EAAE,IAAI;IACnB,MAAM,EAAE,IAAI;IACZ,MAAM,EAAE,IAAI;CACb,CAAC;AAEF,MAAM,QAAQ,GAA0C;IACtD,KAAK,EAAE,aAAa;IACpB,QAAQ,EAAE,gBAAgB;CAC3B,CAAC;AAEF,MAAM,UAAU,WAAW,CAAC,EAAa;IACvC,OAAO,QAAQ,CAAC,EAAE,CAAC,CAAC;AACtB,CAAC;AAED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,aAAa,CAAC,OAA2B;IACvD,IAAI,OAAO,CAAC,MAAM,IAAI,OAAO,CAAC,aAAa;QAAE,OAAO,QAAQ,CAAC;IAC7D,IAAI,OAAO,CAAC,aAAa,IAAI,OAAO,CAAC,QAAQ;QAAE,OAAO,UAAU,CAAC;IACjE,OAAO,cAAc,CAAC;AACxB,CAAC","sourcesContent":["/**\n * Observation profiles.\n *\n * A profile declares what a collector was *capable* of observing, independent of\n * what it found. This distinction is load-bearing: see ADR-0002. Without it, a\n * JSDOM snapshot (no layout engine) compared against a Chromium baseline reports\n * either every rect as \"moved to 0x0\" or — far worse — reports a real geometry\n * regression as `unchanged`, because both sides omitted geometry.\n *\n * A false `unchanged` is the one failure mode this product must never produce.\n */\n\n\nimport type { Tier } from './tier.js';\n\n/** Dimensions a collector may or may not be able to observe. */\nexport interface ObservationProfile {\n readonly id: ProfileId;\n\n /** Roles, accessible names, and ARIA states are resolvable. */\n readonly ariaTree: boolean;\n\n /** Author-declared style is readable (stylesheet rules, inline style). */\n readonly declaredStyle: boolean;\n\n /**\n * The cascade is fully resolved by a real engine, including relative units,\n * percentages, and inherited values that depend on layout.\n */\n readonly computedStyle: boolean;\n\n /** Real box geometry: positions and sizes from a layout engine. */\n readonly layout: boolean;\n\n /** Pixels can be produced. */\n readonly raster: boolean;\n}\n\nexport type ProfileId = 'jsdom' | 'chromium';\n\n/**\n * jest / vitest. Structure, ARIA, and declared style only.\n *\n * JSDOM has no layout engine: `getBoundingClientRect()` returns zeros and\n * `getComputedStyle()` resolves only what was declared. It is not a cheap\n * approximation of a browser — it is an earlier, different gate that decides\n * structural and token-band questions in milliseconds, in the unit-test process.\n */\nexport const JSDOM_PROFILE: ObservationProfile = {\n id: 'jsdom',\n ariaTree: true,\n declaredStyle: true,\n computedStyle: false,\n layout: false,\n raster: false,\n};\n\n/** playwright / agent-browser. Everything. */\nexport const CHROMIUM_PROFILE: ObservationProfile = {\n id: 'chromium',\n ariaTree: true,\n declaredStyle: true,\n computedStyle: true,\n layout: true,\n raster: true,\n};\n\nconst PROFILES: Record<ProfileId, ObservationProfile> = {\n jsdom: JSDOM_PROFILE,\n chromium: CHROMIUM_PROFILE,\n};\n\nexport function profileById(id: ProfileId): ObservationProfile {\n return PROFILES[id];\n}\n\n/**\n * The rung a collector with this profile actually stands on.\n *\n * Derived rather than declared, because a profile already says what it can\n * observe and a second field naming a tier is one more thing that can disagree\n * with the first. Used to charge a collection only for the stabilization tricks\n * it could see the effect of: jsdom has no layout engine and no animation clock,\n * so pinning animations and waiting for fonts there is a per-subject cost that\n * buys a rung that exists to be cheap exactly nothing.\n *\n * `raster` is never returned. Whether pixels *can* be produced is a property of\n * the machine, not of the reading being taken — and a collection is a reading,\n * so charging it for a caret it will never photograph would be the same mistake\n * in the other direction.\n */\nexport function tierOfProfile(profile: ObservationProfile): Tier {\n if (profile.layout || profile.computedStyle) return 'layout';\n if (profile.declaredStyle || profile.ariaTree) return 'semantic';\n return 'reachability';\n}\n"]}
|