@variance-authority/core 0.14.0 → 0.17.1

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.
Files changed (147) hide show
  1. package/CHANGELOG.md +878 -0
  2. package/README.md +38 -17
  3. package/dist/attribute/component-hash.d.ts +14 -5
  4. package/dist/attribute/component-hash.js +14 -5
  5. package/dist/attribute/component-hash.js.map +1 -1
  6. package/dist/attribute/composition.d.ts +16 -0
  7. package/dist/attribute/composition.js +4 -6
  8. package/dist/attribute/composition.js.map +1 -1
  9. package/dist/attribute/index.d.ts +3 -1
  10. package/dist/attribute/index.js +2 -1
  11. package/dist/attribute/index.js.map +1 -1
  12. package/dist/attribute/movement.d.ts +1 -1
  13. package/dist/attribute/movement.js.map +1 -1
  14. package/dist/attribute/region.d.ts +47 -4
  15. package/dist/attribute/region.js +91 -22
  16. package/dist/attribute/region.js.map +1 -1
  17. package/dist/attribute/source.d.ts +10 -24
  18. package/dist/attribute/source.js +4 -38
  19. package/dist/attribute/source.js.map +1 -1
  20. package/dist/attribute/subject-pieces.d.ts +37 -0
  21. package/dist/attribute/subject-pieces.js +122 -0
  22. package/dist/attribute/subject-pieces.js.map +1 -0
  23. package/dist/compare/band.d.ts +8 -2
  24. package/dist/compare/band.js +7 -2
  25. package/dist/compare/band.js.map +1 -1
  26. package/dist/compare/diff/attribution.js +6 -4
  27. package/dist/compare/diff/attribution.js.map +1 -1
  28. package/dist/compare/diff/compare-nodes.js +9 -1
  29. package/dist/compare/diff/compare-nodes.js.map +1 -1
  30. package/dist/compare/diff/match.d.ts +2 -1
  31. package/dist/compare/diff/match.js +52 -0
  32. package/dist/compare/diff/match.js.map +1 -1
  33. package/dist/compare/parting.js +6 -4
  34. package/dist/compare/parting.js.map +1 -1
  35. package/dist/format/capture.d.ts +25 -0
  36. package/dist/format/capture.js +6 -0
  37. package/dist/format/capture.js.map +1 -1
  38. package/dist/format/document.d.ts +28 -5
  39. package/dist/format/document.js +8 -2
  40. package/dist/format/document.js.map +1 -1
  41. package/dist/format/drive.d.ts +191 -0
  42. package/dist/format/drive.js +343 -0
  43. package/dist/format/drive.js.map +1 -0
  44. package/dist/format/environment.d.ts +20 -2
  45. package/dist/format/environment.js +7 -0
  46. package/dist/format/environment.js.map +1 -1
  47. package/dist/format/index.d.ts +13 -3
  48. package/dist/format/index.js +8 -2
  49. package/dist/format/index.js.map +1 -1
  50. package/dist/format/intervention.d.ts +79 -0
  51. package/dist/format/intervention.js.map +1 -1
  52. package/dist/format/origin.d.ts +32 -0
  53. package/dist/format/origin.js +54 -0
  54. package/dist/format/origin.js.map +1 -0
  55. package/dist/format/packed.d.ts +32 -0
  56. package/dist/format/packed.js +23 -0
  57. package/dist/format/packed.js.map +1 -0
  58. package/dist/format/shape.d.ts +31 -0
  59. package/dist/format/shape.js +269 -0
  60. package/dist/format/shape.js.map +1 -0
  61. package/dist/format/snapshot.d.ts +2 -0
  62. package/dist/format/snapshot.js.map +1 -1
  63. package/dist/format/stabilize.d.ts +35 -6
  64. package/dist/format/stabilize.js +58 -103
  65. package/dist/format/stabilize.js.map +1 -1
  66. package/dist/format/steps.d.ts +44 -0
  67. package/dist/format/steps.js +67 -0
  68. package/dist/format/steps.js.map +1 -0
  69. package/dist/format/waits.d.ts +12 -0
  70. package/dist/format/waits.js +156 -0
  71. package/dist/format/waits.js.map +1 -0
  72. package/dist/format/width.d.ts +35 -0
  73. package/dist/format/width.js +38 -0
  74. package/dist/format/width.js.map +1 -0
  75. package/dist/index.d.ts +2 -1
  76. package/dist/index.js +2 -1
  77. package/dist/index.js.map +1 -1
  78. package/dist/judge/docket.js +1 -0
  79. package/dist/judge/docket.js.map +1 -1
  80. package/dist/judge/index.d.ts +1 -0
  81. package/dist/judge/index.js +1 -0
  82. package/dist/judge/index.js.map +1 -1
  83. package/dist/judge/restamp.d.ts +20 -0
  84. package/dist/judge/restamp.js +20 -0
  85. package/dist/judge/restamp.js.map +1 -0
  86. package/dist/nearest.d.ts +46 -0
  87. package/dist/nearest.js +102 -0
  88. package/dist/nearest.js.map +1 -0
  89. package/dist/relate/before.d.ts +19 -24
  90. package/dist/relate/before.js +37 -39
  91. package/dist/relate/before.js.map +1 -1
  92. package/dist/relate/beyond.d.ts +59 -0
  93. package/dist/relate/beyond.js +98 -0
  94. package/dist/relate/beyond.js.map +1 -0
  95. package/dist/relate/carried.d.ts +122 -0
  96. package/dist/relate/carried.js +279 -0
  97. package/dist/relate/carried.js.map +1 -0
  98. package/dist/relate/graph.d.ts +14 -12
  99. package/dist/relate/graph.js +12 -11
  100. package/dist/relate/graph.js.map +1 -1
  101. package/dist/relate/index.d.ts +3 -1
  102. package/dist/relate/index.js +3 -1
  103. package/dist/relate/index.js.map +1 -1
  104. package/dist/relate/merkle.js +2 -11
  105. package/dist/relate/merkle.js.map +1 -1
  106. package/dist/relate/narrow.d.ts +2 -2
  107. package/dist/relate/narrow.js +26 -73
  108. package/dist/relate/narrow.js.map +1 -1
  109. package/dist/relate/reach.d.ts +6 -6
  110. package/dist/relate/reach.js +10 -53
  111. package/dist/relate/reach.js.map +1 -1
  112. package/dist/relate/records.d.ts +6 -5
  113. package/dist/relate/records.js +3 -2
  114. package/dist/relate/records.js.map +1 -1
  115. package/dist/rules/index.d.ts +3 -3
  116. package/dist/rules/index.js +3 -3
  117. package/dist/rules/index.js.map +1 -1
  118. package/dist/rules/normalize/cascade.d.ts +12 -0
  119. package/dist/rules/normalize/cascade.js +34 -16
  120. package/dist/rules/normalize/cascade.js.map +1 -1
  121. package/dist/rules/normalize/fixture.d.ts +3 -0
  122. package/dist/rules/normalize/fixture.js +3 -1
  123. package/dist/rules/normalize/fixture.js.map +1 -1
  124. package/dist/rules/normalize/index.d.ts +2 -2
  125. package/dist/rules/normalize/index.js +9 -2
  126. package/dist/rules/normalize/index.js.map +1 -1
  127. package/dist/rules/normalize/value.d.ts +13 -2
  128. package/dist/rules/normalize/value.js +42 -8
  129. package/dist/rules/normalize/value.js.map +1 -1
  130. package/dist/rules/normalize/wrapper.js +5 -0
  131. package/dist/rules/normalize/wrapper.js.map +1 -1
  132. package/dist/rules/ruleset.d.ts +24 -4
  133. package/dist/rules/ruleset.js +24 -4
  134. package/dist/rules/ruleset.js.map +1 -1
  135. package/dist/segment/index.d.ts +20 -4
  136. package/dist/segment/index.js +28 -7
  137. package/dist/segment/index.js.map +1 -1
  138. package/dist/shard/index.d.ts +11 -0
  139. package/dist/shard/index.js +11 -0
  140. package/dist/shard/index.js.map +1 -0
  141. package/dist/shard/placement.d.ts +119 -0
  142. package/dist/shard/placement.js +156 -0
  143. package/dist/shard/placement.js.map +1 -0
  144. package/dist/share/index.d.ts +1 -1
  145. package/dist/share/index.js +1 -1
  146. package/dist/share/index.js.map +1 -1
  147. package/package.json +8 -1
