@variance-authority/core 0.1.1 → 0.3.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.
Files changed (96) hide show
  1. package/CHANGELOG.md +245 -10
  2. package/README.md +279 -110
  3. package/dist/attribute/call-site.d.ts +20 -2
  4. package/dist/attribute/call-site.js +17 -20
  5. package/dist/attribute/call-site.js.map +1 -1
  6. package/dist/attribute/closure.d.ts +105 -0
  7. package/dist/attribute/closure.js +180 -0
  8. package/dist/attribute/closure.js.map +1 -0
  9. package/dist/attribute/composition.d.ts +5 -3
  10. package/dist/attribute/composition.js +67 -11
  11. package/dist/attribute/composition.js.map +1 -1
  12. package/dist/attribute/divergence.js +7 -1
  13. package/dist/attribute/divergence.js.map +1 -1
  14. package/dist/attribute/index.d.ts +7 -1
  15. package/dist/attribute/index.js +4 -1
  16. package/dist/attribute/index.js.map +1 -1
  17. package/dist/attribute/landmark.d.ts +97 -0
  18. package/dist/attribute/landmark.js +144 -0
  19. package/dist/attribute/landmark.js.map +1 -0
  20. package/dist/attribute/lexicon.d.ts +112 -0
  21. package/dist/attribute/lexicon.js +210 -0
  22. package/dist/attribute/lexicon.js.map +1 -0
  23. package/dist/attribute/source.d.ts +19 -2
  24. package/dist/attribute/source.js +12 -0
  25. package/dist/attribute/source.js.map +1 -1
  26. package/dist/attribute/stack.d.ts +62 -1
  27. package/dist/attribute/stack.js +116 -5
  28. package/dist/attribute/stack.js.map +1 -1
  29. package/dist/compare/derive.d.ts +17 -0
  30. package/dist/compare/derive.js.map +1 -1
  31. package/dist/compare/diff/index.d.ts +25 -1
  32. package/dist/compare/diff/index.js +8 -5
  33. package/dist/compare/diff/index.js.map +1 -1
  34. package/dist/compare/explain.d.ts +1 -1
  35. package/dist/compare/explain.js +15 -8
  36. package/dist/compare/explain.js.map +1 -1
  37. package/dist/compare/index.d.ts +2 -2
  38. package/dist/compare/index.js +1 -1
  39. package/dist/compare/index.js.map +1 -1
  40. package/dist/compare/observability.d.ts +25 -1
  41. package/dist/compare/observability.js +54 -0
  42. package/dist/compare/observability.js.map +1 -1
  43. package/dist/compare/parting.d.ts +24 -2
  44. package/dist/compare/parting.js +14 -9
  45. package/dist/compare/parting.js.map +1 -1
  46. package/dist/compare/slice.d.ts +15 -2
  47. package/dist/compare/slice.js +6 -2
  48. package/dist/compare/slice.js.map +1 -1
  49. package/dist/format/capture.d.ts +15 -0
  50. package/dist/format/capture.js +30 -1
  51. package/dist/format/capture.js.map +1 -1
  52. package/dist/format/document.d.ts +65 -5
  53. package/dist/format/document.js +34 -0
  54. package/dist/format/document.js.map +1 -1
  55. package/dist/format/hash.d.ts +26 -0
  56. package/dist/format/hash.js +41 -0
  57. package/dist/format/hash.js.map +1 -1
  58. package/dist/format/index.d.ts +4 -3
  59. package/dist/format/index.js +3 -2
  60. package/dist/format/index.js.map +1 -1
  61. package/dist/format/sha256.d.ts +10 -2
  62. package/dist/format/sha256.js +42 -9
  63. package/dist/format/sha256.js.map +1 -1
  64. package/dist/format/stabilize.d.ts +15 -0
  65. package/dist/format/stabilize.js +22 -1
  66. package/dist/format/stabilize.js.map +1 -1
  67. package/dist/index.d.ts +3 -11
  68. package/dist/index.js +3 -11
  69. package/dist/index.js.map +1 -1
  70. package/dist/judge/inspect.js +57 -4
  71. package/dist/judge/inspect.js.map +1 -1
  72. package/dist/relate/before.d.ts +137 -0
  73. package/dist/relate/before.js +155 -0
  74. package/dist/relate/before.js.map +1 -0
  75. package/dist/relate/graph.d.ts +53 -16
  76. package/dist/relate/graph.js +33 -15
  77. package/dist/relate/graph.js.map +1 -1
  78. package/dist/relate/index.d.ts +3 -2
  79. package/dist/relate/index.js +2 -1
  80. package/dist/relate/index.js.map +1 -1
  81. package/dist/relate/merkle.d.ts +14 -5
  82. package/dist/relate/merkle.js +14 -6
  83. package/dist/relate/merkle.js.map +1 -1
  84. package/dist/relate/reach.d.ts +19 -5
  85. package/dist/relate/reach.js +10 -8
  86. package/dist/relate/reach.js.map +1 -1
  87. package/dist/relate/records.d.ts +95 -4
  88. package/dist/relate/records.js +93 -13
  89. package/dist/relate/records.js.map +1 -1
  90. package/dist/segment/index.d.ts +123 -0
  91. package/dist/segment/index.js +265 -0
  92. package/dist/segment/index.js.map +1 -0
  93. package/dist/share/index.d.ts +148 -0
  94. package/dist/share/index.js +190 -0
  95. package/dist/share/index.js.map +1 -0
  96. package/package.json +10 -2
@@ -110,6 +110,15 @@ export interface RenderResource {
110
110
  /** Response bytes, base64. */
111
111
  readonly bytes: string;
112
112
  readonly digest: Digest;
113
+ /**
114
+ * The status a renderer answers this URL with. Absent means 200.
115
+ *
116
+ * A subject is allowed to reference something that is not there: a fallback
117
+ * test does it on purpose, and the browser it was acquired in painted the
118
+ * broken state rather than the image. Recording the failure as a failure is
119
+ * what lets a renderer reproduce that state without asking the network.
120
+ */
121
+ readonly status?: number;
113
122
  }
114
123
  export interface RenderFrame {
115
124
  /** Attributes on `<html>`. `class="dark"` here decides half a design system. */
@@ -213,15 +222,29 @@ export declare function identityDigest(identity: RenderIdentity): Digest;
213
222
  * `bytes` is base64 rather than a `Buffer` so the whole value survives the same
214
223
  * hops the document does. A raster that could only exist in Node would make the
215
224
  * remote route a special case in every function that touches one.
225
+ *
226
+ * The image is optional, and the three fields that describe it move together.
227
+ * A subject that occupies no pixels — a wrapper whose only child went to a
228
+ * portal, a `describeConformance` mount with no children — still has a
229
+ * document, rules, components and an accessibility tree, all of which compare.
230
+ * Refusing to photograph it is right; refusing the subject reported a quarter
231
+ * of Material UI's unit tier as unobserved over an axis nothing was asking
232
+ * about. Absent here means *this subject has no pixels to observe*, which is a
233
+ * measurement; it never means *the image was lost*.
216
234
  */
217
235
  export interface Raster {
218
236
  readonly documentDigest: Digest;
219
237
  readonly identity: RenderIdentity;
220
- /** Device pixels. `width / viewport.width` is the scale, and is asserted. */
221
- readonly width: number;
222
- readonly height: number;
223
- /** PNG, base64. */
224
- readonly bytes: string;
238
+ /**
239
+ * Device pixels. `width / viewport.width` is the scale, and is asserted.
240
+ *
241
+ * Absent exactly when {@link bytes} is, and {@link pictured} is the one place
242
+ * that reads the three together.
243
+ */
244
+ readonly width?: number;
245
+ readonly height?: number;
246
+ /** PNG, base64. Absent when the subject occupies no pixels. */
247
+ readonly bytes?: string;
225
248
  /** Fonts the document declared that the renderer did not have. */
226
249
  readonly missingFonts: readonly string[];
227
250
  /**
@@ -276,4 +299,41 @@ export interface Raster {
276
299
  */
277
300
  readonly findingMarks?: readonly string[];
278
301
  }
302
+ /**
303
+ * A raster whose image is in hand.
304
+ *
305
+ * Every pixel comparison in the codebase needs all three of `bytes`, `width`
306
+ * and `height`, and they are absent together or present together. Narrowing
307
+ * them one at a time is how a caller ends up reading the bytes of a subject
308
+ * that was never photographed, so the narrowing lives here and is done once.
309
+ */
310
+ export type Pictured = Raster & {
311
+ readonly width: number;
312
+ readonly height: number;
313
+ readonly bytes: string;
314
+ };
315
+ /** Whether this record carries an image, narrowing all three fields together. */
316
+ export declare function pictured(raster: Raster): raster is Pictured;
317
+ /**
318
+ * Whether the subject occupied any pixels, answered without the image.
319
+ *
320
+ * The question {@link pictured} cannot be asked of a sidecar. Bytes live in a
321
+ * separate file, row or object, so a record read without them carries the width
322
+ * and the height and nothing else — and those *are* the store's record of
323
+ * whether there was ever anything to photograph, which is why `sidecarFrom`
324
+ * refuses a sidecar carrying one of them.
325
+ *
326
+ * So: this for a record, {@link pictured} for an image in hand. A caller that
327
+ * reached for `pictured` here would read every sidecar in the store as a subject
328
+ * with no pixels, because none of them have bytes.
329
+ */
330
+ export declare function occupiesPixels(raster: Omit<Raster, 'bytes'>): boolean;
331
+ /**
332
+ * The image, or a refusal naming the subject that has none.
333
+ *
334
+ * For the paths where a missing image is a programming error rather than a
335
+ * measurement — a diff, a PNG encode — so they read as assertions instead of
336
+ * as a non-null assertion nobody can audit.
337
+ */
338
+ export declare function picture(raster: Raster, subject: string): Pictured;
279
339
  //# sourceMappingURL=document.d.ts.map
@@ -62,4 +62,38 @@ export function identityDigest(identity) {
62
62
  fonts: [...identity.fonts],
63
63
  });
