@ploaness/governance 1.12.0 → 1.13.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 (109) hide show
  1. package/dist/a11y-focus-guard.d.ts +18 -0
  2. package/dist/a11y-focus-guard.d.ts.map +1 -0
  3. package/dist/a11y-focus-guard.js +17 -0
  4. package/dist/a11y-focus-guard.js.map +1 -0
  5. package/dist/a11y-hit-target.d.ts +7 -1
  6. package/dist/a11y-hit-target.d.ts.map +1 -1
  7. package/dist/a11y-hit-target.js +11 -0
  8. package/dist/a11y-hit-target.js.map +1 -1
  9. package/dist/asset-policy.d.ts +8 -4
  10. package/dist/asset-policy.d.ts.map +1 -1
  11. package/dist/asset-policy.js +28 -19
  12. package/dist/asset-policy.js.map +1 -1
  13. package/dist/browser-discovery.d.ts +116 -0
  14. package/dist/browser-discovery.d.ts.map +1 -0
  15. package/dist/browser-discovery.js +161 -0
  16. package/dist/browser-discovery.js.map +1 -0
  17. package/dist/browser-exemptions.d.ts +107 -0
  18. package/dist/browser-exemptions.d.ts.map +1 -0
  19. package/dist/browser-exemptions.js +163 -0
  20. package/dist/browser-exemptions.js.map +1 -0
  21. package/dist/browser-findings.d.ts +54 -0
  22. package/dist/browser-findings.d.ts.map +1 -0
  23. package/dist/browser-findings.js +52 -0
  24. package/dist/browser-findings.js.map +1 -0
  25. package/dist/browser-health.d.ts +45 -0
  26. package/dist/browser-health.d.ts.map +1 -0
  27. package/dist/browser-health.js +80 -0
  28. package/dist/browser-health.js.map +1 -0
  29. package/dist/browser-rules.d.ts +169 -0
  30. package/dist/browser-rules.d.ts.map +1 -0
  31. package/dist/browser-rules.js +345 -0
  32. package/dist/browser-rules.js.map +1 -0
  33. package/dist/browser-run.d.ts +110 -0
  34. package/dist/browser-run.d.ts.map +1 -0
  35. package/dist/browser-run.js +125 -0
  36. package/dist/browser-run.js.map +1 -0
  37. package/dist/browser-viewports.d.ts +77 -0
  38. package/dist/browser-viewports.d.ts.map +1 -0
  39. package/dist/browser-viewports.js +139 -0
  40. package/dist/browser-viewports.js.map +1 -0
  41. package/dist/change-scope.d.ts +158 -0
  42. package/dist/change-scope.d.ts.map +1 -0
  43. package/dist/change-scope.js +340 -0
  44. package/dist/change-scope.js.map +1 -0
  45. package/dist/cloudfront-functions.d.ts +34 -0
  46. package/dist/cloudfront-functions.d.ts.map +1 -0
  47. package/dist/cloudfront-functions.js +124 -0
  48. package/dist/cloudfront-functions.js.map +1 -0
  49. package/dist/generated-denial.d.ts +4 -1
  50. package/dist/generated-denial.d.ts.map +1 -1
  51. package/dist/generated-denial.js +10 -3
  52. package/dist/generated-denial.js.map +1 -1
  53. package/dist/index.d.ts +12 -0
  54. package/dist/index.d.ts.map +1 -1
  55. package/dist/index.js +12 -0
  56. package/dist/index.js.map +1 -1
  57. package/dist/javascript-runtimes.d.ts +36 -0
  58. package/dist/javascript-runtimes.d.ts.map +1 -0
  59. package/dist/javascript-runtimes.js +40 -0
  60. package/dist/javascript-runtimes.js.map +1 -0
  61. package/dist/jsx-accessibility-scope.d.ts.map +1 -1
  62. package/dist/jsx-accessibility-scope.js +3 -0
  63. package/dist/jsx-accessibility-scope.js.map +1 -1
  64. package/dist/layout-boxes.d.ts.map +1 -1
  65. package/dist/layout-boxes.js +12 -14
  66. package/dist/layout-boxes.js.map +1 -1
  67. package/dist/layout-clip.d.ts +24 -0
  68. package/dist/layout-clip.d.ts.map +1 -0
  69. package/dist/layout-clip.js +82 -0
  70. package/dist/layout-clip.js.map +1 -0
  71. package/dist/layout-defects.d.ts +47 -4
  72. package/dist/layout-defects.d.ts.map +1 -1
  73. package/dist/layout-defects.js +94 -76
  74. package/dist/layout-defects.js.map +1 -1
  75. package/dist/layout-snapshot.d.ts +60 -2
  76. package/dist/layout-snapshot.d.ts.map +1 -1
  77. package/dist/layout-snapshot.js +65 -1
  78. package/dist/layout-snapshot.js.map +1 -1
  79. package/dist/layout-text.d.ts +4 -1
  80. package/dist/layout-text.d.ts.map +1 -1
  81. package/dist/layout-text.js +56 -7
  82. package/dist/layout-text.js.map +1 -1
  83. package/dist/route-map.d.ts +13 -0
  84. package/dist/route-map.d.ts.map +1 -1
  85. package/dist/route-map.js +13 -6
  86. package/dist/route-map.js.map +1 -1
  87. package/dist/run-plan.d.ts +27 -3
  88. package/dist/run-plan.d.ts.map +1 -1
  89. package/dist/run-plan.js +20 -3
  90. package/dist/run-plan.js.map +1 -1
  91. package/dist/settings-layering.d.ts.map +1 -1
  92. package/dist/settings-layering.js +6 -4
  93. package/dist/settings-layering.js.map +1 -1
  94. package/dist/settings.d.ts +32 -0
  95. package/dist/settings.d.ts.map +1 -1
  96. package/dist/settings.js +51 -12
  97. package/dist/settings.js.map +1 -1
  98. package/dist/terraform-blocks.d.ts +13 -0
  99. package/dist/terraform-blocks.d.ts.map +1 -0
  100. package/dist/terraform-blocks.js +27 -0
  101. package/dist/terraform-blocks.js.map +1 -0
  102. package/dist/terraform-policy.d.ts.map +1 -1
  103. package/dist/terraform-policy.js +3 -14
  104. package/dist/terraform-policy.js.map +1 -1
  105. package/dist/wiring-policy.d.ts +5 -2
  106. package/dist/wiring-policy.d.ts.map +1 -1
  107. package/dist/wiring-policy.js +16 -6
  108. package/dist/wiring-policy.js.map +1 -1
  109. package/package.json +1 -1