@@ -0,0 +1,269 @@
1
+ import { FORMAT_VERSION } from './packed.js';
2
+ /**
3
+ * Whether a value read back from a file or a store is the format it claims.
4
+ *
5
+ * Read, not believed: a value parsed from JSON is `unknown` until one of these
6
+ * says otherwise, and each checks every field its type declares, so that there
7
+ * is one answer to what a snapshot, a document or a subject is. What a reader
8
+ * can re-derive, a digest against the bytes it names, is the reader's to check.
9
+ */
10
+ /**
11
+ * Whether a value is a `SemanticSnapshot`, every node down to the leaves.
12
+ *
13
+ * The readers walk all of it: the matcher pairs every child, and a node that is
14
+ * not one throws there instead of reading as a snapshot that is not recorded.
15
+ * A capture file and a packed snapshot are the two places one is read back.
16
+ * Whose snapshot it is, against the subject it was filed under, is the
17
+ * reader's question.
18
+ */
19
+ export function isSemanticSnapshot(value) {
20
+ const snapshot = record(value);
21
+ const profile = snapshot === null ? null : record(snapshot.profile);
22
+ const environment = snapshot === null ? null : record(snapshot.environment);
23
+ const root = snapshot === null ? null : record(snapshot.root);
24
+ return (snapshot !== null &&
25
+ snapshot.formatVersion === FORMAT_VERSION &&
26
+ isSubjectRef(snapshot.subject) &&
27
+ profile !== null &&
28
+ (profile.id === 'jsdom' || profile.id === 'chromium') &&
29
+ ['ariaTree', 'declaredStyle', 'computedStyle', 'layout', 'raster'].every((field) => typeof profile[field] === 'boolean') &&
30
+ environment !== null &&
31
+ typeof environment.digest === 'string' &&
32
+ typeof environment.semanticDigest === 'string' &&
33
+ environmentInputsShape(environment.inputs) &&
34
+ typeof snapshot.renderHash === 'string' &&
35
+ typeof snapshot.structureHash === 'string' &&
36
+ typeof snapshot.styleHash === 'string' &&
37
+ root !== null &&
38
+ semanticNodeShape(root) &&
39
+ Array.isArray(snapshot.styleProvenance) &&
40
+ snapshot.styleProvenance.every(styleProvenanceShape) &&
41
+ (snapshot.ignoreSites === undefined ||
42
+ (Array.isArray(snapshot.ignoreSites) && snapshot.ignoreSites.every(ignoreSiteShape))) &&
43
+ diagnosticArray(snapshot.diagnostics));
44
+ }
45
+ /**
46
+ * Whether a value is a `RenderDocument`, its resources' shape included.
47
+ *
48
+ * Shape only: that a resource's bytes are base64 and hash to its digest, and
49
+ * that a document is resource-closed, are what a reader that needs them checks.
50
+ */
51
+ export function isRenderDocument(value) {
52
+ const document = record(value);
53
+ const frame = document === null ? null : record(document.frame);
54
+ const viewport = document === null ? null : record(document.viewport);
55
+ return (document !== null &&
56
+ document.documentVersion === 1 &&
57
+ isSubjectRef(document.subject) &&
58
+ typeof document.html === 'string' &&
59
+ frame !== null &&
60
+ stringRecord(frame.html) &&
61
+ stringRecord(frame.body) &&
62
+ Array.isArray(frame.ancestors) &&
63
+ frame.ancestors.every(frameElementShape) &&
64
+ (frame.containerWidth === undefined || finiteNumber(frame.containerWidth)) &&
65
+ stringArray(document.css) &&
66
+ viewport !== null &&
67
+ finiteNumber(viewport.width) &&
68
+ finiteNumber(viewport.height) &&
69
+ finiteNumber(viewport.deviceScaleFactor) &&
70
+ (viewport.colorScheme === 'light' || viewport.colorScheme === 'dark') &&
71
+ stringRecord(document.inherited) &&
72
+ stringArray(document.fonts) &&
73
+ (document.assets === undefined || stringRecord(document.assets)) &&
74
+ optionalString(document.baseUrl) &&
75
+ (document.resources === undefined ||
76
+ (record(document.resources) !== null && Object.values(document.resources).every(resourceShape))) &&
77
+ diagnosticArray(document.diagnostics));
78
+ }
79
+ /** Whether a value is a `SubjectRef`: an id, one of the four kinds, and an optional title. */
80
+ export function isSubjectRef(value) {
81
+ const subject = record(value);
82
+ return subject !== null &&
83
+ typeof subject.id === 'string' &&
84
+ ['story', 'route', 'fixture', 'value'].includes(String(subject.kind)) &&
85
+ optionalString(subject.title);
86
+ }
87
+ function frameElementShape(value) {
88
+ const element = record(value);
89
+ return element !== null && typeof element.tag === 'string' && stringRecord(element.attributes);
90
+ }
91
+ function resourceShape(value) {
92
+ const resource = record(value);
93
+ return resource !== null &&
94
+ typeof resource.contentType === 'string' &&
95
+ typeof resource.bytes === 'string' &&
96
+ typeof resource.digest === 'string' &&
97
+ (resource.status === undefined || finiteNumber(resource.status));
98
+ }
99
+ function environmentInputsShape(value) {
100
+ const inputs = record(value);
101
+ const viewport = inputs === null ? null : record(inputs.viewport);
102
+ return inputs !== null &&
103
+ (inputs.profile === 'jsdom' || inputs.profile === 'chromium') &&
104
+ typeof inputs.engine === 'string' &&
105
+ typeof inputs.ruleset === 'string' &&
106
+ typeof inputs.allowlist === 'string' &&
107
+ viewport !== null &&
108
+ finiteNumber(viewport.width) &&
109
+ finiteNumber(viewport.height) &&
110
+ finiteNumber(viewport.deviceScaleFactor) &&
111
+ (viewport.colorScheme === 'light' || viewport.colorScheme === 'dark') &&
112
+ stringArray(inputs.fonts) &&
113
+ scalarRecord(inputs.conditions) &&
114
+ stringRecord(inputs.assets) &&
115
+ optionalString(inputs.stabilization) &&
116
+ optionalString(inputs.timezoneId) &&
117
+ optionalString(inputs.locale);
118
+ }
119
+ function styleProvenanceShape(value) {
120
+ const entry = record(value);
121
+ if (entry === null ||
122
+ typeof entry.path !== 'string' ||
123
+ typeof entry.property !== 'string' ||
124
+ typeof entry.sheet !== 'string' ||
125
+ typeof entry.selector !== 'string' ||
126
+ !optionalString(entry.tokenName))
127
+ return false;
128
+ if (entry.source === undefined)
129
+ return true;
130
+ const source = record(entry.source);
131
+ return source !== null && typeof source.file === 'string' && finiteNumber(source.line);
132
+ }
133
+ function semanticNodeShape(node) {
134
+ return (typeof node.path === 'string' &&
135
+ typeof node.tag === 'string' &&
136
+ optionalString(node.alias) &&
137
+ optionalString(node.role) &&
138
+ optionalString(node.name) &&
139
+ optionalString(node.description) &&
140
+ (node.state === undefined || scalarRecord(node.state)) &&
141
+ stringRecord(node.attributes) &&
142
+ stringRecord(node.style) &&
143
+ (node.tokens === undefined || stringRecord(node.tokens)) &&
144
+ (node.styleTokens === undefined || stringRecord(node.styleTokens)) &&
145
+ (node.rect === undefined || rectShape(node.rect)) &&
146
+ optionalString(node.text) &&
147
+ (node.provenance === undefined || provenanceShape(node.provenance)) &&
148
+ (node.wiring === undefined || wiringShape(node.wiring)) &&
149
+ (node.holding === undefined || holdingShape(node.holding)) &&
150
+ (node.portalled === undefined || typeof node.portalled === 'boolean') &&
151
+ (node.ignoredBy === undefined || stringArray(node.ignoredBy)) &&
152
+ optionalString(node.unread) &&
153
+ Array.isArray(node.children) &&
154
+ node.children.every((child) => {
155
+ const parsed = record(child);
156
+ return parsed !== null && semanticNodeShape(parsed);
157
+ }));
158
+ }
159
+ function ignoreSiteShape(value) {
160
+ const site = record(value);
161
+ return site !== null &&
162
+ typeof site.path === 'string' &&
163
+ typeof site.rule === 'string' &&
164
+ (site.rect === undefined || rectShape(site.rect));
165
+ }
166
+ function rectShape(value) {
167
+ const rect = record(value);
168
+ return rect !== null &&
169
+ finiteNumber(rect.x) &&
170
+ finiteNumber(rect.y) &&
171
+ finiteNumber(rect.width) &&
172
+ finiteNumber(rect.height);
173
+ }
174
+ function provenanceShape(value) {
175
+ const provenance = record(value);
176
+ return provenance !== null &&
177
+ Array.isArray(provenance.owners) &&
178
+ provenance.owners.every(ownerShape) &&
179
+ optionalString(provenance.createdBy) &&
180
+ (provenance.source === undefined || sourceLocationShape(provenance.source)) &&
181
+ (provenance.stack === undefined ||
182
+ (Array.isArray(provenance.stack) && provenance.stack.every(stackFrameShape)));
183
+ }
184
+ function ownerShape(value) {
185
+ const owner = record(value);
186
+ return owner !== null &&
187
+ typeof owner.name === 'string' &&
188
+ typeof owner.propsDigest === 'string' &&
189
+ optionalString(owner.createdBy);
190
+ }
191
+ function sourceLocationShape(value) {
192
+ const source = record(value);
193
+ return source !== null &&
194
+ typeof source.file === 'string' &&
195
+ finiteNumber(source.line) &&
196
+ finiteNumber(source.column);
197
+ }
198
+ function stackFrameShape(value) {
199
+ const frame = record(value);
200
+ return frame !== null &&
201
+ typeof frame.url === 'string' &&
202
+ finiteNumber(frame.line) &&
203
+ finiteNumber(frame.column) &&
204
+ optionalString(frame.function);
205
+ }
206
+ function wiringShape(value) {
207
+ const wiring = record(value);
208
+ return wiring !== null &&
209
+ (wiring.hooks === undefined || stringArray(wiring.hooks)) &&
210
+ (wiring.wrappers === undefined ||
211
+ (Array.isArray(wiring.wrappers) &&
212
+ wiring.wrappers.every((entry) => entry === 'memo' || entry === 'forwardRef'))) &&
213
+ (wiring.contexts === undefined || stringArray(wiring.contexts)) &&
214
+ optionalString(wiring.key);
215
+ }
216
+ function holdingShape(value) {
217
+ const holding = record(value);
218
+ return holding !== null &&
219
+ (holding.cells === undefined || (Array.isArray(holding.cells) && holding.cells.every((value) => {
220
+ const cell = record(value);
221
+ return cell !== null && finiteNumber(cell.index) && typeof cell.hook === 'string' && typeof cell.digest === 'string';
222
+ }))) &&
223
+ (holding.contexts === undefined || heldValues(holding.contexts)) &&
224
+ (holding.props === undefined || heldValues(holding.props)) &&
225
+ optionalString(holding.unread);
226
+ }
227
+ function heldValues(value) {
228
+ return Array.isArray(value) && value.every((entry) => {
229
+ const held = record(entry);
230
+ return held !== null && typeof held.name === 'string' && typeof held.digest === 'string';
231
+ });
232
+ }
233
+ function diagnosticArray(value) {
234
+ return Array.isArray(value) && value.every((entry) => {
235
+ const diagnostic = record(entry);
236
+ return diagnostic !== null &&
237
+ (diagnostic.severity === 'warn' || diagnostic.severity === 'error') &&
238
+ typeof diagnostic.code === 'string' &&
239
+ typeof diagnostic.message === 'string' &&
240
+ optionalString(diagnostic.nodePath);
241
+ });
242
+ }
243
+ // A plain object. `@variance-authority/unit-test`'s capture-file reader keeps
244
+ // the same guard privately: it is re-typed in several packages and has no
245
+ // exported owner, and exporting it from here would make it part of the format.
246
+ function record(value) {
247
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
248
+ ? value
249
+ : null;
250
+ }
251
+ function stringRecord(value) {
252
+ const parsed = record(value);
253
+ return parsed !== null && Object.values(parsed).every((entry) => typeof entry === 'string');
254
+ }
255
+ function scalarRecord(value) {
256
+ const parsed = record(value);
257
+ return parsed !== null && Object.values(parsed).every((entry) => ['string', 'boolean', 'number'].includes(typeof entry) &&
258
+ (typeof entry !== 'number' || Number.isFinite(entry)));
259
+ }
260
+ function stringArray(value) {
261
+ return Array.isArray(value) && value.every((entry) => typeof entry === 'string');
262
+ }
263
+ function optionalString(value) {
264
+ return value === undefined || typeof value === 'string';
265
+ }
266
+ function finiteNumber(value) {
267
+ return typeof value === 'number' && Number.isFinite(value);
268
+ }
269
+ //# sourceMappingURL=shape.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shape.js","sourceRoot":"","sources":["../../src/format/shape.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SubjectRef } from './capture.js';\nimport type { RenderDocument } from './document.js';\nimport { FORMAT_VERSION } from './packed.js';\nimport type { SemanticSnapshot } from './snapshot.js';\n\n/**\n * Whether a value read back from a file or a store is the format it claims.\n *\n * Read, not believed: a value parsed from JSON is `unknown` until one of these\n * says otherwise, and each checks every field its type declares, so that there\n * is one answer to what a snapshot, a document or a subject is. What a reader\n * can re-derive, a digest against the bytes it names, is the reader's to check.\n */\n\n/**\n * Whether a value is a `SemanticSnapshot`, every node down to the leaves.\n *\n * The readers walk all of it: the matcher pairs every child, and a node that is\n * not one throws there instead of reading as a snapshot that is not recorded.\n * A capture file and a packed snapshot are the two places one is read back.\n * Whose snapshot it is, against the subject it was filed under, is the\n * reader's question.\n */\nexport function isSemanticSnapshot(value: unknown): value is SemanticSnapshot {\n const snapshot = record(value);\n const profile = snapshot === null ? null : record(snapshot.profile);\n const environment = snapshot === null ? null : record(snapshot.environment);\n const root = snapshot === null ? null : record(snapshot.root);\n return (\n snapshot !== null &&\n snapshot.formatVersion === FORMAT_VERSION &&\n isSubjectRef(snapshot.subject) &&\n profile !== null &&\n (profile.id === 'jsdom' || profile.id === 'chromium') &&\n ['ariaTree', 'declaredStyle', 'computedStyle', 'layout', 'raster'].every(\n (field) => typeof profile[field] === 'boolean',\n ) &&\n environment !== null &&\n typeof environment.digest === 'string' &&\n typeof environment.semanticDigest === 'string' &&\n environmentInputsShape(environment.inputs) &&\n typeof snapshot.renderHash === 'string' &&\n typeof snapshot.structureHash === 'string' &&\n typeof snapshot.styleHash === 'string' &&\n root !== null &&\n semanticNodeShape(root) &&\n Array.isArray(snapshot.styleProvenance) &&\n snapshot.styleProvenance.every(styleProvenanceShape) &&\n (snapshot.ignoreSites === undefined ||\n (Array.isArray(snapshot.ignoreSites) && snapshot.ignoreSites.every(ignoreSiteShape))) &&\n diagnosticArray(snapshot.diagnostics)\n );\n}\n\n/**\n * Whether a value is a `RenderDocument`, its resources' shape included.\n *\n * Shape only: that a resource's bytes are base64 and hash to its digest, and\n * that a document is resource-closed, are what a reader that needs them checks.\n */\nexport function isRenderDocument(value: unknown): value is RenderDocument {\n const document = record(value);\n const frame = document === null ? null : record(document.frame);\n const viewport = document === null ? null : record(document.viewport);\n return (\n document !== null &&\n document.documentVersion === 1 &&\n isSubjectRef(document.subject) &&\n typeof document.html === 'string' &&\n frame !== null &&\n stringRecord(frame.html) &&\n stringRecord(frame.body) &&\n Array.isArray(frame.ancestors) &&\n frame.ancestors.every(frameElementShape) &&\n (frame.containerWidth === undefined || finiteNumber(frame.containerWidth)) &&\n stringArray(document.css) &&\n viewport !== null &&\n finiteNumber(viewport.width) &&\n finiteNumber(viewport.height) &&\n finiteNumber(viewport.deviceScaleFactor) &&\n (viewport.colorScheme === 'light' || viewport.colorScheme === 'dark') &&\n stringRecord(document.inherited) &&\n stringArray(document.fonts) &&\n (document.assets === undefined || stringRecord(document.assets)) &&\n optionalString(document.baseUrl) &&\n (document.resources === undefined ||\n (record(document.resources) !== null && Object.values(document.resources as object).every(resourceShape))) &&\n diagnosticArray(document.diagnostics)\n );\n}\n\n/** Whether a value is a `SubjectRef`: an id, one of the four kinds, and an optional title. */\nexport function isSubjectRef(value: unknown): value is SubjectRef {\n const subject = record(value);\n return subject !== null &&\n typeof subject.id === 'string' &&\n ['story', 'route', 'fixture', 'value'].includes(String(subject.kind)) &&\n optionalString(subject.title);\n}\n\nfunction frameElementShape(value: unknown): boolean {\n const element = record(value);\n return element !== null && typeof element.tag === 'string' && stringRecord(element.attributes);\n}\n\nfunction resourceShape(value: unknown): boolean {\n const resource = record(value);\n return resource !== null &&\n typeof resource.contentType === 'string' &&\n typeof resource.bytes === 'string' &&\n typeof resource.digest === 'string' &&\n (resource.status === undefined || finiteNumber(resource.status));\n}\n\nfunction environmentInputsShape(value: unknown): boolean {\n const inputs = record(value);\n const viewport = inputs === null ? null : record(inputs.viewport);\n return inputs !== null &&\n (inputs.profile === 'jsdom' || inputs.profile === 'chromium') &&\n typeof inputs.engine === 'string' &&\n typeof inputs.ruleset === 'string' &&\n typeof inputs.allowlist === 'string' &&\n viewport !== null &&\n finiteNumber(viewport.width) &&\n finiteNumber(viewport.height) &&\n finiteNumber(viewport.deviceScaleFactor) &&\n (viewport.colorScheme === 'light' || viewport.colorScheme === 'dark') &&\n stringArray(inputs.fonts) &&\n scalarRecord(inputs.conditions) &&\n stringRecord(inputs.assets) &&\n optionalString(inputs.stabilization) &&\n optionalString(inputs.timezoneId) &&\n optionalString(inputs.locale);\n}\n\nfunction styleProvenanceShape(value: unknown): boolean {\n const entry = record(value);\n if (entry === null ||\n typeof entry.path !== 'string' ||\n typeof entry.property !== 'string' ||\n typeof entry.sheet !== 'string' ||\n typeof entry.selector !== 'string' ||\n !optionalString(entry.tokenName)) return false;\n if (entry.source === undefined) return true;\n const source = record(entry.source);\n return source !== null && typeof source.file === 'string' && finiteNumber(source.line);\n}\n\nfunction semanticNodeShape(node: Record<string, unknown>): boolean {\n return (\n typeof node.path === 'string' &&\n typeof node.tag === 'string' &&\n optionalString(node.alias) &&\n optionalString(node.role) &&\n optionalString(node.name) &&\n optionalString(node.description) &&\n (node.state === undefined || scalarRecord(node.state)) &&\n stringRecord(node.attributes) &&\n stringRecord(node.style) &&\n (node.tokens === undefined || stringRecord(node.tokens)) &&\n (node.styleTokens === undefined || stringRecord(node.styleTokens)) &&\n (node.rect === undefined || rectShape(node.rect)) &&\n optionalString(node.text) &&\n (node.provenance === undefined || provenanceShape(node.provenance)) &&\n (node.wiring === undefined || wiringShape(node.wiring)) &&\n (node.holding === undefined || holdingShape(node.holding)) &&\n (node.portalled === undefined || typeof node.portalled === 'boolean') &&\n (node.ignoredBy === undefined || stringArray(node.ignoredBy)) &&\n optionalString(node.unread) &&\n Array.isArray(node.children) &&\n node.children.every((child) => {\n const parsed = record(child);\n return parsed !== null && semanticNodeShape(parsed);\n })\n );\n}\n\nfunction ignoreSiteShape(value: unknown): boolean {\n const site = record(value);\n return site !== null &&\n typeof site.path === 'string' &&\n typeof site.rule === 'string' &&\n (site.rect === undefined || rectShape(site.rect));\n}\n\nfunction rectShape(value: unknown): boolean {\n const rect = record(value);\n return rect !== null &&\n finiteNumber(rect.x) &&\n finiteNumber(rect.y) &&\n finiteNumber(rect.width) &&\n finiteNumber(rect.height);\n}\n\nfunction provenanceShape(value: unknown): boolean {\n const provenance = record(value);\n return provenance !== null &&\n Array.isArray(provenance.owners) &&\n provenance.owners.every(ownerShape) &&\n optionalString(provenance.createdBy) &&\n (provenance.source === undefined || sourceLocationShape(provenance.source)) &&\n (provenance.stack === undefined ||\n (Array.isArray(provenance.stack) && provenance.stack.every(stackFrameShape)));\n}\n\nfunction ownerShape(value: unknown): boolean {\n const owner = record(value);\n return owner !== null &&\n typeof owner.name === 'string' &&\n typeof owner.propsDigest === 'string' &&\n optionalString(owner.createdBy);\n}\n\nfunction sourceLocationShape(value: unknown): boolean {\n const source = record(value);\n return source !== null &&\n typeof source.file === 'string' &&\n finiteNumber(source.line) &&\n finiteNumber(source.column);\n}\n\nfunction stackFrameShape(value: unknown): boolean {\n const frame = record(value);\n return frame !== null &&\n typeof frame.url === 'string' &&\n finiteNumber(frame.line) &&\n finiteNumber(frame.column) &&\n optionalString(frame.function);\n}\n\nfunction wiringShape(value: unknown): boolean {\n const wiring = record(value);\n return wiring !== null &&\n (wiring.hooks === undefined || stringArray(wiring.hooks)) &&\n (wiring.wrappers === undefined ||\n (Array.isArray(wiring.wrappers) &&\n wiring.wrappers.every((entry) => entry === 'memo' || entry === 'forwardRef'))) &&\n (wiring.contexts === undefined || stringArray(wiring.contexts)) &&\n optionalString(wiring.key);\n}\n\nfunction holdingShape(value: unknown): boolean {\n const holding = record(value);\n return holding !== null &&\n (holding.cells === undefined || (Array.isArray(holding.cells) && holding.cells.every((value) => {\n const cell = record(value);\n return cell !== null && finiteNumber(cell.index) && typeof cell.hook === 'string' && typeof cell.digest === 'string';\n }))) &&\n (holding.contexts === undefined || heldValues(holding.contexts)) &&\n (holding.props === undefined || heldValues(holding.props)) &&\n optionalString(holding.unread);\n}\n\nfunction heldValues(value: unknown): boolean {\n return Array.isArray(value) && value.every((entry) => {\n const held = record(entry);\n return held !== null && typeof held.name === 'string' && typeof held.digest === 'string';\n });\n}\n\nfunction diagnosticArray(value: unknown): boolean {\n return Array.isArray(value) && value.every((entry) => {\n const diagnostic = record(entry);\n return diagnostic !== null &&\n (diagnostic.severity === 'warn' || diagnostic.severity === 'error') &&\n typeof diagnostic.code === 'string' &&\n typeof diagnostic.message === 'string' &&\n optionalString(diagnostic.nodePath);\n });\n}\n\n// A plain object. `@variance-authority/unit-test`'s capture-file reader keeps\n// the same guard privately: it is re-typed in several packages and has no\n// exported owner, and exporting it from here would make it part of the format.\nfunction record(value: unknown): Record<string, unknown> | null {\n return typeof value === 'object' && value !== null && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : null;\n}\n\nfunction stringRecord(value: unknown): boolean {\n const parsed = record(value);\n return parsed !== null && Object.values(parsed).every((entry) => typeof entry === 'string');\n}\n\nfunction scalarRecord(value: unknown): boolean {\n const parsed = record(value);\n return parsed !== null && Object.values(parsed).every(\n (entry) => ['string', 'boolean', 'number'].includes(typeof entry) &&\n (typeof entry !== 'number' || Number.isFinite(entry)),\n );\n}\n\nfunction stringArray(value: unknown): boolean {\n return Array.isArray(value) && value.every((entry) => typeof entry === 'string');\n}\n\nfunction optionalString(value: unknown): boolean {\n return value === undefined || typeof value === 'string';\n}\n\nfunction finiteNumber(value: unknown): boolean {\n return typeof value === 'number' && Number.isFinite(value);\n}\n"]}
@@ -162,6 +162,8 @@ export interface SemanticNode {
162
162
  * on `RawNode`. See {@link SemanticSnapshot.ignoreSites}.
163
163
  */
164
164
  readonly ignoredBy?: readonly string[];
165
+ /** What was not read about this node, carried from `RawNode.unread`. Outside every hash. */
166
+ readonly unread?: string;
165
167
  readonly children: readonly SemanticNode[];
166
168
  }