64
64
  }
65
+ /** Whether this record carries an image, narrowing all three fields together. */
66
+ export function pictured(raster) {
67
+ return raster.bytes !== undefined && raster.width !== undefined && raster.height !== undefined;
68
+ }
69
+ /**
70
+ * Whether the subject occupied any pixels, answered without the image.
71
+ *
72
+ * The question {@link pictured} cannot be asked of a sidecar. Bytes live in a
73
+ * separate file, row or object, so a record read without them carries the width
74
+ * and the height and nothing else — and those *are* the store's record of
75
+ * whether there was ever anything to photograph, which is why `sidecarFrom`
76
+ * refuses a sidecar carrying one of them.
77
+ *
78
+ * So: this for a record, {@link pictured} for an image in hand. A caller that
79
+ * reached for `pictured` here would read every sidecar in the store as a subject
80
+ * with no pixels, because none of them have bytes.
81
+ */
82
+ export function occupiesPixels(raster) {
83
+ return raster.width !== undefined && raster.height !== undefined;
84
+ }
85
+ /**
86
+ * The image, or a refusal naming the subject that has none.
87
+ *
88
+ * For the paths where a missing image is a programming error rather than a
89
+ * measurement — a diff, a PNG encode — so they read as assertions instead of
90
+ * as a non-null assertion nobody can audit.
91
+ */
92
+ export function picture(raster, subject) {
93
+ if (!pictured(raster)) {
94
+ throw new Error(`\`${subject}\` occupies no pixels, and this path needs an image; ` +
95
+ 'a subject with no raster compares on its document alone');
96
+ }
97
+ return raster;
98
+ }
65
99
  //# sourceMappingURL=document.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"document.js","sourceRoot":"","sources":["../../src/format/document.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,WAAW,EAAe,MAAM,WAAW,CAAC;AAoJrD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,cAAc,CAAC,QAAwB;IACrD,OAAO,WAAW,CAAC;QACjB,eAAe,EAAE,QAAQ,CAAC,eAAe;QACzC,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,KAAK,EAAE;YACL,IAAI,EAAE,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE;YAChC,IAAI,EAAE,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,EAAE;YAChC,SAAS,EAAE,QAAQ,CAAC,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;gBACpD,GAAG,EAAE,OAAO,CAAC,GAAG;gBAChB,UAAU,EAAE,EAAE,GAAG,OAAO,CAAC,UAAU,EAAE;aACtC,CAAC,CAAC;YACH,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,cAAc,KAAK,SAAS;gBAC7C,CAAC,CAAC,EAAE,cAAc,EAAE,QAAQ,CAAC,KAAK,CAAC,cAAc,EAAE;gBACnD,CAAC,CAAC,EAAE,CAAC;SACR;QACD,GAAG,EAAE,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC;QACtB,QAAQ,EAAE,EAAE,GAAG,QAAQ,CAAC,QAAQ,EAAE;QAClC,SAAS,EAAE,EAAE,GAAG,QAAQ,CAAC,SAAS,EAAE;QACpC,KAAK,EAAE,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;QAC1B,GAAG,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE,GAAG,QAAQ,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC9D,GAAG,CAAC,QAAQ,CAAC,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACxE,GAAG,CAAC,QAAQ,CAAC,SAAS,KAAK,SAAS;YAClC,CAAC,CAAC;gBACE,SAAS,EAAE,MAAM,CAAC,WAAW,CAC3B,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,EAAE,QAAQ,CAAC,EAAE,EAAE,CAAC;oBAC1D,GAAG;oBACH,EAAE,GAAG,QAAQ,EAAE;iBAChB,CAAC,CACH;aACF;YACH,CAAC,CAAC,EAAE,CAAC;KACR,CAAC,CAAC;AACL,CAAC;AAyDD,MAAM,UAAU,cAAc,CAAC,QAAwB;IACrD,OAAO,WAAW,CAAC;QACjB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,QAAQ,EAAE,QAAQ,CAAC,QAAQ;QAC3B,GAAG,CAAC,QAAQ,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1F,GAAG,CAAC,QAAQ,CAAC,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,aAAa,EAAE,QAAQ,CAAC,aAAa,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1F,iBAAiB,EAAE,QAAQ,CAAC,iBAAiB;QAC7C,KAAK,EAAE,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC;KAC3B,CAAC,CAAC;AACL,CAAC","sourcesContent":["import type { Diagnostic, SubjectRef } from './capture.js';\nimport type { Viewport } from './environment.js';\nimport { digestValue, type Digest } from './hash.js';\nimport type { ComponentHash } from './snapshot.js';\n\n/**\n * The render document: what gets *sent* somewhere to become an image.\n *\n * This is the acquisition phase's output and the only thing the rendering side\n * ever sees. It is plain serializable data for the same reason `RawCapture` is\n * (ADR-0006): the renderer may be a page in this process, a browser on this\n * machine, or a pinned container two networks away, and no code upstream of it\n * may assume which.\n *\n * Two routes produce one of these, and the point of the type is that they are\n * interchangeable:\n *\n * 1. **jsdom → document → render elsewhere.** jsdom cannot rasterize. It can\n * describe exactly what to rasterize, which is the sub-renderer split ADR-0002\n * asks for: the cheap tier decides almost everything, and the residue is\n * handed to something that owns a GPU.\n * 2. **playwright → document → render on a server.** The same payload, acquired\n * from a real engine. Used when the *deciding* machine and the *pinned*\n * machine are different, which is the case every CI setup actually has.\n *\n * A browser may instead capture a `Raster` in place. That is the sibling branch\n * of `CaptureMaterial`, not a third route that somehow produces this type.\n *\n * ## Why this is small\n *\n * `css` holds the *applicable* rules only — the pruning of ADR-0003, which on\n * `@variance-authority/dom`'s accretion fixture takes 1010 rules to 1. That\n * number is a cost claim as much as a\n * correctness one: shipping a document over a network hop is only sensible if the\n * document is not the entire design system plus Storybook's chrome.\n */\nexport interface RenderDocument {\n readonly documentVersion: 1;\n readonly subject: SubjectRef;\n\n /**\n * The subject subtree, serialized.\n *\n * Serialized rather than referenced by URL: a URL makes the renderer\n * responsible for reproducing the application's state, which it cannot do and\n * should not try. What is sent is the markup that existed at the moment of\n * capture, which is the only definition of \"this render\" that survives a hop.\n */\n readonly html: string;\n\n /**\n * The ancestor context the subject was rendered inside.\n *\n * Not decoration. Pruning keeps rules like `html.dark .card` and\n * `.app .list > li`, whose left-hand side lives *above* the subject — render\n * the subtree bare and those rules match nothing, so the image is missing\n * exactly the styling the collector went to the trouble of proving applies.\n *\n * Reproduced as empty open tags rather than by re-rendering the application:\n * what a selector needs is a chain of elements with the right tags, ids,\n * classes, and attributes, and nothing above the subject contributes anything\n * else to it that {@link RenderDocument.inherited} does not already carry.\n */\n readonly frame: RenderFrame;\n\n /**\n * Applicable stylesheet text, in cascade order.\n *\n * Order is load-bearing and is the caller's to preserve — the cascade breaks\n * specificity ties by document order, so a set that arrives shuffled paints\n * differently while hashing the same if this were a set rather than a list.\n */\n readonly css: readonly string[];\n\n readonly viewport: Viewport;\n\n /**\n * Inherited values in force at the subject root, applied to its wrapper.\n *\n * The same requirement as `RawCapture.inheritedSeed` and for the same reason:\n * pruning drops rules on ancestors outside the subtree, and a font-size\n * inherited from `<html>` is not optional decoration — dropping it changes\n * every metric in the image.\n */\n readonly inherited: Readonly<Record<string, string>>;\n\n /**\n * Fonts the acquiring side observed as loaded.\n *\n * Carried, not resolved. A renderer that lacks one of these will substitute\n * and produce different metrics, and that has to be *detectable* rather than\n * silently absorbed — see {@link RenderIdentity}.\n */\n readonly fonts: readonly string[];\n\n /** External references, url → content hash. Present so a swap is visible. */\n readonly assets?: Readonly<Record<string, string>>;\n\n /**\n * Base against which relative resource references were resolved.\n *\n * Preserves relative-reference resolution for either document kind. Resource\n * closure is denoted by `resources`, not by the presence of this field.\n */\n readonly baseUrl?: string;\n\n /**\n * Immutable responses needed to paint this document, keyed by absolute URL.\n *\n * Presence — including an empty object — means the document is\n * resource-closed: a renderer must not consult the network for a missing URL.\n * Absence preserves the older network-capable document contract and must not\n * be described as portable to another machine.\n */\n readonly resources?: Readonly<Record<string, RenderResource>>;\n\n readonly diagnostics: readonly Diagnostic[];\n}\n\nexport interface RenderResource {\n readonly contentType: string;\n /** Response bytes, base64. */\n readonly bytes: string;\n readonly digest: Digest;\n}\n\nexport interface RenderFrame {\n /** Attributes on `<html>`. `class=\"dark\"` here decides half a design system. */\n readonly html: Readonly<Record<string, string>>;\n readonly body: Readonly<Record<string, string>>;\n /** Elements between `<body>` and the subject root, outermost first. */\n readonly ancestors: readonly FrameElement[];\n\n /**\n * Used width of the subject's parent box, in CSS pixels, when layout was\n * observable.\n *\n * A subject declaring `width: 100%` is a different size in a 1024px page and\n * inside a 600px panel, and nothing else in this document says which it was.\n * Absent under a profile with no layout engine, in which case the render falls\n * back to the viewport and the difference, if any, is real rather than hidden.\n */\n readonly containerWidth?: number;\n}\n\nexport interface FrameElement {\n readonly tag: string;\n readonly attributes: Readonly<Record<string, string>>;\n}\n\n/**\n * Content address of a document: *what is to be painted*.\n *\n * This is the deferral lever. Rendering is the expensive phase, and a document\n * whose digest has been rendered before under the same identity has an image\n * already — so the cheapest render is the one that is skipped. Nothing about the\n * branch, the commit, or the file path enters this (Principle 4), so moving a\n * story between files does not cost a re-render.\n *\n * Deliberately covers `viewport` in full, `deviceScaleFactor` included: unlike\n * the semantic key (ADR-0010), a raster genuinely differs at 2x and the whole\n * purpose of this digest is to address the raster.\n *\n * **Limit, stated rather than discovered.** Declaration values arrive serialized\n * by the acquiring engine's CSSOM — jsdom writes `rgb(18, 52, 86)` where the\n * author wrote `#123456` — and this phase deliberately does not canonicalize\n * them, because a collector that normalizes is a second ruleset versioned by\n * nothing (ADR-0001). So two engines can address the same page differently. That\n * makes a render cache *per acquiring engine*, which is a missed cache hit and\n * never a wrong image.\n */\nexport function documentDigest(document: RenderDocument): Digest {\n return digestValue({\n documentVersion: document.documentVersion,\n html: document.html,\n frame: {\n html: { ...document.frame.html },\n body: { ...document.frame.body },\n ancestors: document.frame.ancestors.map((element) => ({\n tag: element.tag,\n attributes: { ...element.attributes },\n })),\n ...(document.frame.containerWidth !== undefined\n ? { containerWidth: document.frame.containerWidth }\n : {}),\n },\n css: [...document.css],\n viewport: { ...document.viewport },\n inherited: { ...document.inherited },\n fonts: [...document.fonts],\n ...(document.assets ? { assets: { ...document.assets } } : {}),\n ...(document.baseUrl !== undefined ? { baseUrl: document.baseUrl } : {}),\n ...(document.resources !== undefined\n ? {\n resources: Object.fromEntries(\n Object.entries(document.resources).map(([url, resource]) => [\n url,\n { ...resource },\n ]),\n ),\n }\n : {}),\n });\n}\n\n/**\n * Who painted it.\n *\n * The durable half of this system rests on one requirement — *images compared\n * against each other must come from the same machine* — and a requirement that\n * is merely stated gets violated by a CI runner upgrade nobody announced. This\n * makes it a value, so it can be compared rather than assumed.\n *\n * Every field is something that has been observed to move pixels without moving\n * markup. None of them is a guess about what *might* matter.\n */\nexport interface RenderIdentity {\n /** `playwright-chromium@1.49.0`, `remote:render.internal`, … */\n readonly renderer: string;\n /** Engine build. A Chromium bump repaints text; that is not a regression. */\n readonly engine: string;\n /** OS and architecture. Font rasterization differs across both. */\n readonly platform: string;\n readonly deviceScaleFactor: number;\n /**\n * Fonts the *renderer* actually has.\n *\n * Compared against {@link RenderDocument.fonts} at render time, because a\n * substituted font is the single most common way two machines disagree, and\n * it is invisible in every artifact except the image itself.\n */\n readonly fonts: readonly string[];\n\n /**\n * Digest of what the renderer did *to* the page so it could be observed.\n *\n * Pausing animations, hiding a caret, waiting for fonts — each is necessary\n * and each widens the gap between what was measured and what a person sees.\n * The gap itself is acceptable; a *difference* in it between two runs is not,\n * because the images then differ for a reason that is not the code and the\n * report blames whichever component sits under the pixels. Folding it in here\n * makes that comparison `incomparable` instead, by the same mechanism that\n * refuses a cross-machine one.\n *\n * Optional only because a renderer may predate the field. Absent means the\n * renderer did not record what it did, which is emphatically not the same as\n * \"it did nothing\" — and two such renderers will compare, which is the hazard\n * that argues for every renderer setting it.\n */\n readonly stabilization?: Digest;\n\n /**\n * Digest of pixel-affecting browser launch and raster settings.\n *\n * Separate from stabilization: one describes how the browser paints, the\n * other what was done to the page before it was read.\n */\n readonly rasterization?: Digest;\n}\n\nexport function identityDigest(identity: RenderIdentity): Digest {\n return digestValue({\n renderer: identity.renderer,\n engine: identity.engine,\n platform: identity.platform,\n ...(identity.stabilization !== undefined ? { stabilization: identity.stabilization } : {}),\n ...(identity.rasterization !== undefined ? { rasterization: identity.rasterization } : {}),\n deviceScaleFactor: identity.deviceScaleFactor,\n fonts: [...identity.fonts],\n });\n}\n\n/**\n * A rendered image and the conditions it was produced under.\n *\n * `bytes` is base64 rather than a `Buffer` so the whole value survives the same\n * hops the document does. A raster that could only exist in Node would make the\n * remote route a special case in every function that touches one.\n */\nexport interface Raster {\n readonly documentDigest: Digest;\n readonly identity: RenderIdentity;\n /** Device pixels. `width / viewport.width` is the scale, and is asserted. */\n readonly width: number;\n readonly height: number;\n /** PNG, base64. */\n readonly bytes: string;\n /** Fonts the document declared that the renderer did not have. */\n readonly missingFonts: readonly string[];\n\n /**\n * The browser-computed accessibility tree from the acquisition that produced\n * this candidate. It is independent evidence: pixel equality cannot settle it.\n * Absent means the acquisition host did not observe this boundary.\n */\n readonly accessibility?: import('./accessibility.js').AccessibilitySnapshot;\n\n /**\n * What the document that painted this said about its own components (ADR-0018).\n *\n * The field that makes a baseline self-describing, and the reason it is here\n * rather than only in a history store. Separating the component that *caused* a\n * change from the components the change merely moved needs both revisions, and\n * a stored baseline is an image: without this, a run has one document and ranks\n * by area — which\n * [journal 0013](../../../../docs/context/journal/0013-observability.md)\n * measured as backwards.\n *\n * Text, and small: one line per component boundary. It rides in the sidecar\n * beside a PNG that dominates it, so a baseline carries its own explanation\n * wherever it is copied, and a store or a transport that drops it degrades to\n * ranking by area rather than to a wrong answer.\n *\n * Optional because a baseline written before this existed does not have it, and\n * because a profile or a collector that supplies no snapshot cannot produce it.\n * Absent means *unknown*, never *no components*.\n */\n readonly components?: readonly ComponentHash[];\n\n /**\n * What inspection found in the document that painted this, as marks.\n *\n * The field that lets a later run say *this defect was already here* instead of\n * printing every defect it finds with no indication of when it arrived. A\n * finding is produced from one render with no baseline consulted — that is the\n * point of having one — and the consequence is that the list is identical on\n * the run that introduced a defect and on the two hundred runs after it. A\n * reviewer reading `a control inside another control` has no way to tell an\n * inherited condition from something they just wrote, and the two are different\n * decisions.\n *\n * Marks rather than findings, from {@link findingMark}. The sentence, the\n * landmark phrase and the source line are all rewritten by edits that do not\n * touch the defect, so storing them would report a defect as new every time the\n * copy beside it changed — and they are the bulk of the bytes, in a sidecar\n * that is committed beside every baseline.\n *\n * **Absent means nothing inspected it, never that it was clean.** A baseline\n * written before this existed, or by a collector that supplies no snapshot, has\n * no list, and a run that read absence as `[]` would announce every standing\n * defect in the suite as newly introduced on the first run after an upgrade.\n */\n readonly findingMarks?: readonly string[];\n}\n"]}