@@ -0,0 +1,125 @@
1
+ // How a browser verification run is made reproducible and bounded: the clock it runs at, the pinned
2
+ // browsers it connects to, the engines it measures in, and the time each pass may take.
3
+ /** The variable naming the instant the application's clock starts at, or absent for the real clock. */
4
+ export const CLOCK_ENVIRONMENT_VARIABLE = 'PLOANESS_CLOCK';
5
+ /**
6
+ * The instant every verification run starts its clocks at.
7
+ *
8
+ * Far in the future on purpose. The browser's cookie store keeps real time, so a shifted server that
9
+ * believed it was earlier than now would set absolute cookie expiries the browser already considers
10
+ * past, and every sign-in would fail. Noon, so that a run lasting hours still sees one date.
11
+ */
12
+ export const DEFAULT_CLOCK_INSTANT = '2100-01-01T12:00:00.000Z';
13
+ /** The clock setting that leaves every clock real. */
14
+ export const REAL_CLOCK = 'real';
15
+ const ISO_INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2}(?:\.\d{1,3})?)?(?:Z|[+-]\d{2}:\d{2})$/u;
16
+ /**
17
+ * The clock a project declared: an ISO instant, `"real"`, or the default instant for anything else.
18
+ * @param declared the raw `ploaness.clock` value.
19
+ * @returns the ISO instant to start at, or `"real"`.
20
+ */
21
+ export const clockOf = (declared) => {
22
+ if (declared === REAL_CLOCK) {
23
+ return REAL_CLOCK;
24
+ }
25
+ return typeof declared === 'string' && ISO_INSTANT.test(declared) && !Number.isNaN(Date.parse(declared))
26
+ ? new Date(Date.parse(declared)).toISOString()
27
+ : DEFAULT_CLOCK_INSTANT;
28
+ };
29
+ /**
30
+ * The time a shifted clock reads: the instant it started at, advanced by the real time since.
31
+ * @param instantMs the instant the clock started at.
32
+ * @param startedMs the real time it started.
33
+ * @param realMs the real time now.
34
+ * @returns the shifted time now.
35
+ */
36
+ export const shiftedTime = (instantMs, startedMs, realMs) => instantMs + (realMs - startedMs);
37
+ /** The variable naming the pinned browser server the suite connects to. */
38
+ export const BROWSER_ENDPOINT_ENVIRONMENT_VARIABLE = 'PLOANESS_BROWSER_ENDPOINT';
39
+ /**
40
+ * The pinned Playwright image whose browsers a verification run measures in.
41
+ * @param version the `@playwright/test` version ploaness pins.
42
+ * @returns the image reference.
43
+ */
44
+ export const playwrightImageOf = (version) => `mcr.microsoft.com/playwright:v${version}-noble`;
45
+ /** The engines a verification run measures in. Outside verification only the first runs. */
46
+ export const BROWSER_ENGINES = ['chromium', 'firefox', 'webkit'];
47
+ // What one unit of each pass's work may cost, in milliseconds. A unit is what the pass multiplies:
48
+ // a route for discovery, a page at one viewport for the passes that load pages, a control at one
49
+ // viewport for the interaction pass, a width for the sweep.
50
+ const ALLOWANCES_MS = {
51
+ discovery: 30_000,
52
+ resting: 12_000,
53
+ interaction: 5000,
54
+ disclosures: 8000,
55
+ 'text-stress': 15_000,
56
+ 'width-sweep': 2000,
57
+ 'dark-scheme': 10_000,
58
+ engine: 15_000,
59
+ };
60
+ // What a pass costs before its first unit: starting the browser and loading its plan.
61
+ const PASS_OVERHEAD_MS = 60_000;
62
+ /** The rule a pass that ran out of time reports. */
63
+ export const PASS_BUDGET_RULE = 'pass-budget';
64
+ /**
65
+ * The time a pass may take, derived from the work it discovered.
66
+ * @param pass the pass.
67
+ * @param units how many units of work it has.
68
+ * @returns its budget in milliseconds.
69
+ */
70
+ export const passBudgetMs = (pass, units) => PASS_OVERHEAD_MS + ALLOWANCES_MS[pass] * Math.max(0, units);
71
+ // Every sweep test loads its pages fresh and judges each one alone, so which worker runs it, and what
72
+ // runs beside it, cannot change a verdict. The project's own specs are held to one worker separately,
73
+ // because they may share database state.
74
+ /** The workers a browser run spreads the sweep's tests over. */
75
+ export const SWEEP_WORKERS = 4;
76
+ /** How many tests each sweep pass is split into, so its parts run on parallel workers. */
77
+ export const SWEEP_SHARDS = 4;
78
+ /**
79
+ * The time one part of a pass may take. The parts take the pass's items from one queue, so each does an
80
+ * equal share of the work whatever items it happens to take.
81
+ * @param pass the pass.
82
+ * @param units how many units of work the whole pass has.
83
+ * @param shard the part.
84
+ * @returns its budget in milliseconds.
85
+ */
86
+ export const partBudgetMs = (pass, units, shard) => passBudgetMs(pass, Math.ceil(units / shard.count));
87
+ /** Where the container sees the host's Playwright, mounted read-only. */
88
+ const CONTAINER_PLAYWRIGHT = '/ploaness/playwright-core';
89
+ /** The variable telling the suite its pinned browsers reach the application only through the tunnel. */
90
+ export const BROWSER_TUNNEL_ENVIRONMENT_VARIABLE = 'PLOANESS_BROWSER_TUNNEL';
91
+ /**
92
+ * The `docker` arguments that start the pinned browser server.
93
+ *
94
+ * The container runs the host's own `playwright-core` against the image's browsers, so nothing is
95
+ * fetched at test time and the protocol on both ends is one version. It listens on loopback only: on the
96
+ * host's own when the container shares the host network, or on a port published to it.
97
+ * @param server the server to start.
98
+ * @returns the arguments after `docker`.
99
+ */
100
+ export const pinnedBrowserServerArguments = (server) => [
101
+ 'run',
102
+ '--detach',
103
+ '--rm',
104
+ '--init',
105
+ '--ipc=host',
106
+ `--name=${server.name}`,
107
+ server.isHostNetwork ? '--network=host' : `--publish=127.0.0.1:${String(server.port)}:${String(server.port)}`,
108
+ `--volume=${server.playwrightCore}:${CONTAINER_PLAYWRIGHT}:ro`,
109
+ '--workdir=/tmp',
110
+ server.image,
111
+ 'node',
112
+ `${CONTAINER_PLAYWRIGHT}/cli.js`,
113
+ 'run-server',
114
+ `--port=${String(server.port)}`,
115
+ server.isHostNetwork ? '--host=127.0.0.1' : '--host=0.0.0.0',
116
+ `--path=/${server.secret}`,
117
+ ];
118
+ /**
119
+ * The endpoint the suite connects to.
120
+ * @param port the published port.
121
+ * @param secret the path the server answers on.
122
+ * @returns the WebSocket URL.
123
+ */
124
+ export const pinnedBrowserEndpoint = (port, secret) => `ws://127.0.0.1:${String(port)}/${secret}`;
125
+ //# sourceMappingURL=browser-run.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-run.js","sourceRoot":"","sources":["../src/browser-run.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,77 @@
1
+ import type { FindingViewport } from './browser-findings.js';
2
+ /**
3
+ * The viewports every page is loaded at, each a fresh load: a phone, a tablet and a desktop. The width
4
+ * sweep measures overflow at every width between them, and each page adds its own breakpoint edges. A
5
+ * project may add more, never remove any.
6
+ */
7
+ export declare const BASELINE_VIEWPORTS: readonly FindingViewport[];
8
+ /** The phone and desktop viewports the text-stress passes and the axe scans run at. */
9
+ export declare const STRESS_VIEWPORTS: readonly FindingViewport[];
10
+ /**
11
+ * The viewports the interaction and disclosure passes drive a location at. A story renders one component
12
+ * apart from any page, and its layout at every width is already judged at rest and by the width sweep, so
13
+ * its controls are driven only at the desktop, where hover is judged too. A route is driven at both.
14
+ * @param isStory whether the location is a story.
15
+ * @returns the viewports to drive it at.
16
+ */
17
+ export declare const drivenViewports: (isStory: boolean) => readonly FindingViewport[];
18
+ /** The narrowest width the sweep measures: the WCAG 1.4.10 reflow bound. */
19
+ export declare const SWEEP_MIN_WIDTH: number;
20
+ /** The widest width the sweep measures. */
21
+ export declare const SWEEP_MAX_WIDTH: number;
22
+ /**
23
+ * The step of the width sweep, in CSS pixels. A failing step is bisected to the exact pixel, so the step only
24
+ * bounds how narrow a band of widths a defect can hide in between two passing steps.
25
+ */
26
+ export declare const SWEEP_STEP: number;
27
+ /** The height the width sweep holds while it resizes. */
28
+ export declare const SWEEP_HEIGHT: number;
29
+ /**
30
+ * The widths on both sides of every width condition in a set of media queries.
31
+ *
32
+ * `min-width: N` adds N - 1 and N, `max-width: N` adds N and N + 1, and the range syntax is read the
33
+ * same way. Only `px`, `rem` and `em` are understood, at 16 px to the rem. Widths outside the sweep's
34
+ * range are left out.
35
+ * @param mediaTexts the `media.mediaText` of every `@media` rule on the page.
36
+ * @returns each edge width once, ascending.
37
+ */
38
+ export declare const breakpointEdges: (mediaTexts: readonly string[]) => readonly number[];
39
+ /**
40
+ * The viewports a page is loaded at: the baseline, its breakpoint edges, and the project's additions.
41
+ * @param edges the page's breakpoint edges.
42
+ * @param declared the viewports the project added.
43
+ * @returns each viewport once, ascending by width.
44
+ */
45
+ export declare const pageViewports: (edges: readonly number[], declared: readonly FindingViewport[]) => readonly FindingViewport[];
46
+ /**
47
+ * The widths the sweep steps through.
48
+ * @returns every 32nd width from 320 to 1920, both ends included.
49
+ */
50
+ export declare const sweepWidths: () => readonly number[];
51
+ /**
52
+ * The next width to measure while bisecting between a width that holds and one that fails.
53
+ * @param holds a width at which the defect is absent.
54
+ * @param fails a width at which it is present.
55
+ * @returns the width halfway between, or undefined once the two are adjacent.
56
+ */
57
+ export declare const bisectionWidth: (holds: number, fails: number) => number | undefined;
58
+ /** A defect measured at the widths it holds at. */
59
+ export interface SweptDefect {
60
+ /** The defect, as a message without its width. */
61
+ readonly message: string;
62
+ /** Every width it was measured at, sampled or bisected. */
63
+ readonly widths: readonly number[];
64
+ }
65
+ /** An unbroken run of widths a defect was present at. */
66
+ export interface WidthRange {
67
+ readonly from: number;
68
+ readonly to: number;
69
+ }
70
+ /**
71
+ * Group the widths a defect was found at into runs, given every width that was measured.
72
+ * @param failing the widths the defect was present at.
73
+ * @param measured every width measured, failing or not.
74
+ * @returns the runs, each bounded by measured widths at which the defect was present.
75
+ */
76
+ export declare const failingRanges: (failing: readonly number[], measured: readonly number[]) => readonly WidthRange[];
77
+ //# sourceMappingURL=browser-viewports.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-viewports.d.ts","sourceRoot":"","sources":["../src/browser-viewports.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAA;AAE5D;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,EAAE,SAAS,eAAe,EAIxD,CAAA;AAED,uFAAuF;AACvF,eAAO,MAAM,gBAAgB,EAAE,SAAS,eAAe,EAGtD,CAAA;AAED;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,OAAO,KAAG,SAAS,eAAe,EAClB,CAAA;AAEzD,4EAA4E;AAC5E,eAAO,MAAM,eAAe,EAAE,MAAY,CAAA;AAE1C,2CAA2C;AAC3C,eAAO,MAAM,eAAe,EAAE,MAAa,CAAA;AAE3C;;;GAGG;AACH,eAAO,MAAM,UAAU,EAAE,MAAW,CAAA;AAEpC,yDAAyD;AACzD,eAAO,MAAM,YAAY,EAAE,MAAY,CAAA;AA2DvC;;;;;;;;GAQG;AACH,eAAO,MAAM,eAAe,GAAI,YAAY,SAAS,MAAM,EAAE,KAAG,SAAS,MAAM,EASX,CAAA;AAYpE;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GACxB,OAAO,SAAS,MAAM,EAAE,EACxB,UAAU,SAAS,eAAe,EAAE,KACnC,SAAS,eAAe,EAY1B,CAAA;AAED;;;GAGG;AACH,eAAO,MAAM,WAAW,QAAO,SAAS,MAAM,EAI3C,CAAA;AAIH;;;;;GAKG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,MAAM,EAAE,OAAO,MAAM,KAAG,MAAM,GAAG,SACU,CAAA;AAEjF,mDAAmD;AACnD,MAAM,WAAW,WAAW;IAC1B,kDAAkD;IAClD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,2DAA2D;IAC3D,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAA;CACnC;AAED,yDAAyD;AACzD,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CACpB;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,GAAI,SAAS,SAAS,MAAM,EAAE,EAAE,UAAU,SAAS,MAAM,EAAE,KAAG,SAAS,UAAU,EAe1G,CAAA"}
@@ -0,0 +1,139 @@
1
+ /**
2
+ * The viewports every page is loaded at, each a fresh load: a phone, a tablet and a desktop. The width
3
+ * sweep measures overflow at every width between them, and each page adds its own breakpoint edges. A
4
+ * project may add more, never remove any.
5
+ */
6
+ export const BASELINE_VIEWPORTS = [
7
+ { width: 390, height: 844 },
8
+ { width: 768, height: 1024 },
9
+ { width: 1280, height: 800 },
10
+ ];
11
+ /** The phone and desktop viewports the text-stress passes and the axe scans run at. */
12
+ export const STRESS_VIEWPORTS = [
13
+ { width: 390, height: 844 },
14
+ { width: 1280, height: 800 },
15
+ ];
16
+ /**
17
+ * The viewports the interaction and disclosure passes drive a location at. A story renders one component
18
+ * apart from any page, and its layout at every width is already judged at rest and by the width sweep, so
19
+ * its controls are driven only at the desktop, where hover is judged too. A route is driven at both.
20
+ * @param isStory whether the location is a story.
21
+ * @returns the viewports to drive it at.
22
+ */
23
+ export const drivenViewports = (isStory) => isStory ? STRESS_VIEWPORTS.slice(-1) : STRESS_VIEWPORTS;
24
+ /** The narrowest width the sweep measures: the WCAG 1.4.10 reflow bound. */
25
+ export const SWEEP_MIN_WIDTH = 320;
26
+ /** The widest width the sweep measures. */
27
+ export const SWEEP_MAX_WIDTH = 1920;
28
+ /**
29
+ * The step of the width sweep, in CSS pixels. A failing step is bisected to the exact pixel, so the step only
30
+ * bounds how narrow a band of widths a defect can hide in between two passing steps.
31
+ */
32
+ export const SWEEP_STEP = 32;
33
+ /** The height the width sweep holds while it resizes. */
34
+ export const SWEEP_HEIGHT = 900;
35
+ const REM_PIXELS = 16;
36
+ const NUMBER = String.raw `(\d+(?:\.\d+)?)(px|rem|em)`;
37
+ const pixelsOf = (value, unit) => Number(value) * (unit === 'px' ? 1 : REM_PIXELS);
38
+ const MIRRORED = { '>=': '<=', '>': '<', '<=': '>=', '<': '>' };
39
+ const COMPARISONS = new Set(Object.keys(MIRRORED));
40
+ const isComparison = (text) => COMPARISONS.has(text);
41
+ const legacyConditions = (media) => [...media.matchAll(new RegExp(String.raw `\(\s*(min|max)-width\s*:\s*${NUMBER}\s*\)`, 'giu'))].map((match) => ({
42
+ comparison: String(match[1]).toLowerCase() === 'min' ? '>=' : '<=',
43
+ pixels: pixelsOf(String(match[2]), String(match[3]).toLowerCase()),
44
+ }));
45
+ const rangeConditions = (media) => {
46
+ const after = [
47
+ ...media.matchAll(new RegExp(String.raw `(?<![\w-])width\s*(>=|<=|>|<)\s*${NUMBER}`, 'giu')),
48
+ ].flatMap((match) => {
49
+ const comparison = String(match[1]);
50
+ return isComparison(comparison)
51
+ ? [{ comparison, pixels: pixelsOf(String(match[2]), String(match[3]).toLowerCase()) }]
52
+ : [];
53
+ });
54
+ const before = [
55
+ ...media.matchAll(new RegExp(String.raw `${NUMBER}\s*(>=|<=|>|<)\s*width(?![\w-])`, 'giu')),
56
+ ].flatMap((match) => {
57
+ const comparison = String(match[3]);
58
+ return isComparison(comparison)
59
+ ? [{ comparison: MIRRORED[comparison], pixels: pixelsOf(String(match[1]), String(match[2]).toLowerCase()) }]
60
+ : [];
61
+ });
62
+ return [...after, ...before];
63
+ };
64
+ // The last width on one side of the edge and the first on the other, both whole pixels.
65
+ const edgeWidths = (condition) => {
66
+ const { comparison, pixels } = condition;
67
+ if (comparison === '>=' || comparison === '<') {
68
+ return [Math.ceil(pixels) - 1, Math.ceil(pixels)];
69
+ }
70
+ return [Math.floor(pixels), Math.floor(pixels) + 1];
71
+ };
72
+ /**
73
+ * The widths on both sides of every width condition in a set of media queries.
74
+ *
75
+ * `min-width: N` adds N - 1 and N, `max-width: N` adds N and N + 1, and the range syntax is read the
76
+ * same way. Only `px`, `rem` and `em` are understood, at 16 px to the rem. Widths outside the sweep's
77
+ * range are left out.
78
+ * @param mediaTexts the `media.mediaText` of every `@media` rule on the page.
79
+ * @returns each edge width once, ascending.
80
+ */
81
+ export const breakpointEdges = (mediaTexts) => [
82
+ ...new Set(mediaTexts
83
+ .flatMap((media) => [...legacyConditions(media), ...rangeConditions(media)])
84
+ .flatMap((condition) => edgeWidths(condition))),
85
+ ]
86
+ .filter((width) => width >= SWEEP_MIN_WIDTH && width <= SWEEP_MAX_WIDTH)
87
+ .toSorted((left, right) => left - right);
88
+ const heightFor = (width) => BASELINE_VIEWPORTS.reduce((nearest, viewport) => Math.abs(viewport.width - width) < Math.abs(nearest.width - width) ? viewport : nearest, { width: Number.POSITIVE_INFINITY, height: SWEEP_HEIGHT }).height;
89
+ const isSame = (left, right) => left.width === right.width && left.height === right.height;
90
+ /**
91
+ * The viewports a page is loaded at: the baseline, its breakpoint edges, and the project's additions.
92
+ * @param edges the page's breakpoint edges.
93
+ * @param declared the viewports the project added.
94
+ * @returns each viewport once, ascending by width.
95
+ */
96
+ export const pageViewports = (edges, declared) => {
97
+ const all = [
98
+ ...BASELINE_VIEWPORTS,
99
+ ...edges.map((width) => ({ width, height: heightFor(width) })),
100
+ ...declared,
101
+ ];
102
+ return all
103
+ .filter((viewport, index) => all.findIndex((other) => isSame(other, viewport)) === index)
104
+ .toSorted((left, right) => left.width - right.width);
105
+ };
106
+ /**
107
+ * The widths the sweep steps through.
108
+ * @returns every 32nd width from 320 to 1920, both ends included.
109
+ */
110
+ export const sweepWidths = () => Array.from({ length: Math.floor((SWEEP_MAX_WIDTH - SWEEP_MIN_WIDTH) / SWEEP_STEP) + 1 }, (_unused, index) => SWEEP_MIN_WIDTH + index * SWEEP_STEP);
111
+ const HALVES = 2;
112
+ /**
113
+ * The next width to measure while bisecting between a width that holds and one that fails.
114
+ * @param holds a width at which the defect is absent.
115
+ * @param fails a width at which it is present.
116
+ * @returns the width halfway between, or undefined once the two are adjacent.
117
+ */
118
+ export const bisectionWidth = (holds, fails) => Math.abs(fails - holds) <= 1 ? undefined : Math.floor((holds + fails) / HALVES);
119
+ /**
120
+ * Group the widths a defect was found at into runs, given every width that was measured.
121
+ * @param failing the widths the defect was present at.
122
+ * @param measured every width measured, failing or not.
123
+ * @returns the runs, each bounded by measured widths at which the defect was present.
124
+ */
125
+ export const failingRanges = (failing, measured) => {
126
+ const failed = new Set(failing);
127
+ const ordered = [...new Set(measured)].toSorted((left, right) => left - right);
128
+ return ordered.reduce((ranges, width, index) => {
129
+ if (!failed.has(width)) {
130
+ return ranges;
131
+ }
132
+ const previous = ordered[index - 1];
133
+ const last = ranges.at(-1);
134
+ return last !== undefined && previous !== undefined && failed.has(previous)
135
+ ? [...ranges.slice(0, -1), { from: last.from, to: width }]
136
+ : [...ranges, { from: width, to: width }];
137
+ }, []);
138
+ };
139
+ //# sourceMappingURL=browser-viewports.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser-viewports.js","sourceRoot":"","sources":["../src/browser-viewports.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,158 @@
1
+ import { type DeclaredRoute } from './route-map.js';
2
+ /** Names the file that holds the change scope a `delta` run hands its browser sweep. */
3
+ export declare const SWEEP_SCOPE_ENVIRONMENT_VARIABLE: string;
4
+ /**
5
+ * Whether the browser sweep runs its engine pass in Firefox and WebKit as well as in Chromium. Only an
6
+ * unscoped run with the pinned browsers does: a `delta` run sweeps the change in Chromium alone.
7
+ * @param environment the runner's environment.
8
+ * @returns true when the other engines run.
9
+ */
10
+ export declare const coversEveryEngine: (environment: Readonly<Record<string, string | undefined>>) => boolean;
11
+ /**
12
+ * The remote whose default branch is the baseline: `origin` when there is one, otherwise the first.
13
+ * @param remotes the repository's remotes, as `git remote` lists them.
14
+ * @returns the remote, or undefined when the repository has none.
15
+ */
16
+ export declare const baselineRemote: (remotes: readonly string[]) => string | undefined;
17
+ /**
18
+ * The remote-tracking branch that stands for the remote's default branch.
19
+ * @param remote the remote.
20
+ * @param symbolicHead what `refs/remotes/<remote>/HEAD` points at, or undefined when it is not recorded.
21
+ * @returns a ref such as `origin/main`.
22
+ */
23
+ export declare const trackingBranch: (remote: string, symbolicHead: string | undefined) => string;
24
+ /** What Git said about the baseline. */
25
+ export interface BaselineFacts {
26
+ /** The remote-tracking default branch, or undefined when the repository has none. */
27
+ readonly branch: string | undefined;
28
+ readonly isShallow: boolean;
29
+ /** The merge-base of HEAD with the branch, or undefined when there is none. */
30
+ readonly mergeBase: string | undefined;
31
+ }
32
+ /** The commit a change is measured from, or why there is none. */
33
+ export type Baseline = {
34
+ readonly commit: string;
35
+ readonly branch: string;
36
+ } | {
37
+ readonly commit: undefined;
38
+ readonly problem: string;
39
+ };
40
+ /**
41
+ * Decide the commit a change is measured from. Local `main` is never it: a commit made on it would be
42
+ * its own baseline and never be checked. A shallow clone may lack the true merge-base, so it has none.
43
+ * @param facts what Git said.
44
+ * @returns the merge-base, or why the sweep has to be complete.
45
+ */
46
+ export declare const changeBaseline: (facts: BaselineFacts) => Baseline;
47
+ /** A file that differs from the baseline, relative to the repository root. */
48
+ export interface ChangedFile {
49
+ readonly path: string;
50
+ readonly isDeleted: boolean;
51
+ }
52
+ /** A route pattern or story file in scope, and why. */
53
+ export interface ScopedTarget {
54
+ readonly target: string;
55
+ readonly reason: string;
56
+ }
57
+ /** What a `delta` run hands its sweep: the change, and either everything or what the change reaches. */
58
+ export type SweepScope = {
59
+ readonly isComplete: true;
60
+ readonly baseline: string | undefined;
61
+ readonly branch: string | undefined;
62
+ readonly changed: readonly string[];
63
+ /** Why the sweep covers everything. */
64
+ readonly reasons: readonly string[];
65
+ } | {
66
+ readonly isComplete: false;
67
+ readonly baseline: string;
68
+ readonly branch: string;
69
+ readonly changed: readonly string[];
70
+ /** Declared route patterns, as `declaredRoutesOf` spells them. */
71
+ readonly routes: readonly ScopedTarget[];
72
+ /** Story files, relative to the application's root. */
73
+ readonly stories: readonly ScopedTarget[];
74
+ };
75
+ /** Everything deciding one application's scope needs. */
76
+ export interface ScopeInputs {
77
+ readonly baseline: Baseline;
78
+ readonly changed: readonly ChangedFile[];
79
+ /** The application's path in the repository, `.` at the root. */
80
+ readonly member: string;
81
+ readonly sourceRoots: readonly string[];
82
+ /** Every governed member's path. */
83
+ readonly members: readonly string[];
84
+ /**
85
+ * The members whose change reaches this application without an import: those it starts as auxiliary
86
+ * servers, such as a CMS it renders from, and those it depends on as packages.
87
+ */
88
+ readonly related: readonly string[];
89
+ /** The application's files, relative to its root. */
90
+ readonly files: readonly string[];
91
+ /** The application's import graph: each module, relative to its root, and the modules it imports. */
92
+ readonly graph: Readonly<Record<string, readonly string[]>>;
93
+ }
94
+ /**
95
+ * Decide what one application's browser sweep covers for a change.
96
+ *
97
+ * Everything, when there is no baseline, or when a changed file is a global input: a dependency or
98
+ * toolchain file, the application's manifest, TypeScript, Next.js, Tailwind or PostCSS configuration,
99
+ * request handling, an environment file, a public file, the Storybook configuration, a global
100
+ * stylesheet, the root layout, a file under a source root the import graph does not hold, or any file
101
+ * of a member the application runs beside or depends on. Otherwise the routes and stories whose entry
102
+ * modules reach a changed module. A file outside every source root, such as documentation or a
103
+ * workflow, and a file of an unrelated member, change no page.
104
+ * @param inputs the baseline, the change, and the application's members, files and graph.
105
+ * @returns the scope, with a reason for each route and story or for covering everything.
106
+ */
107
+ export declare const sweepScopeOf: (inputs: ScopeInputs) => SweepScope;
108
+ /**
109
+ * Whether a parsed value is a scope as `sweepScopeOf` produces it.
110
+ * @param value the parsed value.
111
+ * @returns whether it carries every field its kind has.
112
+ */
113
+ export declare const isSweepScope: (value: unknown) => value is SweepScope;
114
+ /**
115
+ * One line naming the level and what it covers.
116
+ * @param scope the scope.
117
+ * @returns the line.
118
+ */
119
+ export declare const scopeHeadline: (scope: SweepScope) => string;
120
+ /**
121
+ * Why the sweep scans a crawled route, or undefined when the change cannot reach it. A route no
122
+ * declaration renders is scanned, because what reaches it is unknown.
123
+ * @param scope the scope.
124
+ * @param declared every route the application's file tree declares.
125
+ * @param route the crawled address.
126
+ * @returns the reason, or undefined.
127
+ */
128
+ export declare const routeReason: (scope: SweepScope, declared: readonly DeclaredRoute[], route: string) => string | undefined;
129
+ /**
130
+ * The file a story is declared in, relative to the application's root.
131
+ * @param importPath the story's `importPath`, relative to the Storybook's own directory.
132
+ * @param directory that directory, relative to the application's root; `.` when it is the same.
133
+ * @returns the file.
134
+ */
135
+ export declare const storyFileOf: (importPath: string, directory: string) => string;
136
+ /**
137
+ * Why the sweep scans a story, or undefined when the change cannot reach it. A story declared outside
138
+ * the application, or in no file Storybook names, is scanned, because what reaches it is unknown.
139
+ * @param scope the scope.
140
+ * @param storyFile the story's file, from `storyFileOf`, or empty when Storybook names none.
141
+ * @returns the reason, or undefined.
142
+ */
143
+ export declare const storyReason: (scope: SweepScope, storyFile: string) => string | undefined;
144
+ /**
145
+ * The import graph in dependency-cruiser's JSON report, the analyzer the architecture gate runs.
146
+ * @param report the parsed report.
147
+ * @returns each module, relative to where the analyzer ran, and the modules it imports; or undefined
148
+ * when the value is not such a report.
149
+ */
150
+ export declare const importGraphOf: (report: unknown) => Readonly<Record<string, readonly string[]>> | undefined;
151
+ /**
152
+ * The scope as a reader sees it: the level and baseline, every changed file, and each route and story
153
+ * with the reason it is swept.
154
+ * @param scope the scope.
155
+ * @returns one line each, indented below the headline.
156
+ */
157
+ export declare const scopeLines: (scope: SweepScope) => readonly string[];
158
+ //# sourceMappingURL=change-scope.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"change-scope.d.ts","sourceRoot":"","sources":["../src/change-scope.ts"],"names":[],"mappings":"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"}