167
169
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/format/snapshot.ts"],"names":[],"mappings":"","sourcesContent":["import type { SubjectRef, Rect, Diagnostic } from './capture.js';\nimport type { EnvironmentKey } from './environment.js';\nimport type { Digest } from './hash.js';\nimport type { ObservationProfile } from './profile.js';\nimport type { Provenance } from './provenance.js';\nimport type { Wiring } from './wiring.js';\nimport type { Holding } from './holding.js';\n\n/**\n * The normalized semantic snapshot: the verdict's input, and the thing a render\n * hash addresses.\n *\n * Everything volatile is gone by this point. Ids have become structural aliases,\n * class attributes have been dropped entirely, inapplicable CSS has been pruned,\n * and the cascade has been resolved to winning values (ADR-0003). What remains is\n * meant to be *read*: a snapshot a reviewer refuses to open is a snapshot that\n * gets rubber-stamped.\n */\nexport interface SemanticSnapshot {\n readonly formatVersion: 1;\n readonly subject: SubjectRef;\n readonly profile: ObservationProfile;\n readonly environment: EnvironmentKey;\n\n /** Content hash of `root` under `environment`. The identity of this state. */\n readonly renderHash: Digest;\n\n /**\n * Structure and applicable style, hashed separately.\n *\n * These back the tier below full semantic comparison (ADR-0003): matching both\n * settles a subject without resolving layout or taking a screenshot. Split\n * rather than combined because the docket wants to say *which* held — \"the DOM\n * is identical, only styling moved\" is the sentence that turns a diff into a\n * token-band root.\n */\n readonly structureHash: Digest;\n readonly styleHash: Digest;\n\n readonly root: SemanticNode;\n\n /**\n * Where each winning declaration came from. Deliberately outside the hash.\n *\n * Pruning discards exactly what attribution needs — *which rule set this?* — so\n * it is preserved here instead. Keeping it out of the hash is the point: moving\n * a rule between files renames a source without changing a render, and must not\n * invalidate a baseline.\n */\n readonly styleProvenance: readonly StyleProvenanceEntry[];\n\n /**\n * Subtrees the operator declared out of scope, resolved against *this* tree.\n *\n * Deliberately outside the hash, for the reason `styleProvenance` is: an ignore\n * changes what a run says about a render, never the render. Folding it into the\n * identity would invalidate every baseline in the repository the first time\n * somebody masked a clock, which is the surest way to make a safety feature the\n * thing people turn off.\n *\n * Present only when something was excluded, so a snapshot from a run with no\n * ignores is byte-identical to one produced before this field existed.\n */\n readonly ignoreSites?: readonly IgnoreSite[];\n\n /**\n * What the collector or the normalizer could not do, carried forward.\n *\n * Outside the hash: a diagnostic describes the *observation*, not the render,\n * and a snapshot that hashed its own warnings would invalidate whenever the\n * warning text was reworded. But it must survive to the verdict — a subject\n * whose capture half-failed is not a subject that legitimately `unchanged`,\n * and silently discarding that fact is how a broken collector reads as a clean\n * build.\n */\n readonly diagnostics: readonly Diagnostic[];\n}\n\n/**\n * A node's stable address: child indices from the root, e.g. `0/2/1`.\n *\n * Positional because nothing else survives. Ids are volatile (that is why they\n * were aliased away), and class names have been dropped. Position is honest about\n * what it costs: inserting a sibling renumbers everything after it, so the differ\n * matches nodes by shape and provenance before falling back to path (see\n * `diff.ts`), and treats a path change alone as evidence of nothing.\n */\nexport type NodePath = string;\n\nexport interface SemanticNode {\n readonly path: NodePath;\n readonly tag: string;\n\n /** Structural alias, e.g. `#a0`. Absent when the node carried no id. */\n readonly alias?: string;\n\n readonly role?: string;\n readonly name?: string;\n\n /**\n * Accessible description, resolved. See `RawAria.description` for why this is\n * a field of its own rather than an attribute.\n */\n readonly description?: string;\n\n readonly state?: Readonly<Record<string, string | boolean | number>>;\n\n /** Semantic attributes surviving normalization. `class` is never among them. */\n readonly attributes: Readonly<Record<string, string>>;\n\n /** Winning values for allowlisted properties, canonicalized. */\n readonly style: Readonly<Record<string, string>>;\n\n /**\n * Custom properties this node's winning declarations resolved through, as\n * name → resolved value.\n *\n * Recording the name alongside the value is what lets a design-token change\n * surface as one root with counted collateral instead of hundreds of unrelated\n * colour diffs (spec §5, `token` band).\n */\n readonly tokens?: Readonly<Record<string, string>>;\n\n /**\n * Which token each styled property resolved through, including by inheritance.\n *\n * Outside the hash, like `styleProvenance` and for the same reason: swapping a\n * literal for a token of the same value renames a source without changing a\n * render. It exists so the differ can tell *which* property a token edit\n * explains — `tokens` alone says a node uses tokens, not which of its values\n * one drove.\n */\n readonly styleTokens?: Readonly<Record<string, string>>;\n\n /** Present only under a profile with layout. Absent, never zeroed (ADR-0002). */\n readonly rect?: Rect;\n\n /** Text content, or its digest when policy declares the region volatile. */\n readonly text?: string;\n\n readonly provenance?: Provenance;\n\n /**\n * How the framework holds this node's component, when a framework adapter\n * supplied it. See {@link Wiring}.\n *\n * On the node rather than on `Provenance`, though both come from the same\n * adapter and the same fiber, because they answer to different readers.\n * Provenance exists so a diff arrives carrying a name; wiring is a *dimension\n * of the subject*, hashed into a band of its own, and a band's inputs belong\n * beside the other bands' inputs — next to `style` and `attributes`, which is\n * where `shapeOf` goes looking.\n */\n readonly wiring?: Wiring;\n\n /**\n * What that component was holding. **Outside every hash**, always.\n *\n * The field beside it is the contrast worth reading: `wiring` is a band and\n * this is evidence. `Wiring` refuses state values because a hook's value is\n * exactly what may legitimately differ between two readings of one page, and\n * that refusal is right and is not softened here — a `holding` reaches the\n * snapshot and reaches no digest, so a subject whose clock ticked has the same\n * `renderHash` it had before this field existed.\n *\n * It rides outside on the same terms as `styleProvenance`, `styleTokens` and\n * `ignoredBy`, by the same mechanism: `structureOf`, `styleOf` and `shapeOf`\n * project the fields they hash **by name**, so a field none of them names\n * cannot reach an identity. `holding.hash.test.ts` asserts that rather than\n * trusting it, because the cost of being wrong is every baseline in a\n * repository invalidating the first time a component held a timestamp.\n */\n readonly holding?: Holding;\n\n /**\n * `true` on the root of a subtree rendered through a portal.\n *\n * Recorded in the structure hash so that \"the dialog moved from inline to\n * portalled\" is a change rather than a coincidence of identical content, and\n * so the docket can say *where* something rendered, not only that it exists.\n */\n readonly portalled?: boolean;\n\n /**\n * Ignore rules this node was found under. Outside every hash, like its origin\n * on `RawNode`. See {@link SemanticSnapshot.ignoreSites}.\n */\n readonly ignoredBy?: readonly string[];\n\n readonly children: readonly SemanticNode[];\n}\n\n/**\n * A subtree the operator excluded, resolved to a path and a box (spec 0024).\n *\n * Lives in `format` rather than beside the rules that consume it, because it is\n * part of what a snapshot *is*: the collector produced it, it travels with the\n * document over every wire this project has, and `core/judge` is only its first\n * reader. A type the wire format needs cannot live in the policy layer.\n */\nexport interface IgnoreSite {\n /** Root of the excluded subtree, in this snapshot's own path space. */\n readonly path: NodePath;\n\n /** Which rule put it here, matched exactly against `IgnoreRule.id`. */\n readonly rule: string;\n\n /**\n * The box it occupied, for the raster tier.\n *\n * Absent under a profile with no layout — which is ADR-0002 rather than a\n * failure: a profile that cannot measure a box cannot decide pixels either, so\n * there is nothing for the box to subtract from.\n */\n readonly rect?: Rect;\n}\n\n/**\n * A component's own content, hashed per band (ADR-0018).\n *\n * In `format` rather than beside `hashComponents`, which computes it, because a\n * baseline carries these: a stored image plus the component hashes of the\n * document that painted it is what lets a later run tell the component that\n * *caused* a change from the components the change merely moved. That makes this\n * part of the wire format every store and every transport has to preserve, and a\n * type the wire needs cannot live in the layer that derives it.\n */\nexport interface ComponentHash {\n readonly component: string;\n\n /** Boundaries of this component in the subject, counted in document order. */\n readonly instances: number;\n\n /**\n * Tree shape: tags, structural aliases, portalling, allowlisted attributes,\n * and which child boundaries sit where. The `geometry` band's structural half.\n *\n * **Split out of a single `structure` digest on 2026-08-06.** It used to carry\n * the accessible semantics and the text too, and the fusion was invisible\n * until something needed to ask *which band moved* — at which point a baseline\n * could say \"this component changed\" and never say whether a heading was\n * renamed, a paragraph reworded, or a node inserted. Those are three different\n * bands, three different readers, and one of them is the band a route-level\n * test exists to ignore.\n */\n readonly structure: Digest;\n\n /**\n * Role, accessible name, and ARIA state. The `a11y` band.\n *\n * Its own digest because it is the band that must never be absorbed. Every\n * sensitivity level asserts on it, including the ones that ignore everything\n * else — and a level cannot assert on a band that is fused into another one.\n */\n readonly semantics: Digest;\n\n /** Text runs, in document order. The `content` band. */\n readonly text: Digest;\n\n readonly style: Digest;\n\n /**\n * Absent under a profile without layout — absent, never empty (ADR-0002).\n *\n * An empty geometry digest would compare equal between a run that observed no\n * movement and a run that could not observe movement at all, which is the\n * false `unchanged` this system must never produce.\n */\n readonly geometry?: Digest;\n\n /**\n * The box each instance occupies, in document order. Never hashed.\n *\n * A digest can say a component is not what it was and can never say by how\n * much. That is the gap a reviewer falls into: `Button — geometry, token` is\n * true of a control that lost a shadow and of one that grew eight pixels and\n * pushed the page down, and only the second explains why the subject is a\n * different height. Four numbers per instance close it.\n *\n * Absent for a baseline written before this existed and for a profile with no\n * layout — the same absence, and both mean *nobody measured*, never *it did\n * not move*. A `null` entry is a boundary the browser laid out nothing for,\n * kept in place so the list stays parallel to the instances it counts.\n */\n readonly boxes?: readonly (Rect | null)[];\n\n /**\n * Every distinct value the component's own nodes declared, per property, in\n * code-unit order; a custom property appears under its `--name` with the\n * values it resolved to. Never hashed.\n *\n * `boxes` answers *by how much*; this answers *which declaration*. `style`\n * moved says a `Button` is not styled as it was, and a reviewer holding only\n * that opens the file and diffs by eye. With both revisions' values the run\n * says `padding-left 8px → 14px`, which is the line somebody edited.\n *\n * Absent for a baseline written before this existed. That means *nobody\n * recorded it*, and the comparison says nothing about properties rather than\n * reporting every one of them as new.\n */\n readonly values?: Readonly<Record<string, readonly string[]>>;\n}\n\nexport interface StyleProvenanceEntry {\n readonly path: NodePath;\n readonly property: string;\n readonly sheet: string;\n /** Generated segments replaced by placeholders, so it can name a root safely. */\n readonly selector: string;\n readonly tokenName?: string;\n readonly source?: { readonly file: string; readonly line: number };\n}\n"]}
1
+ {"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/format/snapshot.ts"],"names":[],"mappings":"","sourcesContent":["import type { SubjectRef, Rect, Diagnostic } from './capture.js';\nimport type { EnvironmentKey } from './environment.js';\nimport type { Digest } from './hash.js';\nimport type { ObservationProfile } from './profile.js';\nimport type { Provenance } from './provenance.js';\nimport type { Wiring } from './wiring.js';\nimport type { Holding } from './holding.js';\n\n/**\n * The normalized semantic snapshot: the verdict's input, and the thing a render\n * hash addresses.\n *\n * Everything volatile is gone by this point. Ids have become structural aliases,\n * class attributes have been dropped entirely, inapplicable CSS has been pruned,\n * and the cascade has been resolved to winning values (ADR-0003). What remains is\n * meant to be *read*: a snapshot a reviewer refuses to open is a snapshot that\n * gets rubber-stamped.\n */\nexport interface SemanticSnapshot {\n readonly formatVersion: 1;\n readonly subject: SubjectRef;\n readonly profile: ObservationProfile;\n readonly environment: EnvironmentKey;\n\n /** Content hash of `root` under `environment`. The identity of this state. */\n readonly renderHash: Digest;\n\n /**\n * Structure and applicable style, hashed separately.\n *\n * These back the tier below full semantic comparison (ADR-0003): matching both\n * settles a subject without resolving layout or taking a screenshot. Split\n * rather than combined because the docket wants to say *which* held — \"the DOM\n * is identical, only styling moved\" is the sentence that turns a diff into a\n * token-band root.\n */\n readonly structureHash: Digest;\n readonly styleHash: Digest;\n\n readonly root: SemanticNode;\n\n /**\n * Where each winning declaration came from. Deliberately outside the hash.\n *\n * Pruning discards exactly what attribution needs — *which rule set this?* — so\n * it is preserved here instead. Keeping it out of the hash is the point: moving\n * a rule between files renames a source without changing a render, and must not\n * invalidate a baseline.\n */\n readonly styleProvenance: readonly StyleProvenanceEntry[];\n\n /**\n * Subtrees the operator declared out of scope, resolved against *this* tree.\n *\n * Deliberately outside the hash, for the reason `styleProvenance` is: an ignore\n * changes what a run says about a render, never the render. Folding it into the\n * identity would invalidate every baseline in the repository the first time\n * somebody masked a clock, which is the surest way to make a safety feature the\n * thing people turn off.\n *\n * Present only when something was excluded, so a snapshot from a run with no\n * ignores is byte-identical to one produced before this field existed.\n */\n readonly ignoreSites?: readonly IgnoreSite[];\n\n /**\n * What the collector or the normalizer could not do, carried forward.\n *\n * Outside the hash: a diagnostic describes the *observation*, not the render,\n * and a snapshot that hashed its own warnings would invalidate whenever the\n * warning text was reworded. But it must survive to the verdict — a subject\n * whose capture half-failed is not a subject that legitimately `unchanged`,\n * and silently discarding that fact is how a broken collector reads as a clean\n * build.\n */\n readonly diagnostics: readonly Diagnostic[];\n}\n\n/**\n * A node's stable address: child indices from the root, e.g. `0/2/1`.\n *\n * Positional because nothing else survives. Ids are volatile (that is why they\n * were aliased away), and class names have been dropped. Position is honest about\n * what it costs: inserting a sibling renumbers everything after it, so the differ\n * matches nodes by shape and provenance before falling back to path (see\n * `diff.ts`), and treats a path change alone as evidence of nothing.\n */\nexport type NodePath = string;\n\nexport interface SemanticNode {\n readonly path: NodePath;\n readonly tag: string;\n\n /** Structural alias, e.g. `#a0`. Absent when the node carried no id. */\n readonly alias?: string;\n\n readonly role?: string;\n readonly name?: string;\n\n /**\n * Accessible description, resolved. See `RawAria.description` for why this is\n * a field of its own rather than an attribute.\n */\n readonly description?: string;\n\n readonly state?: Readonly<Record<string, string | boolean | number>>;\n\n /** Semantic attributes surviving normalization. `class` is never among them. */\n readonly attributes: Readonly<Record<string, string>>;\n\n /** Winning values for allowlisted properties, canonicalized. */\n readonly style: Readonly<Record<string, string>>;\n\n /**\n * Custom properties this node's winning declarations resolved through, as\n * name → resolved value.\n *\n * Recording the name alongside the value is what lets a design-token change\n * surface as one root with counted collateral instead of hundreds of unrelated\n * colour diffs (spec §5, `token` band).\n */\n readonly tokens?: Readonly<Record<string, string>>;\n\n /**\n * Which token each styled property resolved through, including by inheritance.\n *\n * Outside the hash, like `styleProvenance` and for the same reason: swapping a\n * literal for a token of the same value renames a source without changing a\n * render. It exists so the differ can tell *which* property a token edit\n * explains — `tokens` alone says a node uses tokens, not which of its values\n * one drove.\n */\n readonly styleTokens?: Readonly<Record<string, string>>;\n\n /** Present only under a profile with layout. Absent, never zeroed (ADR-0002). */\n readonly rect?: Rect;\n\n /** Text content, or its digest when policy declares the region volatile. */\n readonly text?: string;\n\n readonly provenance?: Provenance;\n\n /**\n * How the framework holds this node's component, when a framework adapter\n * supplied it. See {@link Wiring}.\n *\n * On the node rather than on `Provenance`, though both come from the same\n * adapter and the same fiber, because they answer to different readers.\n * Provenance exists so a diff arrives carrying a name; wiring is a *dimension\n * of the subject*, hashed into a band of its own, and a band's inputs belong\n * beside the other bands' inputs — next to `style` and `attributes`, which is\n * where `shapeOf` goes looking.\n */\n readonly wiring?: Wiring;\n\n /**\n * What that component was holding. **Outside every hash**, always.\n *\n * The field beside it is the contrast worth reading: `wiring` is a band and\n * this is evidence. `Wiring` refuses state values because a hook's value is\n * exactly what may legitimately differ between two readings of one page, and\n * that refusal is right and is not softened here — a `holding` reaches the\n * snapshot and reaches no digest, so a subject whose clock ticked has the same\n * `renderHash` it had before this field existed.\n *\n * It rides outside on the same terms as `styleProvenance`, `styleTokens` and\n * `ignoredBy`, by the same mechanism: `structureOf`, `styleOf` and `shapeOf`\n * project the fields they hash **by name**, so a field none of them names\n * cannot reach an identity. `holding.hash.test.ts` asserts that rather than\n * trusting it, because the cost of being wrong is every baseline in a\n * repository invalidating the first time a component held a timestamp.\n */\n readonly holding?: Holding;\n\n /**\n * `true` on the root of a subtree rendered through a portal.\n *\n * Recorded in the structure hash so that \"the dialog moved from inline to\n * portalled\" is a change rather than a coincidence of identical content, and\n * so the docket can say *where* something rendered, not only that it exists.\n */\n readonly portalled?: boolean;\n\n /**\n * Ignore rules this node was found under. Outside every hash, like its origin\n * on `RawNode`. See {@link SemanticSnapshot.ignoreSites}.\n */\n readonly ignoredBy?: readonly string[];\n\n /** What was not read about this node, carried from `RawNode.unread`. Outside every hash. */\n readonly unread?: string;\n\n readonly children: readonly SemanticNode[];\n}\n\n/**\n * A subtree the operator excluded, resolved to a path and a box (spec 0024).\n *\n * Lives in `format` rather than beside the rules that consume it, because it is\n * part of what a snapshot *is*: the collector produced it, it travels with the\n * document over every wire this project has, and `core/judge` is only its first\n * reader. A type the wire format needs cannot live in the policy layer.\n */\nexport interface IgnoreSite {\n /** Root of the excluded subtree, in this snapshot's own path space. */\n readonly path: NodePath;\n\n /** Which rule put it here, matched exactly against `IgnoreRule.id`. */\n readonly rule: string;\n\n /**\n * The box it occupied, for the raster tier.\n *\n * Absent under a profile with no layout — which is ADR-0002 rather than a\n * failure: a profile that cannot measure a box cannot decide pixels either, so\n * there is nothing for the box to subtract from.\n */\n readonly rect?: Rect;\n}\n\n/**\n * A component's own content, hashed per band (ADR-0018).\n *\n * In `format` rather than beside `hashComponents`, which computes it, because a\n * baseline carries these: a stored image plus the component hashes of the\n * document that painted it is what lets a later run tell the component that\n * *caused* a change from the components the change merely moved. That makes this\n * part of the wire format every store and every transport has to preserve, and a\n * type the wire needs cannot live in the layer that derives it.\n */\nexport interface ComponentHash {\n readonly component: string;\n\n /** Boundaries of this component in the subject, counted in document order. */\n readonly instances: number;\n\n /**\n * Tree shape: tags, structural aliases, portalling, allowlisted attributes,\n * and which child boundaries sit where. The `geometry` band's structural half.\n *\n * **Split out of a single `structure` digest on 2026-08-06.** It used to carry\n * the accessible semantics and the text too, and the fusion was invisible\n * until something needed to ask *which band moved* — at which point a baseline\n * could say \"this component changed\" and never say whether a heading was\n * renamed, a paragraph reworded, or a node inserted. Those are three different\n * bands, three different readers, and one of them is the band a route-level\n * test exists to ignore.\n */\n readonly structure: Digest;\n\n /**\n * Role, accessible name, and ARIA state. The `a11y` band.\n *\n * Its own digest because it is the band that must never be absorbed. Every\n * sensitivity level asserts on it, including the ones that ignore everything\n * else — and a level cannot assert on a band that is fused into another one.\n */\n readonly semantics: Digest;\n\n /** Text runs, in document order. The `content` band. */\n readonly text: Digest;\n\n readonly style: Digest;\n\n /**\n * Absent under a profile without layout — absent, never empty (ADR-0002).\n *\n * An empty geometry digest would compare equal between a run that observed no\n * movement and a run that could not observe movement at all, which is the\n * false `unchanged` this system must never produce.\n */\n readonly geometry?: Digest;\n\n /**\n * The box each instance occupies, in document order. Never hashed.\n *\n * A digest can say a component is not what it was and can never say by how\n * much. That is the gap a reviewer falls into: `Button — geometry, token` is\n * true of a control that lost a shadow and of one that grew eight pixels and\n * pushed the page down, and only the second explains why the subject is a\n * different height. Four numbers per instance close it.\n *\n * Absent for a baseline written before this existed and for a profile with no\n * layout — the same absence, and both mean *nobody measured*, never *it did\n * not move*. A `null` entry is a boundary the browser laid out nothing for,\n * kept in place so the list stays parallel to the instances it counts.\n */\n readonly boxes?: readonly (Rect | null)[];\n\n /**\n * Every distinct value the component's own nodes declared, per property, in\n * code-unit order; a custom property appears under its `--name` with the\n * values it resolved to. Never hashed.\n *\n * `boxes` answers *by how much*; this answers *which declaration*. `style`\n * moved says a `Button` is not styled as it was, and a reviewer holding only\n * that opens the file and diffs by eye. With both revisions' values the run\n * says `padding-left 8px → 14px`, which is the line somebody edited.\n *\n * Absent for a baseline written before this existed. That means *nobody\n * recorded it*, and the comparison says nothing about properties rather than\n * reporting every one of them as new.\n */\n readonly values?: Readonly<Record<string, readonly string[]>>;\n}\n\nexport interface StyleProvenanceEntry {\n readonly path: NodePath;\n readonly property: string;\n readonly sheet: string;\n /** Generated segments replaced by placeholders, so it can name a root safely. */\n readonly selector: string;\n readonly tokenName?: string;\n readonly source?: { readonly file: string; readonly line: number };\n}\n"]}
@@ -1,6 +1,7 @@
1
1
  import { type Digest } from './hash.js';