1
+ {"version":3,"file":"document.js","sourceRoot":"","sources":["../../src/format/document.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Diagnostic, SubjectRef } from './capture.js';\nimport type { Viewport } from './environment.js';\nimport { digestValue, type Digest } from './hash.js';\nimport type { ComponentHash } from './snapshot.js';\n\n/**\n * The render document: what gets *sent* somewhere to become an image.\n *\n * This is the acquisition phase's output and the only thing the rendering side\n * ever sees. It is plain serializable data for the same reason `RawCapture` is\n * (ADR-0006): the renderer may be a page in this process, a browser on this\n * machine, or a pinned container two networks away, and no code upstream of it\n * may assume which.\n *\n * Two routes produce one of these, and the point of the type is that they are\n * interchangeable:\n *\n * 1. **jsdom → document → render elsewhere.** jsdom cannot rasterize. It can\n * describe exactly what to rasterize, which is the sub-renderer split ADR-0002\n * asks for: the cheap tier decides almost everything, and the residue is\n * handed to something that owns a GPU.\n * 2. **playwright → document → render on a server.** The same payload, acquired\n * from a real engine. Used when the *deciding* machine and the *pinned*\n * machine are different, which is the case every CI setup actually has.\n *\n * A browser may instead capture a `Raster` in place. That is the sibling branch\n * of `CaptureMaterial`, not a third route that somehow produces this type.\n *\n * ## Why this is small\n *\n * `css` holds the *applicable* rules only — the pruning of ADR-0003, which on\n * `@variance-authority/dom`'s accretion fixture takes 1010 rules to 1. That\n * number is a cost claim as much as a\n * correctness one: shipping a document over a network hop is only sensible if the\n * document is not the entire design system plus Storybook's chrome.\n */\nexport interface RenderDocument {\n readonly documentVersion: 1;\n readonly subject: SubjectRef;\n\n /**\n * The subject subtree, serialized.\n *\n * Serialized rather than referenced by URL: a URL makes the renderer\n * responsible for reproducing the application's state, which it cannot do and\n * should not try. What is sent is the markup that existed at the moment of\n * capture, which is the only definition of \"this render\" that survives a hop.\n */\n readonly html: string;\n\n /**\n * The ancestor context the subject was rendered inside.\n *\n * Not decoration. Pruning keeps rules like `html.dark .card` and\n * `.app .list > li`, whose left-hand side lives *above* the subject — render\n * the subtree bare and those rules match nothing, so the image is missing\n * exactly the styling the collector went to the trouble of proving applies.\n *\n * Reproduced as empty open tags rather than by re-rendering the application:\n * what a selector needs is a chain of elements with the right tags, ids,\n * classes, and attributes, and nothing above the subject contributes anything\n * else to it that {@link RenderDocument.inherited} does not already carry.\n */\n readonly frame: RenderFrame;\n\n /**\n * Applicable stylesheet text, in cascade order.\n *\n * Order is load-bearing and is the caller's to preserve — the cascade breaks\n * specificity ties by document order, so a set that arrives shuffled paints\n * differently while hashing the same if this were a set rather than a list.\n */\n readonly css: readonly string[];\n\n readonly viewport: Viewport;\n\n /**\n * Inherited values in force at the subject root, applied to its wrapper.\n *\n * The same requirement as `RawCapture.inheritedSeed` and for the same reason:\n * pruning drops rules on ancestors outside the subtree, and a font-size\n * inherited from `<html>` is not optional decoration — dropping it changes\n * every metric in the image.\n */\n readonly inherited: Readonly<Record<string, string>>;\n\n /**\n * Fonts the acquiring side observed as loaded.\n *\n * Carried, not resolved. A renderer that lacks one of these will substitute\n * and produce different metrics, and that has to be *detectable* rather than\n * silently absorbed — see {@link RenderIdentity}.\n */\n readonly fonts: readonly string[];\n\n /** External references, url → content hash. Present so a swap is visible. */\n readonly assets?: Readonly<Record<string, string>>;\n\n /**\n * Base against which relative resource references were resolved.\n *\n * Preserves relative-reference resolution for either document kind. Resource\n * closure is denoted by `resources`, not by the presence of this field.\n */\n readonly baseUrl?: string;\n\n /**\n * Immutable responses needed to paint this document, keyed by absolute URL.\n *\n * Presence — including an empty object — means the document is\n * resource-closed: a renderer must not consult the network for a missing URL.\n * Absence preserves the older network-capable document contract and must not\n * be described as portable to another machine.\n */\n readonly resources?: Readonly<Record<string, RenderResource>>;\n\n readonly diagnostics: readonly Diagnostic[];\n}\n\nexport interface RenderResource {\n readonly contentType: string;\n /** Response bytes, base64. */\n readonly bytes: string;\n readonly digest: Digest;\n\n /**\n * The status a renderer answers this URL with. Absent means 200.\n *\n * A subject is allowed to reference something that is not there: a fallback\n * test does it on purpose, and the browser it was acquired in painted the\n * broken state rather than the image. Recording the failure as a failure is\n * what lets a renderer reproduce that state without asking the network.\n */\n readonly status?: number;\n}\n\nexport interface RenderFrame {\n /** Attributes on `<html>`. `class=\"dark\"` here decides half a design system. */\n readonly html: Readonly<Record<string, string>>;\n readonly body: Readonly<Record<string, string>>;\n /** Elements between `<body>` and the subject root, outermost first. */\n readonly ancestors: readonly FrameElement[];\n\n /**\n * Used width of the subject's parent box, in CSS pixels, when layout was\n * observable.\n *\n * A subject declaring `width: 100%` is a different size in a 1024px page and\n * inside a 600px panel, and nothing else in this document says which it was.\n * Absent under a profile with no layout engine, in which case the render falls\n * back to the viewport and the difference, if any, is real rather than hidden.\n */\n readonly containerWidth?: number;\n}\n\nexport interface FrameElement {\n readonly tag: string;\n readonly attributes: Readonly<Record<string, string>>;\n}\n\n/**\n * Content address of a document: *what is to be painted*.\n *\n * This is the deferral lever. Rendering is the expensive phase, and a document\n * whose digest has been rendered before under the same identity has an image\n * already — so the cheapest render is the one that is skipped. Nothing about the\n * branch, the commit, or the file path enters this (Principle 4), so moving a\n * story between files does not cost a re-render.\n *\n * Deliberately covers `viewport` in full, `deviceScaleFactor` included: unlike\n * the semantic key (ADR-0010), a raster genuinely differs at 2x and the whole\n * purpose of this digest is to address the raster.\n *\n * **Limit, stated rather than discovered.** Declaration values arrive serialized\n * by the acquiring engine's CSSOM — jsdom writes `rgb(18, 52, 86)` where the\n * author wrote `#123456` — and this phase deliberately does not canonicalize\n * them, because a collector that normalizes is a second ruleset versioned by\n * nothing (ADR-0001). So two engines can address the same page differently. That\n * makes a render cache *per acquiring engine*, which is a missed cache hit and\n * never a wrong image.\n */\nexport function documentDigest(document: RenderDocument): Digest {\n return digestValue({\n documentVersion: document.documentVersion,\n html: document.html,\n frame: {\n html: { ...document.frame.html },\n body: { ...document.frame.body },\n ancestors: document.frame.ancestors.map((element) => ({\n tag: element.tag,\n attributes: { ...element.attributes },\n })),\n ...(document.frame.containerWidth !== undefined\n ? { containerWidth: document.frame.containerWidth }\n : {}),\n },\n css: [...document.css],\n viewport: { ...document.viewport },\n inherited: { ...document.inherited },\n fonts: [...document.fonts],\n ...(document.assets ? { assets: { ...document.assets } } : {}),\n ...(document.baseUrl !== undefined ? { baseUrl: document.baseUrl } : {}),\n ...(document.resources !== undefined\n ? {\n resources: Object.fromEntries(\n Object.entries(document.resources).map(([url, resource]) => [\n url,\n { ...resource },\n ]),\n ),\n }\n : {}),\n });\n}\n\n/**\n * Who painted it.\n *\n * The durable half of this system rests on one requirement — *images compared\n * against each other must come from the same machine* — and a requirement that\n * is merely stated gets violated by a CI runner upgrade nobody announced. This\n * makes it a value, so it can be compared rather than assumed.\n *\n * Every field is something that has been observed to move pixels without moving\n * markup. None of them is a guess about what *might* matter.\n */\nexport interface RenderIdentity {\n /** `playwright-chromium@1.49.0`, `remote:render.internal`, … */\n readonly renderer: string;\n /** Engine build. A Chromium bump repaints text; that is not a regression. */\n readonly engine: string;\n /** OS and architecture. Font rasterization differs across both. */\n readonly platform: string;\n readonly deviceScaleFactor: number;\n /**\n * Fonts the *renderer* actually has.\n *\n * Compared against {@link RenderDocument.fonts} at render time, because a\n * substituted font is the single most common way two machines disagree, and\n * it is invisible in every artifact except the image itself.\n */\n readonly fonts: readonly string[];\n\n /**\n * Digest of what the renderer did *to* the page so it could be observed.\n *\n * Pausing animations, hiding a caret, waiting for fonts — each is necessary\n * and each widens the gap between what was measured and what a person sees.\n * The gap itself is acceptable; a *difference* in it between two runs is not,\n * because the images then differ for a reason that is not the code and the\n * report blames whichever component sits under the pixels. Folding it in here\n * makes that comparison `incomparable` instead, by the same mechanism that\n * refuses a cross-machine one.\n *\n * Optional only because a renderer may predate the field. Absent means the\n * renderer did not record what it did, which is emphatically not the same as\n * \"it did nothing\" — and two such renderers will compare, which is the hazard\n * that argues for every renderer setting it.\n */\n readonly stabilization?: Digest;\n\n /**\n * Digest of pixel-affecting browser launch and raster settings.\n *\n * Separate from stabilization: one describes how the browser paints, the\n * other what was done to the page before it was read.\n */\n readonly rasterization?: Digest;\n}\n\nexport function identityDigest(identity: RenderIdentity): Digest {\n return digestValue({\n renderer: identity.renderer,\n engine: identity.engine,\n platform: identity.platform,\n ...(identity.stabilization !== undefined ? { stabilization: identity.stabilization } : {}),\n ...(identity.rasterization !== undefined ? { rasterization: identity.rasterization } : {}),\n deviceScaleFactor: identity.deviceScaleFactor,\n fonts: [...identity.fonts],\n });\n}\n\n/**\n * A rendered image and the conditions it was produced under.\n *\n * `bytes` is base64 rather than a `Buffer` so the whole value survives the same\n * hops the document does. A raster that could only exist in Node would make the\n * remote route a special case in every function that touches one.\n *\n * The image is optional, and the three fields that describe it move together.\n * A subject that occupies no pixels — a wrapper whose only child went to a\n * portal, a `describeConformance` mount with no children — still has a\n * document, rules, components and an accessibility tree, all of which compare.\n * Refusing to photograph it is right; refusing the subject reported a quarter\n * of Material UI's unit tier as unobserved over an axis nothing was asking\n * about. Absent here means *this subject has no pixels to observe*, which is a\n * measurement; it never means *the image was lost*.\n */\nexport interface Raster {\n readonly documentDigest: Digest;\n readonly identity: RenderIdentity;\n /**\n * Device pixels. `width / viewport.width` is the scale, and is asserted.\n *\n * Absent exactly when {@link bytes} is, and {@link pictured} is the one place\n * that reads the three together.\n */\n readonly width?: number;\n readonly height?: number;\n /** PNG, base64. Absent when the subject occupies no pixels. */\n readonly bytes?: string;\n /** Fonts the document declared that the renderer did not have. */\n readonly missingFonts: readonly string[];\n\n /**\n * The browser-computed accessibility tree from the acquisition that produced\n * this candidate. It is independent evidence: pixel equality cannot settle it.\n * Absent means the acquisition host did not observe this boundary.\n */\n readonly accessibility?: import('./accessibility.js').AccessibilitySnapshot;\n\n /**\n * What the document that painted this said about its own components (ADR-0018).\n *\n * The field that makes a baseline self-describing, and the reason it is here\n * rather than only in a history store. Separating the component that *caused* a\n * change from the components the change merely moved needs both revisions, and\n * a stored baseline is an image: without this, a run has one document and ranks\n * by area — which\n * [journal 0013](../../../../docs/context/journal/0013-observability.md)\n * measured as backwards.\n *\n * Text, and small: one line per component boundary. It rides in the sidecar\n * beside a PNG that dominates it, so a baseline carries its own explanation\n * wherever it is copied, and a store or a transport that drops it degrades to\n * ranking by area rather than to a wrong answer.\n *\n * Optional because a baseline written before this existed does not have it, and\n * because a profile or a collector that supplies no snapshot cannot produce it.\n * Absent means *unknown*, never *no components*.\n */\n readonly components?: readonly ComponentHash[];\n\n /**\n * What inspection found in the document that painted this, as marks.\n *\n * The field that lets a later run say *this defect was already here* instead of\n * printing every defect it finds with no indication of when it arrived. A\n * finding is produced from one render with no baseline consulted — that is the\n * point of having one — and the consequence is that the list is identical on\n * the run that introduced a defect and on the two hundred runs after it. A\n * reviewer reading `a control inside another control` has no way to tell an\n * inherited condition from something they just wrote, and the two are different\n * decisions.\n *\n * Marks rather than findings, from {@link findingMark}. The sentence, the\n * landmark phrase and the source line are all rewritten by edits that do not\n * touch the defect, so storing them would report a defect as new every time the\n * copy beside it changed — and they are the bulk of the bytes, in a sidecar\n * that is committed beside every baseline.\n *\n * **Absent means nothing inspected it, never that it was clean.** A baseline\n * written before this existed, or by a collector that supplies no snapshot, has\n * no list, and a run that read absence as `[]` would announce every standing\n * defect in the suite as newly introduced on the first run after an upgrade.\n */\n readonly findingMarks?: readonly string[];\n}\n\n/**\n * A raster whose image is in hand.\n *\n * Every pixel comparison in the codebase needs all three of `bytes`, `width`\n * and `height`, and they are absent together or present together. Narrowing\n * them one at a time is how a caller ends up reading the bytes of a subject\n * that was never photographed, so the narrowing lives here and is done once.\n */\nexport type Pictured = Raster & {\n readonly width: number;\n readonly height: number;\n readonly bytes: string;\n};\n\n/** Whether this record carries an image, narrowing all three fields together. */\nexport function pictured(raster: Raster): raster is Pictured {\n return raster.bytes !== undefined && raster.width !== undefined && raster.height !== undefined;\n}\n\n/**\n * Whether the subject occupied any pixels, answered without the image.\n *\n * The question {@link pictured} cannot be asked of a sidecar. Bytes live in a\n * separate file, row or object, so a record read without them carries the width\n * and the height and nothing else — and those *are* the store's record of\n * whether there was ever anything to photograph, which is why `sidecarFrom`\n * refuses a sidecar carrying one of them.\n *\n * So: this for a record, {@link pictured} for an image in hand. A caller that\n * reached for `pictured` here would read every sidecar in the store as a subject\n * with no pixels, because none of them have bytes.\n */\nexport function occupiesPixels(raster: Omit<Raster, 'bytes'>): boolean {\n return raster.width !== undefined && raster.height !== undefined;\n}\n\n/**\n * The image, or a refusal naming the subject that has none.\n *\n * For the paths where a missing image is a programming error rather than a\n * measurement — a diff, a PNG encode — so they read as assertions instead of\n * as a non-null assertion nobody can audit.\n */\nexport function picture(raster: Raster, subject: string): Pictured {\n if (!pictured(raster)) {\n throw new Error(\n `\\`${subject}\\` occupies no pixels, and this path needs an image; ` +\n 'a subject with no raster compares on its document alone',\n );\n }\n return raster;\n}\n"]}
@@ -23,6 +23,21 @@ export type Digest = string;
23
23
  * and would colour the whole normalizer; see `sha256.ts`.
