/** * Serve an image as *nothing*, at exactly the size it would have been. * * ## What this is for * * A page's images are not all part of what a visual test is asserting. A hero * photograph rotated by a CMS, an avatar served from a third party, a marketing * illustration re-exported at a new compression — each repaints a large area, * none of them is a regression, and every one of them is indistinguishable from * a regression once it reaches the pixels. The industry answer is a mask drawn * over the region afterwards, and it is the wrong shape in three separate ways: * the bytes are still fetched, the *layout* still moves when the new image has * different dimensions, and the environment key still changes, so the run * re-renders every subject the image appears on to discover it need not have. * * Blanking answers all three at once, on the wire, before the browser has * decoded anything: the response is replaced with a fully transparent image of * **the original's intrinsic dimensions**, so every box on the page resolves * exactly as it would have, and the asset is recorded in the environment key as * *the blank it became* rather than as the bytes it was. * * ## Why the dimensions are the whole difficulty * * A one-pixel transparent PNG is trivial to serve and destroys the page. An * `` with no width in CSS lays out at its intrinsic size, so replacing a * 1200×600 banner with a 1×1 collapses the column it was holding open, and the * run then reports a layout regression that this tool caused. So the size is * read out of the original bytes first, and a format whose header this cannot * read is **served unmodified with a diagnostic** rather than blanked at a * guessed size. Refusing is cheap; a fabricated layout is a red run nobody can * explain. * * ## Transparent, not grey * * What is left is the page's own background, which is the honest rendering of * "there is nothing here". A flat fill would be a second thing to compare — and * would silently change the contrast under any text sitting on top, which the * accessibility rules would then report as a defect of the page rather than of * the substitution. One consequence to know: a contrast finding over a blanked * image is measured against whatever is *behind* the image. * * ## What it cannot see * * A request carries no idea which element wanted it. `role="presentation"`, * `alt=""`, a selector, a rendered box — none of those exists at this layer, and * no amount of care here will produce them. Those are decided in the page, by * the collector, against the DOM; this file decides the half that is a fact * about the bytes and the URL. The split is not a limitation to be worked around * later, it is where the two kinds of knowledge actually live. */ /** * One reason to blank, and what it applies to. * * Every matcher present must hold. A rule that names **no** matcher is refused * by {@link blankRuleError} rather than treated as "everything": a config that * blanks every image on the site is a legitimate thing to want and an illegitimate * thing to arrive at by leaving a field out. */ export interface BlankRule { /** Names this rule in the ledger and in the environment key. */ readonly id: string; /** * Why these images are not the subject. Optional here, required in a config. * * The asymmetry is deliberate and matches `ignore`. A library caller composing * this by hand has their reason in the code that composed it; an operator * writing a rule into a file that outlives them does not, and six months later * the only question anyone asks is whether it is still true. A rule that cannot * answer gets kept out of superstition. */ readonly reason?: string; /** Glob over the request URL: `**\/hero/**`, `https://cdn.example/**`. */ readonly url?: string; /** * Intrinsic `width × height`, at or above which this rule applies. * * Intrinsic rather than rendered, because rendered is a DOM fact and this is * not the DOM. The two differ, and the direction they differ in is safe: an * image displayed small but shipped large is still a large *download* whose * bytes still move under a CDN, so treating it by what was sent is the more * conservative reading. */ readonly minPixels?: number; /** * Intrinsic `width × height`, at or below which this rule applies. * * The upper bound exists so the lower one can be used without qualification. * A chevron, a checkmark, a status dot — small images that carry the meaning a * layout assertion is *about* — are kept by never matching a `minPixels` rule, * and this is here for the opposite policy, where a project wants its icon set * pinned and its photography ignored. */ readonly maxPixels?: number; } /** Intrinsic dimensions, in image pixels. */ export interface ImageSize { readonly width: number; readonly height: number; } /** * Why a rule list is unusable, or `null`. * * Returned rather than thrown so the caller decides whether an unusable rule is * an operator error at config time or a diagnostic at run time. Both callers * exist. */ export declare function blankRuleError(rules: readonly BlankRule[]): string | null; /** The first rule that applies to this image, or `null`. */ export declare function blankRuleFor(rules: readonly BlankRule[], url: string, size: ImageSize): BlankRule | null; /** * Whether any rule names this URL, ignoring size. * * For the one case where a rule *looks* applied and is not: an image whose * header this cannot measure. Size-only rules are excluded deliberately — a * `minPixels` rule that skipped an unmeasurable SVG has behaved exactly as * written, and warning about it on every page would bury the case where an * operator aimed a path at a file and nothing happened. */ export declare function blankUrlAimedAt(rules: readonly BlankRule[], url: string): boolean; /** * What the environment key records for a blanked asset. * * **The dimensions are in it, and leaving them out would be a false * `unchanged`.** The whole saving of blanking is that a changed illustration no * longer moves the key — but a *resized* illustration moves the layout, and a * key that said only `blank:illustrations` would settle every subject it appears * on without rendering, against a page whose columns have shifted. So the value * carries exactly what survived the substitution: which rule did it, and what * size the box still is. */ export declare function blankKey(rule: BlankRule, size: ImageSize): string; /** * Intrinsic dimensions from a container header, or `null` for a format this does * not read. * * Headers only — no decoder, no dependency, in the same spirit as * [`gif.ts`](gif.ts) and for the same reason: this package is defined by * requiring a browser, and acquiring an image codec to read four integers would * make every consumer of it pay for one. * * SVG is `null` on purpose rather than by omission. An SVG's intrinsic size is a * function of `width`, `height`, `viewBox` and the box it is placed in, and the * cases where all three disagree are exactly the cases where guessing wrong * moves the layout. AVIF and JPEG 2000 are `null` because reading them means * walking an ISO-BMFF box tree, which is a decoder by another name. */ export declare function imageSize(bytes: Uint8Array): ImageSize | null; /** * A fully transparent PNG of exactly these dimensions. * * One-bit palette rather than 8-bit RGBA, which is not a micro-optimization: a * 2000×2000 RGBA canvas is sixteen megabytes of zeroes to allocate and hand to * `deflate` for every blanked image on every subject, and the same picture as a * one-bit palette is half a megabyte. Both compress to nothing; only one of them * has to exist in memory first. * * Transparency comes from `tRNS` giving the single palette entry an alpha of * zero, which is the smallest legal way to say "none of this is there". */ export declare function blankPng(width: number, height: number): Buffer; //# sourceMappingURL=blank.d.ts.map