2
2
  import { type Tier } from './tier.js';
3
3
  import type { Intervention, ScreenshotOptions, SettleTarget } from './intervention.js';
4
+ import type { StepClock } from './steps.js';
4
5
  /**
5
6
  * Interventions as separate, nameable tricks — not a fixed set of switches.
6
7
  *
@@ -20,8 +21,8 @@ import type { Intervention, ScreenshotOptions, SettleTarget } from './interventi
20
21
  *
21
22
  * A recipe can be filtered by tier, so the structure-and-style rung waits for
22
23
  * nothing; composed from another project's tricks alongside these; digested, so
23
- * a baseline made under one recipe is `incomparable` with a run made under
24
- * another rather than `changed`; and checked for tricks that fight each other.
24
+ * a run under another recipe than its baseline names the recipe as the cause;
25
+ * and checked for tricks that fight each other.
25
26
  *
26
27
  * ## Where the damage lands
27
28
  *
@@ -52,6 +53,7 @@ import type { Intervention, ScreenshotOptions, SettleTarget } from './interventi
52
53
  * digest, and the vocabulary belongs beside the key it is part of.
53
54
  */
54
55
  export type { Intervention, ScreenshotOptions, SettleTarget, Trick } from './intervention.js';
56
+ export { waitForFonts, waitForImages } from './waits.js';
55
57
  /**
56
58
  * Hand animations to the browser rather than emulating the hold in CSS.
57
59
  *
@@ -111,8 +113,6 @@ export declare const hideScrollbars: Intervention;
111
113
  * `alt=""` image would hide real regressions under a green run.
112
114
  */