24
24
  */
25
25
  export declare function digestString(input: string): Digest;
26
+ /**
27
+ * The digest of a SHA-256 some other implementation already computed.
28
+ *
29
+ * The algorithm in `sha256.ts` is portable TypeScript because `core` has to run
30
+ * inside the page. A host that is not a page — a transform, a scanner, anything
31
+ * reading files off a disk — has the same hash natively and an order of
32
+ * magnitude faster, and FIPS 180-4 leaves no room for the two to disagree.
33
+ *
34
+ * What such a host must not also own is the shape. The prefix is what makes an
35
+ * algorithm change a greppable migration, and the truncation is what keeps a
36
+ * manifest readable; a second place deciding either is a digest domain that
37
+ * splits without anybody noticing. So the host brings the hex and this brings
38
+ * the identity.
39
+ */
40
+ export declare function digestOfSha256(hex: string): Digest;
26
41
  /**
27
42
  * Hash raw bytes — an image, a font file, anything whose identity is its octets.
28
43
  *
@@ -42,4 +57,15 @@ export declare function digestValue(value: CanonicalValue): Digest;
42
57
  * change could masquerade as a content match.
43
58
  */
44
59
  export declare function digestCombine(domain: string, parts: readonly Digest[]): Digest;
60
+ /**
61
+ * A filesystem name for an id that was not written with filesystems in mind.
62
+ *
63
+ * An id long enough to overrun a filename is not an exotic case: a suite that
64
+ * names subjects after the test that produced them -- a file path and a full
65
+ * test name -- passes 255 bytes on ordinary tests, and the whole point of that
66
+ * convention is that the id says where the subject came from. Truncating alone
67
+ * would put two tests in one file, so a long id keeps a readable prefix and
68
+ * earns a digest of the whole id, which is what actually distinguishes it.
69
+ */
70
+ export declare function fileNameFor(id: string): string;
45
71
  //# sourceMappingURL=hash.d.ts.map