113
115
  export declare const hidePresentationalImages: Intervention;
114
- export declare const waitForFonts: Intervention;
115
- export declare const waitForImages: Intervention;
116
116
  export declare const INTERVENTIONS: readonly Intervention[];
117
117
  export type Recipe = readonly Intervention[];
118
118
  /**
@@ -124,6 +124,13 @@ export type Recipe = readonly Intervention[];
124
124
  * rung the tier ladder exists to make cheap.
125
125
  */
126
126
  export declare const SEMANTIC_RECIPE: Recipe;
127
+ /**
128
+ * What a document is painted under, and of {@link UNTOUCHED} only `no-focus`: a
129
+ * collected `autofocus` takes focus again. The other three have nothing but a
130
+ * carried script to act on: the printed date and the animation's place are in
131
+ * the document already, its `@media` rules were resolved when it was read, and
132
+ * nothing moves the pointer over it.
133
+ */
127
134
  export declare const LAYOUT_RECIPE: Recipe;
128
135
  export declare const RASTER_RECIPE: Recipe;
129
136
  /**
@@ -153,6 +160,12 @@ export declare const RASTER_RECIPE: Recipe;
153
160
  * the trade {@link SEMANTIC_RECIPE} refuses.
154
161
  */
155
162
  export declare const COLLECT_RECIPE: Recipe;
163
+ /**
164
+ * The ids of {@link COLLECT_RECIPE}, as an absent `stabilize` means them. A list
165
+ * replaces the recipe, so one trick is turned off with this list without it:
166
+ * `stabilize: DEFAULT_STABILIZE.filter((id) => id !== 'freeze-clock')`.
167
+ */
168
+ export declare const DEFAULT_STABILIZE: readonly string[];
156
169
  /**
157
170
  * Resolve a trick by the name a caller wrote down.
158
171
  *
@@ -174,6 +187,17 @@ export declare function interventionById(id: string): Intervention | undefined;
174
187
  * of the message.
175
188
  */
176
189
  export declare function recipeOf(ids: readonly string[]): Recipe;
190
+ /**
191
+ * The recipe a host that drives the browser applies, checked before it opens one:
192
+ * `stabilize` by id, or {@link COLLECT_RECIPE} when it is absent, with its clock
193
+ * fixed at `now`, which travels in the clock's id and lands in the digest. Throws
194
+ * on an unknown id, an instant with no zone, or a `now` with no clock to fix, so
195
+ * the operator hears about it before a page is loaded, not at the first subject.
196
+ */
197
+ export declare function recipeToDrive(options: {
198
+ readonly stabilize?: readonly string[];
199
+ readonly now?: string;
200
+ }): Recipe;
177
201
  /** Only the tricks a tier can observe the effect of. */