@@ -13,6 +13,23 @@ const HEX_LENGTH = 32;
13
13
  export function digestString(input) {
14
14
  return `${PREFIX}:${sha256Hex(input).slice(0, HEX_LENGTH)}`;
15
15
  }
16
+ /**
17
+ * The digest of a SHA-256 some other implementation already computed.
18
+ *
19
+ * The algorithm in `sha256.ts` is portable TypeScript because `core` has to run
20
+ * inside the page. A host that is not a page — a transform, a scanner, anything
21
+ * reading files off a disk — has the same hash natively and an order of
22
+ * magnitude faster, and FIPS 180-4 leaves no room for the two to disagree.
23
+ *
24
+ * What such a host must not also own is the shape. The prefix is what makes an
25
+ * algorithm change a greppable migration, and the truncation is what keeps a
26
+ * manifest readable; a second place deciding either is a digest domain that
27
+ * splits without anybody noticing. So the host brings the hex and this brings
28
+ * the identity.
29
+ */
30
+ export function digestOfSha256(hex) {
31
+ return `${PREFIX}:${hex.slice(0, HEX_LENGTH)}`;
32
+ }
16
33
  /**
17
34
  * Hash raw bytes — an image, a font file, anything whose identity is its octets.
18
35
  *
@@ -38,4 +55,28 @@ export function digestValue(value) {
38
55
  export function digestCombine(domain, parts) {
39
56
  return digestString(`${domain}\u0000${parts.join('\u0000')}`);
40
57
  }
58
+ /**
59
+ * How much of an encoded id a filename may carry.
60
+ *
61
+ * A filename is capped at 255 bytes on every filesystem anybody runs a suite on,
62
+ * and a caller's own suffix -- an extension, a temporary marker with a pid and a
63
+ * UUID in it -- comes out of the same 255. What is left is the budget.
64
+ */
65
+ const NAME_BUDGET = 140;
66
+ /**
67
+ * A filesystem name for an id that was not written with filesystems in mind.
68
+ *
69
+ * An id long enough to overrun a filename is not an exotic case: a suite that
70
+ * names subjects after the test that produced them -- a file path and a full
71
+ * test name -- passes 255 bytes on ordinary tests, and the whole point of that
72
+ * convention is that the id says where the subject came from. Truncating alone
73
+ * would put two tests in one file, so a long id keeps a readable prefix and
74
+ * earns a digest of the whole id, which is what actually distinguishes it.
75
+ */
76
+ export function fileNameFor(id) {
77
+ const encoded = encodeURIComponent(id);
78
+ if (encoded.length <= NAME_BUDGET)
79
+ return encoded;
80
+ return `${encoded.slice(0, NAME_BUDGET)}~${digestString(id).replace(':', '-')}`;
81
+ }
41
82
  //# sourceMappingURL=hash.js.map