178
202
  export declare function forTier(recipe: Recipe, tier: Tier): Recipe;
179
203
  /**
@@ -190,13 +214,18 @@ export declare function conflicts(recipe: Recipe): readonly {
190
214
  }[];
191
215
  export declare function recipeCss(recipe: Recipe): string;
192
216
  export declare function recipeScreenshot(recipe: Recipe): ScreenshotOptions;
193
- export declare function settleRecipe(recipe: Recipe, target: SettleTarget): Promise<void>;
217
+ /**
218
+ * Every wait the recipe holds, in order. Each is one step on `steps`, named by
219
+ * the intervention's id, so a run can say which wait a slow subject spent its
220
+ * time in.
221
+ */
222
+ export declare function settleRecipe(recipe: Recipe, target: SettleTarget, steps?: StepClock): Promise<void>;
194
223
  /**
195
224
  * The identity of a recipe.
196
225
  *
197
226
  * Sorted, so the order tricks were composed in does not change the identity —
198
227
  * only which ones are present. Folded into `RenderIdentity`, which is what makes
199
- * a cross-recipe comparison report `incomparable` instead of inventing a
228
+ * a cross-recipe comparison name the recipe as its cause instead of inventing a
200
229
  * component to blame.
201
230
  */
202
231
  export declare function recipeDigest(recipe: Recipe): Digest;