@@ -1 +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"]}
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;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,OAAO,GAAG,MAAM,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,UAAU,CAAC,EAAE,CAAC;AACjD,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;AAED;;;;;;GAMG;AACH,MAAM,WAAW,GAAG,GAAG,CAAC;AAExB;;;;;;;;;GASG;AACH,MAAM,UAAU,WAAW,CAAC,EAAU;IACpC,MAAM,OAAO,GAAG,kBAAkB,CAAC,EAAE,CAAC,CAAC;IACvC,IAAI,OAAO,CAAC,MAAM,IAAI,WAAW;QAAE,OAAO,OAAO,CAAC;IAClD,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,WAAW,CAAC,IAAI,YAAY,CAAC,EAAE,CAAC,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,EAAE,CAAC;AAClF,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 * The digest of a SHA-256 some other implementation already computed.\n *\n * The algorithm in `sha256.ts` is portable TypeScript because `core` has to run\n * inside the page. A host that is not a page — a transform, a scanner, anything\n * reading files off a disk — has the same hash natively and an order of\n * magnitude faster, and FIPS 180-4 leaves no room for the two to disagree.\n *\n * What such a host must not also own is the shape. The prefix is what makes an\n * algorithm change a greppable migration, and the truncation is what keeps a\n * manifest readable; a second place deciding either is a digest domain that\n * splits without anybody noticing. So the host brings the hex and this brings\n * the identity.\n */\nexport function digestOfSha256(hex: string): Digest {\n return `${PREFIX}:${hex.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\n/**\n * How much of an encoded id a filename may carry.\n *\n * A filename is capped at 255 bytes on every filesystem anybody runs a suite on,\n * and a caller's own suffix -- an extension, a temporary marker with a pid and a\n * UUID in it -- comes out of the same 255. What is left is the budget.\n */\nconst NAME_BUDGET = 140;\n\n/**\n * A filesystem name for an id that was not written with filesystems in mind.\n *\n * An id long enough to overrun a filename is not an exotic case: a suite that\n * names subjects after the test that produced them -- a file path and a full\n * test name -- passes 255 bytes on ordinary tests, and the whole point of that\n * convention is that the id says where the subject came from. Truncating alone\n * would put two tests in one file, so a long id keeps a readable prefix and\n * earns a digest of the whole id, which is what actually distinguishes it.\n */\nexport function fileNameFor(id: string): string {\n const encoded = encodeURIComponent(id);\n if (encoded.length <= NAME_BUDGET) return encoded;\n return `${encoded.slice(0, NAME_BUDGET)}~${digestString(id).replace(':', '-')}`;\n}\n"]}
@@ -16,7 +16,7 @@ export { canonicalize, canonicalNumber } from './canonical.js';
16
16
  export type { CapturedValue, ShapeValueOptions, ValueShaping } from './value.js';
17
17
  export { shapeValue, pointerShape, pointerToken, DROPPED, VALUE_RECIPE } from './value.js';
18
18
  export type { Digest } from './hash.js';
19
- export { digestString, digestBytes, digestValue, digestCombine } from './hash.js';
19
+ export { digestString, digestBytes, digestValue, digestCombine, digestOfSha256, fileNameFor } from './hash.js';
20
20
  export type { AccessibilitySnapshot } from './accessibility.js';
21
21
  export { accessibilitySnapshot } from './accessibility.js';
22
22
  export type { ObservationProfile, ProfileId } from './profile.js';
@@ -33,7 +33,8 @@ export type { Holding, HeldCell, HeldValue } from './holding.js';
33
33
  export type { Wiring } from './wiring.js';
34
34
  export { keyedByPosition } from './wiring.js';
35
35
  export type { RawCapture, RawNode, RawAria, MatchedRule, Declaration, Rect, SubjectRef, Diagnostic, } from './capture.js';
36
+ export { mergeDiagnostics } from './capture.js';
36
37
  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';
38
+ export { documentDigest, identityDigest, occupiesPixels, pictured, picture } from './document.js';
39
+ export type { RenderDocument, RenderFrame, FrameElement, RenderResource, RenderIdentity, Raster, Pictured, } from './document.js';
39
40
  //# sourceMappingURL=index.d.ts.map
@@ -13,7 +13,7 @@
13
13
  */
14
14
  export { canonicalize, canonicalNumber } from './canonical.js';
15
15
  export { shapeValue, pointerShape, pointerToken, DROPPED, VALUE_RECIPE } from './value.js';
16
- export { digestString, digestBytes, digestValue, digestCombine } from './hash.js';
16
+ export { digestString, digestBytes, digestValue, digestCombine, digestOfSha256, fileNameFor } from './hash.js';
17
17
  export { accessibilitySnapshot } from './accessibility.js';
18
18
  export { JSDOM_PROFILE, CHROMIUM_PROFILE, profileById, tierOfProfile } from './profile.js';
19
19
  export { tierReaches } from './tier.js';
@@ -21,5 +21,6 @@ export { environmentKey, diffEnvironments } from './environment.js';
21
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
22
  export { propsDigest, heldDigest, jsxSourceOf, relativizeSource, JSX_SOURCE } from './provenance.js';
23
23
  export { keyedByPosition } from './wiring.js';
24
- export { documentDigest, identityDigest } from './document.js';
24
+ export { mergeDiagnostics } from './capture.js';
25
+ export { documentDigest, identityDigest, occupiesPixels, pictured, picture } from './document.js';
25
26
  //# sourceMappingURL=index.js.map
@@ -1 +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"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/format/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAKH,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,cAAc,EAAE,WAAW,EAAE,MAAM,WAAW,CAAC;AAG/G,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;AAY9C,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAWhD,OAAO,EAAE,cAAc,EAAE,cAAc,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,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\n// compass: variance-authority.normalization\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, digestOfSha256, fileNameFor } 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';\nexport { mergeDiagnostics } 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, occupiesPixels, pictured, picture } from './document.js';\nexport type {\n RenderDocument,\n RenderFrame,\n FrameElement,\n RenderResource,\n RenderIdentity,\n Raster,\n Pictured,\n} from './document.js';\n"]}
@@ -16,8 +16,16 @@
16
16
  *
17
17
  * So the algorithm lives here. It is fully specified by FIPS 180-4, so this
18
18
  * produces byte-identical output to any other correct implementation on any
19
- * runtime, which is exactly the determinism Principle 2 requires. Performance is
20
- * not a consideration until a gate in ADR-0004 fires; none has.
19
+ * runtime, which is exactly the determinism Principle 2 requires.
20
+ *
21
+ * Performance was declared a non-consideration here until a gate fired. One has.
22
+ * A props digest is taken for every composite boundary enclosing every node of a
23
+ * subject, and on a page that hands its components a theme object or a data
24
+ * grid's API handle that is megabytes of canonical text per capture — so this
25
+ * function's throughput is a term in how long photographing a page takes. What
26
+ * that bought is allocation removal only: the block loop and the encoder below
27
+ * are the same arithmetic they were, and every digest this file has ever
28
+ * produced it still produces.
21
29
  */
22
30
  /** Hash a UTF-8 string, returning lowercase hex. */
23
31
  export declare function sha256Hex(input: string): string;
@@ -16,8 +16,16 @@
16
16
  *
17
17
  * So the algorithm lives here. It is fully specified by FIPS 180-4, so this
18
18
  * produces byte-identical output to any other correct implementation on any
19
- * runtime, which is exactly the determinism Principle 2 requires. Performance is
20
- * not a consideration until a gate in ADR-0004 fires; none has.
19
+ * runtime, which is exactly the determinism Principle 2 requires.
20
+ *
21
+ * Performance was declared a non-consideration here until a gate fired. One has.
22
+ * A props digest is taken for every composite boundary enclosing every node of a
23
+ * subject, and on a page that hands its components a theme object or a data
24
+ * grid's API handle that is megabytes of canonical text per capture — so this
25
+ * function's throughput is a term in how long photographing a page takes. What
26
+ * that bought is allocation removal only: the block loop and the encoder below
27
+ * are the same arithmetic they were, and every digest this file has ever
28
+ * produced it still produces.
21
29
  */
22
30
  const K = new Uint32Array([
23
31
  0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
@@ -73,7 +81,19 @@ export function sha256HexBytes(bytes) {
73
81
  const s1 = rotr(w[i - 2], 17) ^ rotr(w[i - 2], 19) ^ (w[i - 2] >>> 10);
74
82
  w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
75
83
  }
76
- let [a, b, c, d, e, f, g, h] = state;
84
+ // Read by index rather than destructured. `state` is a `Uint32Array`, and
85
+ // array destructuring goes through the iterator protocol — one iterator
86
+ // object and eight `next()` results allocated per 64-byte block, which on a
87
+ // page digesting megabytes of props is most of the garbage this function
88
+ // makes.
89
+ let a = state[0];
90
+ let b = state[1];
91
+ let c = state[2];
92
+ let d = state[3];
93
+ let e = state[4];
94
+ let f = state[5];
95
+ let g = state[6];
96
+ let h = state[7];
77
97
  for (let i = 0; i < 64; i += 1) {
78
98
  const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);
79
99
  const ch = (e & f) ^ (~e & g);
@@ -118,7 +138,14 @@ function rotr(value, bits) {
118
138
  * two different digests on two runtimes.
119
139
  */
120
140
  function utf8Bytes(input) {
121
- const bytes = [];
141
+ // Written into a buffer sized for the worst case rather than pushed onto a
142
+ // `number[]` that is then copied. Three bytes per UTF-16 unit is exactly the
143
+ // ceiling: a unit below U+0800 costs at most two, one at or above it costs
144
+ // three, and a surrogate pair costs four across two units. The alternative
145
+ // allocated a boxed array the length of the text and then a second copy of it,
146
+ // and a page digesting megabytes of props pays for both.
147
+ const bytes = new Uint8Array(input.length * 3);
148
+ let length = 0;
122
149
  for (let i = 0; i < input.length; i += 1) {
123
150
  let code = input.charCodeAt(i);
124
151
  if (code >= 0xd800 && code <= 0xdbff) {
@@ -135,18 +162,24 @@ function utf8Bytes(input) {
135
162
  code = 0xfffd;
136
163
  }
137
164
  if (code < 0x80) {
138
- bytes.push(code);
165
+ bytes[length++] = code;
139
166
  }
140
167
  else if (code < 0x800) {
141
- bytes.push(0xc0 | (code >> 6), 0x80 | (code & 0x3f));
168
+ bytes[length++] = 0xc0 | (code >> 6);
169
+ bytes[length++] = 0x80 | (code & 0x3f);
142
170
  }
143
171
  else if (code < 0x10000) {
144
- bytes.push(0xe0 | (code >> 12), 0x80 | ((code >> 6) & 0x3f), 0x80 | (code & 0x3f));
172
+ bytes[length++] = 0xe0 | (code >> 12);
173
+ bytes[length++] = 0x80 | ((code >> 6) & 0x3f);
174
+ bytes[length++] = 0x80 | (code & 0x3f);
145
175
  }
146
176
  else {
147
- bytes.push(0xf0 | (code >> 18), 0x80 | ((code >> 12) & 0x3f), 0x80 | ((code >> 6) & 0x3f), 0x80 | (code & 0x3f));
177
+ bytes[length++] = 0xf0 | (code >> 18);
178
+ bytes[length++] = 0x80 | ((code >> 12) & 0x3f);
179
+ bytes[length++] = 0x80 | ((code >> 6) & 0x3f);
180
+ bytes[length++] = 0x80 | (code & 0x3f);
148
181
  }
149
182
  }
150
- return Uint8Array.from(bytes);
183
+ return bytes.subarray(0, length);
151
184
  }
152
185
  //# sourceMappingURL=sha256.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"sha256.js","sourceRoot":"","sources":["../../src/format/sha256.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SHA-256, in portable TypeScript.\n *\n * `core` originally used `node:crypto`, which was wrong — and the reason is not\n * ergonomic. `propsDigest` has to run *inside the page*: a React prop can be a\n * function or an element, and neither survives serialization out to a host\n * process, so the digest must be taken where the value still exists. A `core`\n * that imports `node:crypto` cannot run there, which quietly contradicted the\n * remote-sub-renderer story in ADR-0002 and the DOM-free boundary in ADR-0001.\n * (The `lib: [\"ES2022\"]` guard is one-directional: it stops `core` reaching into\n * a browser, not `core` depending on Node.)\n *\n * `crypto.subtle.digest` is the obvious replacement and is not usable: it is\n * async, and hashing is synchronous throughout by design — making it async would\n * colour the entire normalizer for no benefit.\n *\n * So the algorithm lives here. It is fully specified by FIPS 180-4, so this\n * produces byte-identical output to any other correct implementation on any\n * runtime, which is exactly the determinism Principle 2 requires. Performance is\n * not a consideration until a gate in ADR-0004 fires; none has.\n */\n\nconst K = new Uint32Array([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n]);\n\nconst INITIAL = new Uint32Array([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\n/** Hash a UTF-8 string, returning lowercase hex. */\nexport function sha256Hex(input: string): string {\n return sha256HexBytes(utf8Bytes(input));\n}\n\n/**\n * Hash raw bytes, returning lowercase hex.\n *\n * The string form is this with one encoding step in front of it, and not the\n * other way round: an image is bytes, and routing it through a string would mean\n * choosing an encoding that survives arbitrary octets — which is a second\n * decision, made in the wrong place, that changes the digest if it is ever\n * revisited.\n */\nexport function sha256HexBytes(bytes: Uint8Array): string {\n const state = new Uint32Array(INITIAL);\n\n // Padding: a single 1 bit, zeros, then the 64-bit big-endian bit length.\n const bitLength = bytes.length * 8;\n // Padding is minimal by definition: the smallest multiple of 64 that fits the\n // message, the 0x80 byte, and the 8-byte length. Rounding up unconditionally\n // would append a spare block whenever `length + 9` is already a multiple of\n // 64, which is a different message and therefore a different digest.\n const paddedLength = Math.ceil((bytes.length + 9) / 64) * 64;\n const padded = new Uint8Array(paddedLength);\n padded.set(bytes);\n padded[bytes.length] = 0x80;\n\n // Written as two 32-bit halves rather than a BigInt: inputs never approach\n // 2^32 bits (512 MiB), and the high word is always zero in practice, but\n // emitting it keeps the block format exactly as specified.\n const view = new DataView(padded.buffer);\n view.setUint32(paddedLength - 8, Math.floor(bitLength / 0x100000000), false);\n view.setUint32(paddedLength - 4, bitLength >>> 0, false);\n\n const w = new Uint32Array(64);\n\n for (let offset = 0; offset < paddedLength; offset += 64) {\n for (let i = 0; i < 16; i += 1) {\n w[i] = view.getUint32(offset + i * 4, false);\n }\n for (let i = 16; i < 64; i += 1) {\n const s0 = rotr(w[i - 15]!, 7) ^ rotr(w[i - 15]!, 18) ^ (w[i - 15]! >>> 3);\n const s1 = rotr(w[i - 2]!, 17) ^ rotr(w[i - 2]!, 19) ^ (w[i - 2]! >>> 10);\n w[i] = (w[i - 16]! + s0 + w[i - 7]! + s1) >>> 0;\n }\n\n let [a, b, c, d, e, f, g, h] = state as unknown as number[];\n\n for (let i = 0; i < 64; i += 1) {\n const S1 = rotr(e!, 6) ^ rotr(e!, 11) ^ rotr(e!, 25);\n const ch = (e! & f!) ^ (~e! & g!);\n const temp1 = (h! + S1 + ch + K[i]! + w[i]!) >>> 0;\n const S0 = rotr(a!, 2) ^ rotr(a!, 13) ^ rotr(a!, 22);\n const maj = (a! & b!) ^ (a! & c!) ^ (b! & c!);\n const temp2 = (S0 + maj) >>> 0;\n\n h = g;\n g = f;\n f = e;\n e = (d! + temp1) >>> 0;\n d = c;\n c = b;\n b = a;\n a = (temp1 + temp2) >>> 0;\n }\n\n state[0] = (state[0]! + a!) >>> 0;\n state[1] = (state[1]! + b!) >>> 0;\n state[2] = (state[2]! + c!) >>> 0;\n state[3] = (state[3]! + d!) >>> 0;\n state[4] = (state[4]! + e!) >>> 0;\n state[5] = (state[5]! + f!) >>> 0;\n state[6] = (state[6]! + g!) >>> 0;\n state[7] = (state[7]! + h!) >>> 0;\n }\n\n let hex = '';\n for (const word of state) {\n hex += word.toString(16).padStart(8, '0');\n }\n return hex;\n}\n\nfunction rotr(value: number, bits: number): number {\n return ((value >>> bits) | (value << (32 - bits))) >>> 0;\n}\n\n/**\n * Encode as UTF-8 without `TextEncoder`.\n *\n * `TextEncoder` is available everywhere this runs today, but hand-encoding keeps\n * `core` free of *every* host global, not merely the Node ones — which is the\n * property that just failed to hold. Lone surrogates are encoded as U+FFFD, the\n * same substitution `TextEncoder` makes, so a malformed string cannot produce\n * two different digests on two runtimes.\n */\nfunction utf8Bytes(input: string): Uint8Array {\n const bytes: number[] = [];\n\n for (let i = 0; i < input.length; i += 1) {\n let code = input.charCodeAt(i);\n\n if (code >= 0xd800 && code <= 0xdbff) {\n const next = input.charCodeAt(i + 1);\n if (next >= 0xdc00 && next <= 0xdfff) {\n code = 0x10000 + ((code - 0xd800) << 10) + (next - 0xdc00);\n i += 1;\n } else {\n code = 0xfffd;\n }\n } else if (code >= 0xdc00 && code <= 0xdfff) {\n code = 0xfffd;\n }\n\n if (code < 0x80) {\n bytes.push(code);\n } else if (code < 0x800) {\n bytes.push(0xc0 | (code >> 6), 0x80 | (code & 0x3f));\n } else if (code < 0x10000) {\n bytes.push(0xe0 | (code >> 12), 0x80 | ((code >> 6) & 0x3f), 0x80 | (code & 0x3f));\n } else {\n bytes.push(\n 0xf0 | (code >> 18),\n 0x80 | ((code >> 12) & 0x3f),\n 0x80 | ((code >> 6) & 0x3f),\n 0x80 | (code & 0x3f),\n );\n }\n }\n\n return Uint8Array.from(bytes);\n}\n"]}
1
+ {"version":3,"file":"sha256.js","sourceRoot":"","sources":["../../src/format/sha256.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * SHA-256, in portable TypeScript.\n *\n * `core` originally used `node:crypto`, which was wrong — and the reason is not\n * ergonomic. `propsDigest` has to run *inside the page*: a React prop can be a\n * function or an element, and neither survives serialization out to a host\n * process, so the digest must be taken where the value still exists. A `core`\n * that imports `node:crypto` cannot run there, which quietly contradicted the\n * remote-sub-renderer story in ADR-0002 and the DOM-free boundary in ADR-0001.\n * (The `lib: [\"ES2022\"]` guard is one-directional: it stops `core` reaching into\n * a browser, not `core` depending on Node.)\n *\n * `crypto.subtle.digest` is the obvious replacement and is not usable: it is\n * async, and hashing is synchronous throughout by design — making it async would\n * colour the entire normalizer for no benefit.\n *\n * So the algorithm lives here. It is fully specified by FIPS 180-4, so this\n * produces byte-identical output to any other correct implementation on any\n * runtime, which is exactly the determinism Principle 2 requires.\n *\n * Performance was declared a non-consideration here until a gate fired. One has.\n * A props digest is taken for every composite boundary enclosing every node of a\n * subject, and on a page that hands its components a theme object or a data\n * grid's API handle that is megabytes of canonical text per capture — so this\n * function's throughput is a term in how long photographing a page takes. What\n * that bought is allocation removal only: the block loop and the encoder below\n * are the same arithmetic they were, and every digest this file has ever\n * produced it still produces.\n */\n\nconst K = new Uint32Array([\n 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,\n 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,\n 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,\n 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,\n 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,\n 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,\n 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,\n 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,\n]);\n\nconst INITIAL = new Uint32Array([\n 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,\n]);\n\n/** Hash a UTF-8 string, returning lowercase hex. */\nexport function sha256Hex(input: string): string {\n return sha256HexBytes(utf8Bytes(input));\n}\n\n/**\n * Hash raw bytes, returning lowercase hex.\n *\n * The string form is this with one encoding step in front of it, and not the\n * other way round: an image is bytes, and routing it through a string would mean\n * choosing an encoding that survives arbitrary octets — which is a second\n * decision, made in the wrong place, that changes the digest if it is ever\n * revisited.\n */\nexport function sha256HexBytes(bytes: Uint8Array): string {\n const state = new Uint32Array(INITIAL);\n\n // Padding: a single 1 bit, zeros, then the 64-bit big-endian bit length.\n const bitLength = bytes.length * 8;\n // Padding is minimal by definition: the smallest multiple of 64 that fits the\n // message, the 0x80 byte, and the 8-byte length. Rounding up unconditionally\n // would append a spare block whenever `length + 9` is already a multiple of\n // 64, which is a different message and therefore a different digest.\n const paddedLength = Math.ceil((bytes.length + 9) / 64) * 64;\n const padded = new Uint8Array(paddedLength);\n padded.set(bytes);\n padded[bytes.length] = 0x80;\n\n // Written as two 32-bit halves rather than a BigInt: inputs never approach\n // 2^32 bits (512 MiB), and the high word is always zero in practice, but\n // emitting it keeps the block format exactly as specified.\n const view = new DataView(padded.buffer);\n view.setUint32(paddedLength - 8, Math.floor(bitLength / 0x100000000), false);\n view.setUint32(paddedLength - 4, bitLength >>> 0, false);\n\n const w = new Uint32Array(64);\n\n for (let offset = 0; offset < paddedLength; offset += 64) {\n for (let i = 0; i < 16; i += 1) {\n w[i] = view.getUint32(offset + i * 4, false);\n }\n for (let i = 16; i < 64; i += 1) {\n const s0 = rotr(w[i - 15]!, 7) ^ rotr(w[i - 15]!, 18) ^ (w[i - 15]! >>> 3);\n const s1 = rotr(w[i - 2]!, 17) ^ rotr(w[i - 2]!, 19) ^ (w[i - 2]! >>> 10);\n w[i] = (w[i - 16]! + s0 + w[i - 7]! + s1) >>> 0;\n }\n\n // Read by index rather than destructured. `state` is a `Uint32Array`, and\n // array destructuring goes through the iterator protocol — one iterator\n // object and eight `next()` results allocated per 64-byte block, which on a\n // page digesting megabytes of props is most of the garbage this function\n // makes.\n let a = state[0]!;\n let b = state[1]!;\n let c = state[2]!;\n let d = state[3]!;\n let e = state[4]!;\n let f = state[5]!;\n let g = state[6]!;\n let h = state[7]!;\n\n for (let i = 0; i < 64; i += 1) {\n const S1 = rotr(e, 6) ^ rotr(e, 11) ^ rotr(e, 25);\n const ch = (e & f) ^ (~e & g);\n const temp1 = (h + S1 + ch + K[i]! + w[i]!) >>> 0;\n const S0 = rotr(a, 2) ^ rotr(a, 13) ^ rotr(a, 22);\n const maj = (a & b) ^ (a & c) ^ (b & c);\n const temp2 = (S0 + maj) >>> 0;\n\n h = g;\n g = f;\n f = e;\n e = (d + temp1) >>> 0;\n d = c;\n c = b;\n b = a;\n a = (temp1 + temp2) >>> 0;\n }\n\n state[0] = (state[0]! + a) >>> 0;\n state[1] = (state[1]! + b) >>> 0;\n state[2] = (state[2]! + c) >>> 0;\n state[3] = (state[3]! + d) >>> 0;\n state[4] = (state[4]! + e) >>> 0;\n state[5] = (state[5]! + f) >>> 0;\n state[6] = (state[6]! + g) >>> 0;\n state[7] = (state[7]! + h) >>> 0;\n }\n\n let hex = '';\n for (const word of state) {\n hex += word.toString(16).padStart(8, '0');\n }\n return hex;\n}\n\nfunction rotr(value: number, bits: number): number {\n return ((value >>> bits) | (value << (32 - bits))) >>> 0;\n}\n\n/**\n * Encode as UTF-8 without `TextEncoder`.\n *\n * `TextEncoder` is available everywhere this runs today, but hand-encoding keeps\n * `core` free of *every* host global, not merely the Node ones — which is the\n * property that just failed to hold. Lone surrogates are encoded as U+FFFD, the\n * same substitution `TextEncoder` makes, so a malformed string cannot produce\n * two different digests on two runtimes.\n */\nfunction utf8Bytes(input: string): Uint8Array {\n // Written into a buffer sized for the worst case rather than pushed onto a\n // `number[]` that is then copied. Three bytes per UTF-16 unit is exactly the\n // ceiling: a unit below U+0800 costs at most two, one at or above it costs\n // three, and a surrogate pair costs four across two units. The alternative\n // allocated a boxed array the length of the text and then a second copy of it,\n // and a page digesting megabytes of props pays for both.\n const bytes = new Uint8Array(input.length * 3);\n let length = 0;\n\n for (let i = 0; i < input.length; i += 1) {\n let code = input.charCodeAt(i);\n\n if (code >= 0xd800 && code <= 0xdbff) {\n const next = input.charCodeAt(i + 1);\n if (next >= 0xdc00 && next <= 0xdfff) {\n code = 0x10000 + ((code - 0xd800) << 10) + (next - 0xdc00);\n i += 1;\n } else {\n code = 0xfffd;\n }\n } else if (code >= 0xdc00 && code <= 0xdfff) {\n code = 0xfffd;\n }\n\n if (code < 0x80) {\n bytes[length++] = code;\n } else if (code < 0x800) {\n bytes[length++] = 0xc0 | (code >> 6);\n bytes[length++] = 0x80 | (code & 0x3f);\n } else if (code < 0x10000) {\n bytes[length++] = 0xe0 | (code >> 12);\n bytes[length++] = 0x80 | ((code >> 6) & 0x3f);\n bytes[length++] = 0x80 | (code & 0x3f);\n } else {\n bytes[length++] = 0xf0 | (code >> 18);\n bytes[length++] = 0x80 | ((code >> 12) & 0x3f);\n bytes[length++] = 0x80 | ((code >> 6) & 0x3f);\n bytes[length++] = 0x80 | (code & 0x3f);\n }\n }\n\n return bytes.subarray(0, length);\n}\n"]}
@@ -70,6 +70,21 @@ export declare const holdAnimations: Intervention;
70
70
  * page rather than stopping it.
71
71
  */
72
72
  export declare const pinAnimations: Intervention;
73
+ /**
74
+ * A stylesheet rather than the driver's own caret switch, deliberately.
75
+ *
76
+ * Playwright and its peers hide a caret by writing `caret-color` onto every
77
+ * focusable element and putting it back afterwards, and putting it back leaves
78
+ * `style=""` where there was no `style` attribute at all. Any capture that reads
79
+ * the DOM on both sides of a screenshot — which is what the in-place path does
80
+ * to prove the subject held still — then sees markup that moved, and reports the
81
+ * driver's housekeeping as the page being unstable. A page with one text input
82
+ * is enough.
83
+ *
84
+ * So this hold owns the caret the way {@link pinAnimations} owns animations: one
85
+ * stylesheet, installed before the subject is read, removed with the rest of the
86
+ * recipe.
87
+ */
73
88
  export declare const hideCaret: Intervention;
74
89
  export declare const hideScrollbars: Intervention;
75
90
  /**