@fairfox/polly 0.78.0 → 0.80.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 (97) hide show
  1. package/dist/cli/polly.js +76 -3
  2. package/dist/cli/polly.js.map +3 -3
  3. package/dist/src/background/index.js.map +3 -3
  4. package/dist/src/background/message-router.js.map +3 -3
  5. package/dist/src/client/index.js +137 -32
  6. package/dist/src/client/index.js.map +6 -5
  7. package/dist/src/client/wrapper.d.ts +39 -2
  8. package/dist/src/elysia/index.js +22 -3
  9. package/dist/src/elysia/index.js.map +5 -5
  10. package/dist/src/elysia/route-match.d.ts +13 -0
  11. package/dist/src/index.d.ts +1 -1
  12. package/dist/src/index.js +12 -2
  13. package/dist/src/index.js.map +7 -7
  14. package/dist/src/mesh.js +28 -9
  15. package/dist/src/mesh.js.map +10 -9
  16. package/dist/src/peer.js +6 -2
  17. package/dist/src/peer.js.map +5 -5
  18. package/dist/src/polly-ui/Badge.d.ts +5 -0
  19. package/dist/src/polly-ui/Button.d.ts +31 -6
  20. package/dist/src/polly-ui/Dropdown.d.ts +6 -0
  21. package/dist/src/polly-ui/Select.d.ts +11 -1
  22. package/dist/src/polly-ui/TextInput.d.ts +30 -0
  23. package/dist/src/polly-ui/index.css +10 -0
  24. package/dist/src/polly-ui/index.js +81 -32
  25. package/dist/src/polly-ui/index.js.map +10 -10
  26. package/dist/src/polly-ui/styles.css +10 -0
  27. package/dist/src/shared/adapters/index.js.map +3 -3
  28. package/dist/src/shared/lib/context-helpers.js.map +3 -3
  29. package/dist/src/shared/lib/message-bus.js.map +3 -3
  30. package/dist/src/shared/lib/resource.js +11 -2
  31. package/dist/src/shared/lib/resource.js.map +6 -6
  32. package/dist/src/shared/lib/state.d.ts +20 -0
  33. package/dist/src/shared/lib/state.js +11 -1
  34. package/dist/src/shared/lib/state.js.map +5 -5
  35. package/dist/src/shared/state/app-state.js +10 -1
  36. package/dist/src/shared/state/app-state.js.map +5 -5
  37. package/dist/tools/init/src/cli.js +23 -2
  38. package/dist/tools/init/src/cli.js.map +4 -4
  39. package/dist/tools/init/templates/pwa/package.json.template +1 -1
  40. package/dist/tools/init/templates/pwa/src/service-worker.ts.template +26 -15
  41. package/dist/tools/init/templates/pwa/src/shared-worker.ts.template +13 -3
  42. package/dist/tools/init/templates/pwa/tsconfig.json.template +2 -2
  43. package/dist/tools/init/templates/pwa/tsconfig.worker.json.template +17 -0
  44. package/dist/tools/test/src/browser/index.js +5 -2
  45. package/dist/tools/test/src/browser/index.js.map +3 -3
  46. package/dist/tools/test/src/contrast/index.js +20 -15
  47. package/dist/tools/test/src/contrast/index.js.map +3 -3
  48. package/dist/tools/test/src/coverage-policy/cli.d.ts +19 -0
  49. package/dist/tools/test/src/coverage-policy/cli.js +339 -0
  50. package/dist/tools/test/src/coverage-policy/cli.js.map +13 -0
  51. package/dist/tools/test/src/coverage-policy/discover.d.ts +23 -0
  52. package/dist/tools/test/src/coverage-policy/enforce.d.ts +54 -0
  53. package/dist/tools/test/src/coverage-policy/index.d.ts +10 -0
  54. package/dist/tools/test/src/coverage-policy/index.js +242 -0
  55. package/dist/tools/test/src/coverage-policy/index.js.map +13 -0
  56. package/dist/tools/test/src/coverage-policy/mutate-targets.d.ts +30 -0
  57. package/dist/tools/test/src/coverage-policy/types.d.ts +35 -0
  58. package/dist/tools/test/src/e2e-cli/index.d.ts +10 -0
  59. package/dist/tools/test/src/e2e-cli/run-cli.d.ts +25 -0
  60. package/dist/tools/test/src/e2e-cli/with-temp-dir.d.ts +15 -0
  61. package/dist/tools/test/src/e2e-mesh/index.js +29 -8
  62. package/dist/tools/test/src/e2e-mesh/index.js.map +7 -6
  63. package/dist/tools/test/src/e2e-mesh/launch-peer.d.ts +7 -1
  64. package/dist/tools/test/src/e2e-mesh/wait-for-convergence.d.ts +8 -0
  65. package/dist/tools/test/src/e2e-relay/index.d.ts +12 -0
  66. package/dist/tools/test/src/e2e-relay/index.js +1421 -0
  67. package/dist/tools/test/src/e2e-relay/index.js.map +30 -0
  68. package/dist/tools/test/src/e2e-relay/wait-for-relay-convergence.d.ts +35 -0
  69. package/dist/tools/test/src/e2e-relay/with-repo-server.d.ts +33 -0
  70. package/dist/tools/test/src/e2e-shared/assert.d.ts +18 -0
  71. package/dist/tools/test/src/e2e-shared/contract.d.ts +40 -0
  72. package/dist/tools/test/src/e2e-shared/index.d.ts +3 -0
  73. package/dist/tools/test/src/e2e-shared/timeout-context.d.ts +17 -0
  74. package/dist/tools/test/src/index.d.ts +1 -0
  75. package/dist/tools/test/src/index.js +16 -1
  76. package/dist/tools/test/src/index.js.map +5 -4
  77. package/dist/tools/test/src/tiers/args.d.ts +23 -0
  78. package/dist/tools/test/src/tiers/cli.d.ts +2 -0
  79. package/dist/tools/test/src/tiers/cli.js +490 -0
  80. package/dist/tools/test/src/tiers/cli.js.map +16 -0
  81. package/dist/tools/test/src/tiers/detect.d.ts +12 -0
  82. package/dist/tools/test/src/tiers/discover.d.ts +2 -0
  83. package/dist/tools/test/src/tiers/engine.d.ts +3 -0
  84. package/dist/tools/test/src/tiers/index.d.ts +14 -0
  85. package/dist/tools/test/src/tiers/protocol.d.ts +10 -0
  86. package/dist/tools/test/src/tiers/reporter.d.ts +12 -0
  87. package/dist/tools/test/src/tiers/types.d.ts +94 -0
  88. package/dist/tools/test/src/tiers/worker.d.ts +2 -0
  89. package/dist/tools/test/src/tiers/worker.js +60 -0
  90. package/dist/tools/test/src/tiers/worker.js.map +12 -0
  91. package/dist/tools/verify/src/cli.js +165 -30
  92. package/dist/tools/verify/src/cli.js.map +7 -6
  93. package/dist/tools/verify/src/stryker/index.js +20 -11
  94. package/dist/tools/verify/src/stryker/index.js.map +3 -3
  95. package/dist/tools/visualize/src/cli.js +8 -5
  96. package/dist/tools/visualize/src/cli.js.map +4 -4
  97. package/package.json +26 -6
@@ -2,9 +2,9 @@
2
2
  "version": 3,
3
3
  "sources": ["../tools/test/src/browser/harness.ts"],
4
4
  "sourcesContent": [
5
- "/**\n * Browser-side test harness for Polly applications.\n *\n * Provides describe/test/expect/done that run inside a Puppeteer-launched\n * browser tab and record results on window.__testResults for the Node-side\n * runner to collect. Matchers cover both value assertions and DOM element\n * assertions so that Preact component tests and WebRTC adapter tests use\n * the same harness.\n *\n * @example\n * ```typescript\n * import { describe, test, expect, done, flush, cleanup } from \"@fairfox/polly/test/browser\";\n *\n * const app = document.getElementById(\"app\")!;\n *\n * describe(\"my feature\", () => {\n * test(\"renders correctly\", async () => {\n * render(<MyComponent />, app);\n * await flush();\n * expect(app.querySelector(\"h1\")).toHaveTextContent(\"Hello\");\n * cleanup(app);\n * });\n * });\n *\n * done();\n * ```\n */\n\ninterface TestResult {\n name: string;\n passed: boolean;\n error?: string;\n}\n\nconst results: TestResult[] = [];\nconst suites: Array<{\n name: string;\n tests: Array<{ name: string; fn: () => Promise<void> | void }>;\n}> = [];\n\nexport function describe(name: string, fn: () => void): void {\n suites.push({ name, tests: [] });\n fn();\n}\n\nexport function test(name: string, fn: () => Promise<void> | void): void {\n const suite = suites[suites.length - 1];\n if (suite) {\n suite.tests.push({ name, fn });\n }\n}\n\nfunction assertElement(value: unknown): Element {\n if (!(value instanceof Element)) {\n throw new Error(`Expected an Element, got ${typeof value}: ${String(value)}`);\n }\n return value;\n}\n\nexport function expect<T>(actual: T) {\n return {\n // Value matchers\n toBe(expected: T) {\n if (actual !== expected) {\n throw new Error(`Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);\n }\n },\n toEqual(expected: T) {\n if (JSON.stringify(actual) !== JSON.stringify(expected)) {\n throw new Error(`Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);\n }\n },\n toContain(sub: string) {\n if (!String(actual).includes(sub)) {\n throw new Error(`Expected \"${String(actual)}\" to contain \"${sub}\"`);\n }\n },\n toBeTruthy() {\n if (!actual) throw new Error(`Expected truthy, got ${String(actual)}`);\n },\n toBeFalsy() {\n if (actual) throw new Error(`Expected falsy, got ${String(actual)}`);\n },\n toBeNull() {\n if (actual !== null) throw new Error(`Expected null, got ${String(actual)}`);\n },\n toBeDefined() {\n if (actual === undefined || actual === null) {\n throw new Error(`Expected value to be defined, got ${String(actual)}`);\n }\n },\n toBeUndefined() {\n if (actual !== undefined) {\n throw new Error(`Expected undefined, got ${JSON.stringify(actual)}`);\n }\n },\n toBeGreaterThan(expected: number) {\n if (typeof actual !== \"number\" || actual <= expected) {\n throw new Error(`Expected ${String(actual)} to be greater than ${expected}`);\n }\n },\n toHaveLength(expected: number) {\n const obj = actual;\n const len = obj && typeof obj === \"object\" && \"length\" in obj ? Number(obj.length) : -1;\n if (len !== expected) throw new Error(`Expected length ${expected}, got ${len}`);\n },\n toExist() {\n if (actual == null) throw new Error(`Expected value to exist, got ${String(actual)}`);\n },\n\n // DOM element matchers\n toHaveTextContent(expected: string) {\n const el = assertElement(actual);\n if (!el.textContent?.includes(expected)) {\n throw new Error(\n `Expected text content to include ${JSON.stringify(expected)}, got ${JSON.stringify(el.textContent)}`\n );\n }\n },\n toBeChecked() {\n const el = assertElement(actual);\n if (!(el instanceof HTMLInputElement) || !el.checked) {\n throw new Error(\"Expected element to be checked\");\n }\n },\n toBeDisabled() {\n const el = assertElement(actual);\n if (!el.hasAttribute(\"disabled\") && el.getAttribute(\"aria-disabled\") !== \"true\") {\n throw new Error(\"Expected element to be disabled\");\n }\n },\n toHaveValue(expected: string) {\n const el = assertElement(actual);\n const inputEl =\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement ? el : null;\n if (!inputEl || inputEl.value !== expected) {\n throw new Error(\n `Expected value ${JSON.stringify(expected)}, got ${JSON.stringify(inputEl?.value ?? \"(not an input)\")}`\n );\n }\n },\n toHaveAttribute(name: string, value?: string) {\n const el = assertElement(actual);\n if (!el.hasAttribute(name)) {\n throw new Error(`Expected element to have attribute \"${name}\"`);\n }\n if (value !== undefined && el.getAttribute(name) !== value) {\n throw new Error(\n `Expected attribute \"${name}\" to be ${JSON.stringify(value)}, got ${JSON.stringify(el.getAttribute(name))}`\n );\n }\n },\n\n // .not variants\n not: {\n toBe(expected: T) {\n if (actual === expected) {\n throw new Error(`Expected value NOT to be ${JSON.stringify(expected)}`);\n }\n },\n toEqual(expected: T) {\n if (JSON.stringify(actual) === JSON.stringify(expected)) {\n throw new Error(`Expected value NOT to equal ${JSON.stringify(expected)}`);\n }\n },\n toContain(sub: string) {\n if (String(actual).includes(sub)) {\n throw new Error(`Expected \"${String(actual)}\" NOT to contain \"${sub}\"`);\n }\n },\n toBeNull() {\n if (actual === null) throw new Error(\"Expected value NOT to be null\");\n },\n toExist() {\n if (actual != null) throw new Error(`Expected value NOT to exist, got ${String(actual)}`);\n },\n toBeChecked() {\n const el = assertElement(actual);\n if (el instanceof HTMLInputElement && el.checked) {\n throw new Error(\"Expected element NOT to be checked\");\n }\n },\n toBeDisabled() {\n const el = assertElement(actual);\n if (el.hasAttribute(\"disabled\") || el.getAttribute(\"aria-disabled\") === \"true\") {\n throw new Error(\"Expected element NOT to be disabled\");\n }\n },\n toHaveAttribute(name: string) {\n const el = assertElement(actual);\n if (el.hasAttribute(name)) {\n throw new Error(`Expected element NOT to have attribute \"${name}\"`);\n }\n },\n },\n };\n}\n\n/**\n * Flush microtasks and pending DOM updates. Call after signal assignments\n * or render calls to give the reactive system and the browser a chance to\n * settle before asserting on the result.\n */\nexport function flush(ms = 50): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\n/**\n * Clear a container's rendered content. Call at the end of each test to\n * prevent state leaking between tests. If you use Preact's render(), pass\n * the same container; the function calls render(null, container) if Preact\n * is available, otherwise sets innerHTML to \"\".\n */\nexport function cleanup(container: Element): void {\n container.innerHTML = \"\";\n}\n\n/**\n * Run all registered tests and report the tally to the Node-side runner.\n *\n * The runner injects `window.__pollyReport` (via Puppeteer's\n * `page.exposeFunction`) before navigating, so reporting is a single\n * push from the page to the runner — no polling, no waiting on a long-\n * running CDP call from the Node side. Polled `page.evaluate` was the\n * root cause of the intermittent `Runtime.callFunctionOn` stalls\n * tracked in polly#138.\n *\n * Call this at the end of every .browser.ts test file.\n */\nexport async function done(): Promise<void> {\n for (const suite of suites) {\n for (const t of suite.tests) {\n const fullName = `${suite.name} > ${t.name}`;\n try {\n await t.fn();\n results.push({ name: fullName, passed: true });\n } catch (err) {\n results.push({\n name: fullName,\n passed: false,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n }\n\n const report = (window as unknown as Record<string, unknown>)[\"__pollyReport\"];\n if (typeof report !== \"function\") {\n throw new Error(\n \"harness.done(): window.__pollyReport is not defined. \" +\n \"This harness must be driven by the Polly browser test runner.\"\n );\n }\n (report as (r: TestResult[]) => void)(results);\n}\n\n/**\n * Wait until a predicate returns true, polling every intervalMs. Rejects\n * after timeoutMs.\n */\nexport async function waitFor(\n predicate: () => boolean | Promise<boolean>,\n timeoutMs = 5000,\n intervalMs = 25\n): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (await predicate()) return;\n await new Promise((r) => setTimeout(r, intervalMs));\n }\n throw new Error(`waitFor timed out after ${timeoutMs}ms`);\n}\n"
5
+ "/**\n * Browser-side test harness for Polly applications.\n *\n * Provides describe/test/expect/done that run inside a Puppeteer-launched\n * browser tab and record results on window.__testResults for the Node-side\n * runner to collect. Matchers cover both value assertions and DOM element\n * assertions so that Preact component tests and WebRTC adapter tests use\n * the same harness.\n *\n * @example\n * ```typescript\n * import { describe, test, expect, done, flush, cleanup } from \"@fairfox/polly/test/browser\";\n *\n * const app = document.getElementById(\"app\")!;\n *\n * describe(\"my feature\", () => {\n * test(\"renders correctly\", async () => {\n * render(<MyComponent />, app);\n * await flush();\n * expect(app.querySelector(\"h1\")).toHaveTextContent(\"Hello\");\n * cleanup(app);\n * });\n * });\n *\n * done();\n * ```\n */\n\ninterface TestResult {\n name: string;\n passed: boolean;\n error?: string;\n}\n\nconst results: TestResult[] = [];\nconst suites: Array<{\n name: string;\n tests: Array<{ name: string; fn: () => Promise<void> | void }>;\n}> = [];\n\nexport function describe(name: string, fn: () => void): void {\n suites.push({ name, tests: [] });\n fn();\n}\n\nexport function test(name: string, fn: () => Promise<void> | void): void {\n const suite = suites[suites.length - 1];\n if (suite) {\n suite.tests.push({ name, fn });\n }\n}\n\nfunction assertElement(value: unknown): Element {\n if (!(value instanceof Element)) {\n throw new Error(`Expected an Element, got ${typeof value}: ${String(value)}`);\n }\n return value;\n}\n\nexport function expect<T>(actual: T) {\n return {\n // Value matchers\n toBe(expected: T) {\n if (actual !== expected) {\n throw new Error(`Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);\n }\n },\n toEqual(expected: T) {\n if (JSON.stringify(actual) !== JSON.stringify(expected)) {\n throw new Error(`Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);\n }\n },\n toContain(sub: string) {\n if (!String(actual).includes(sub)) {\n throw new Error(`Expected \"${String(actual)}\" to contain \"${sub}\"`);\n }\n },\n toBeTruthy() {\n if (!actual) throw new Error(`Expected truthy, got ${String(actual)}`);\n },\n toBeFalsy() {\n if (actual) throw new Error(`Expected falsy, got ${String(actual)}`);\n },\n toBeNull() {\n if (actual !== null) throw new Error(`Expected null, got ${String(actual)}`);\n },\n toBeDefined() {\n if (actual === undefined || actual === null) {\n throw new Error(`Expected value to be defined, got ${String(actual)}`);\n }\n },\n toBeUndefined() {\n if (actual !== undefined) {\n throw new Error(`Expected undefined, got ${JSON.stringify(actual)}`);\n }\n },\n toBeGreaterThan(expected: number) {\n if (typeof actual !== \"number\" || actual <= expected) {\n throw new Error(`Expected ${String(actual)} to be greater than ${expected}`);\n }\n },\n toHaveLength(expected: number) {\n const obj = actual;\n const len = obj && typeof obj === \"object\" && \"length\" in obj ? Number(obj.length) : -1;\n if (len !== expected) throw new Error(`Expected length ${expected}, got ${len}`);\n },\n toExist() {\n if (actual == null) throw new Error(`Expected value to exist, got ${String(actual)}`);\n },\n\n // DOM element matchers\n toHaveTextContent(expected: string) {\n const el = assertElement(actual);\n if (!el.textContent?.includes(expected)) {\n throw new Error(\n `Expected text content to include ${JSON.stringify(expected)}, got ${JSON.stringify(el.textContent)}`\n );\n }\n },\n toBeChecked() {\n const el = assertElement(actual);\n if (!(el instanceof HTMLInputElement) || !el.checked) {\n throw new Error(\"Expected element to be checked\");\n }\n },\n toBeDisabled() {\n const el = assertElement(actual);\n if (!el.hasAttribute(\"disabled\") && el.getAttribute(\"aria-disabled\") !== \"true\") {\n throw new Error(\"Expected element to be disabled\");\n }\n },\n toHaveValue(expected: string) {\n const el = assertElement(actual);\n const inputEl =\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement ? el : null;\n if (!inputEl || inputEl.value !== expected) {\n throw new Error(\n `Expected value ${JSON.stringify(expected)}, got ${JSON.stringify(inputEl?.value ?? \"(not an input)\")}`\n );\n }\n },\n toHaveAttribute(name: string, value?: string) {\n const el = assertElement(actual);\n if (!el.hasAttribute(name)) {\n throw new Error(`Expected element to have attribute \"${name}\"`);\n }\n if (value !== undefined && el.getAttribute(name) !== value) {\n throw new Error(\n `Expected attribute \"${name}\" to be ${JSON.stringify(value)}, got ${JSON.stringify(el.getAttribute(name))}`\n );\n }\n },\n\n // .not variants\n not: {\n toBe(expected: T) {\n if (actual === expected) {\n throw new Error(`Expected value NOT to be ${JSON.stringify(expected)}`);\n }\n },\n toEqual(expected: T) {\n if (JSON.stringify(actual) === JSON.stringify(expected)) {\n throw new Error(`Expected value NOT to equal ${JSON.stringify(expected)}`);\n }\n },\n toContain(sub: string) {\n if (String(actual).includes(sub)) {\n throw new Error(`Expected \"${String(actual)}\" NOT to contain \"${sub}\"`);\n }\n },\n toBeNull() {\n if (actual === null) throw new Error(\"Expected value NOT to be null\");\n },\n toExist() {\n if (actual != null) throw new Error(`Expected value NOT to exist, got ${String(actual)}`);\n },\n toBeChecked() {\n const el = assertElement(actual);\n if (el instanceof HTMLInputElement && el.checked) {\n throw new Error(\"Expected element NOT to be checked\");\n }\n },\n toBeDisabled() {\n const el = assertElement(actual);\n if (el.hasAttribute(\"disabled\") || el.getAttribute(\"aria-disabled\") === \"true\") {\n throw new Error(\"Expected element NOT to be disabled\");\n }\n },\n toHaveAttribute(name: string) {\n const el = assertElement(actual);\n if (el.hasAttribute(name)) {\n throw new Error(`Expected element NOT to have attribute \"${name}\"`);\n }\n },\n },\n };\n}\n\n/**\n * Flush microtasks and pending DOM updates. Call after signal assignments\n * or render calls to give the reactive system and the browser a chance to\n * settle before asserting on the result.\n */\nexport function flush(ms = 50): Promise<void> {\n return new Promise((r) => setTimeout(r, ms));\n}\n\n/**\n * Clear a container's rendered content. Call at the end of each test to\n * prevent state leaking between tests. If you use Preact's render(), pass\n * the same container; the function calls render(null, container) if Preact\n * is available, otherwise sets innerHTML to \"\".\n */\nexport function cleanup(container: Element): void {\n container.innerHTML = \"\";\n}\n\n/**\n * Run all registered tests and report the tally to the Node-side runner.\n *\n * The runner injects `window.__pollyReport` (via Puppeteer's\n * `page.exposeFunction`) before navigating, so reporting is a single\n * push from the page to the runner — no polling, no waiting on a long-\n * running CDP call from the Node side. Polled `page.evaluate` was the\n * root cause of the intermittent `Runtime.callFunctionOn` stalls\n * tracked in polly#138.\n *\n * Call this at the end of every .browser.ts test file.\n */\nexport async function done(): Promise<void> {\n for (const suite of suites) {\n for (const t of suite.tests) {\n const fullName = `${suite.name} > ${t.name}`;\n try {\n await t.fn();\n results.push({ name: fullName, passed: true });\n } catch (err) {\n results.push({\n name: fullName,\n passed: false,\n error: err instanceof Error ? err.message : String(err),\n });\n }\n }\n }\n\n const report = (window as unknown as Record<string, unknown>)[\"__pollyReport\"];\n if (!isReportFunction(report)) {\n throw new Error(\n \"harness.done(): window.__pollyReport is not defined. \" +\n \"This harness must be driven by the Polly browser test runner.\"\n );\n }\n report(results);\n}\n\n/** The runner injects `window.__pollyReport`; all we can check is callability. */\nfunction isReportFunction(value: unknown): value is (r: TestResult[]) => void {\n return typeof value === \"function\";\n}\n\n/**\n * Wait until a predicate returns true, polling every intervalMs. Rejects\n * after timeoutMs.\n */\nexport async function waitFor(\n predicate: () => boolean | Promise<boolean>,\n timeoutMs = 5000,\n intervalMs = 25\n): Promise<void> {\n const deadline = Date.now() + timeoutMs;\n while (Date.now() < deadline) {\n if (await predicate()) return;\n await new Promise((r) => setTimeout(r, intervalMs));\n }\n throw new Error(`waitFor timed out after ${timeoutMs}ms`);\n}\n"
6
6
  ],
7
- "mappings": "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",
8
- "debugId": "F2832DFB58875E3964756E2164756E21",
7
+ "mappings": "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",
8
+ "debugId": "24287A6E9F4D05B664756E2164756E21",
9
9
  "names": []
10
10
  }
@@ -266,11 +266,12 @@ function splitFunctionArgs(inner) {
266
266
  }
267
267
  function parseRgb(inner) {
268
268
  const { parts, alpha } = splitFunctionArgs(inner);
269
- if (parts.length < 3)
269
+ const [rToken, gToken, bToken] = parts;
270
+ if (rToken === undefined || gToken === undefined || bToken === undefined)
270
271
  return null;
271
- const r = clampChannel(parseComponent(parts[0], 255));
272
- const g = clampChannel(parseComponent(parts[1], 255));
273
- const b = clampChannel(parseComponent(parts[2], 255));
272
+ const r = clampChannel(parseComponent(rToken, 255));
273
+ const g = clampChannel(parseComponent(gToken, 255));
274
+ const b = clampChannel(parseComponent(bToken, 255));
274
275
  if (![r, g, b].every(Number.isFinite))
275
276
  return null;
276
277
  return { r, g, b, a: parseAlpha(alpha) };
@@ -301,11 +302,12 @@ function hslToRgb(h, s, l) {
301
302
  }
302
303
  function parseHsl(inner) {
303
304
  const { parts, alpha } = splitFunctionArgs(inner);
304
- if (parts.length < 3)
305
+ const [hToken, sToken, lToken] = parts;
306
+ if (hToken === undefined || sToken === undefined || lToken === undefined)
305
307
  return null;
306
- const h = Number.parseFloat(parts[0].replace(/deg$/, ""));
307
- const s = clamp(parseComponent(parts[1], 100) / 100, 0, 1);
308
- const l = clamp(parseComponent(parts[2], 100) / 100, 0, 1);
308
+ const h = Number.parseFloat(hToken.replace(/deg$/, ""));
309
+ const s = clamp(parseComponent(sToken, 100) / 100, 0, 1);
310
+ const l = clamp(parseComponent(lToken, 100) / 100, 0, 1);
309
311
  if (![h, s, l].every(Number.isFinite))
310
312
  return null;
311
313
  return { ...hslToRgb(h, s, l), a: parseAlpha(alpha) };
@@ -325,13 +327,13 @@ function parseColor(input) {
325
327
  if (value.startsWith("#"))
326
328
  return parseHex(value);
327
329
  const fn = value.match(/^([a-z]+)\(([^)]*)\)$/i);
328
- if (fn) {
329
- const name = fn[1].toLowerCase();
330
- const inner = fn[2];
330
+ const [, fnName, fnInner] = fn ?? [];
331
+ if (fnName !== undefined && fnInner !== undefined) {
332
+ const name = fnName.toLowerCase();
331
333
  if (name === "rgb" || name === "rgba")
332
- return parseRgb(inner);
334
+ return parseRgb(fnInner);
333
335
  if (name === "hsl" || name === "hsla")
334
- return parseHsl(inner);
336
+ return parseHsl(fnInner);
335
337
  }
336
338
  return null;
337
339
  }
@@ -386,7 +388,10 @@ function effectiveBackground(element) {
386
388
  }
387
389
  let result = { r: 255, g: 255, b: 255, a: 1 };
388
390
  for (let i = stack.length - 1;i >= 0; i--) {
389
- result = compositeOver(stack[i], result);
391
+ const layer = stack[i];
392
+ if (layer === undefined)
393
+ continue;
394
+ result = compositeOver(layer, result);
390
395
  }
391
396
  return result;
392
397
  }
@@ -430,4 +435,4 @@ export {
430
435
  assertContrast
431
436
  };
432
437
 
433
- //# debugId=38512E3938AFDCFA64756E2164756E21
438
+ //# debugId=6107E968AC1112A564756E2164756E21
@@ -3,9 +3,9 @@
3
3
  "sources": ["../tools/test/src/contrast/named-colors.ts", "../tools/test/src/contrast/index.ts"],
4
4
  "sourcesContent": [
5
5
  "// The complete CSS named-color keyword table (CSS Color Module Level 4),\n// keyed by lowercase name → `#rrggbb`. Parsed by `parseColor` so a consumer can\n// write `assertContrast(el, { against: \"white\" })` or feed a named color\n// directly. `transparent` is handled separately (it carries alpha 0); the rest\n// are fully opaque. `rebeccapurple` is included per CSS Color 4.\n//\n// Kept as data so `parseColor` stays transport-agnostic — no DOM round-trip\n// needed to resolve a name, so it works identically in Node and in a\n// `page.evaluate` bundle.\nexport const CSS_NAMED_COLORS: Readonly<Record<string, string>> = Object.freeze({\n aliceblue: \"#f0f8ff\",\n antiquewhite: \"#faebd7\",\n aqua: \"#00ffff\",\n aquamarine: \"#7fffd4\",\n azure: \"#f0ffff\",\n beige: \"#f5f5dc\",\n bisque: \"#ffe4c4\",\n black: \"#000000\",\n blanchedalmond: \"#ffebcd\",\n blue: \"#0000ff\",\n blueviolet: \"#8a2be2\",\n brown: \"#a52a2a\",\n burlywood: \"#deb887\",\n cadetblue: \"#5f9ea0\",\n chartreuse: \"#7fff00\",\n chocolate: \"#d2691e\",\n coral: \"#ff7f50\",\n cornflowerblue: \"#6495ed\",\n cornsilk: \"#fff8dc\",\n crimson: \"#dc143c\",\n cyan: \"#00ffff\",\n darkblue: \"#00008b\",\n darkcyan: \"#008b8b\",\n darkgoldenrod: \"#b8860b\",\n darkgray: \"#a9a9a9\",\n darkgreen: \"#006400\",\n darkgrey: \"#a9a9a9\",\n darkkhaki: \"#bdb76b\",\n darkmagenta: \"#8b008b\",\n darkolivegreen: \"#556b2f\",\n darkorange: \"#ff8c00\",\n darkorchid: \"#9932cc\",\n darkred: \"#8b0000\",\n darksalmon: \"#e9967a\",\n darkseagreen: \"#8fbc8f\",\n darkslateblue: \"#483d8b\",\n darkslategray: \"#2f4f4f\",\n darkslategrey: \"#2f4f4f\",\n darkturquoise: \"#00ced1\",\n darkviolet: \"#9400d3\",\n deeppink: \"#ff1493\",\n deepskyblue: \"#00bfff\",\n dimgray: \"#696969\",\n dimgrey: \"#696969\",\n dodgerblue: \"#1e90ff\",\n firebrick: \"#b22222\",\n floralwhite: \"#fffaf0\",\n forestgreen: \"#228b22\",\n fuchsia: \"#ff00ff\",\n gainsboro: \"#dcdcdc\",\n ghostwhite: \"#f8f8ff\",\n gold: \"#ffd700\",\n goldenrod: \"#daa520\",\n gray: \"#808080\",\n green: \"#008000\",\n greenyellow: \"#adff2f\",\n grey: \"#808080\",\n honeydew: \"#f0fff0\",\n hotpink: \"#ff69b4\",\n indianred: \"#cd5c5c\",\n indigo: \"#4b0082\",\n ivory: \"#fffff0\",\n khaki: \"#f0e68c\",\n lavender: \"#e6e6fa\",\n lavenderblush: \"#fff0f5\",\n lawngreen: \"#7cfc00\",\n lemonchiffon: \"#fffacd\",\n lightblue: \"#add8e6\",\n lightcoral: \"#f08080\",\n lightcyan: \"#e0ffff\",\n lightgoldenrodyellow: \"#fafad2\",\n lightgray: \"#d3d3d3\",\n lightgreen: \"#90ee90\",\n lightgrey: \"#d3d3d3\",\n lightpink: \"#ffb6c1\",\n lightsalmon: \"#ffa07a\",\n lightseagreen: \"#20b2aa\",\n lightskyblue: \"#87cefa\",\n lightslategray: \"#778899\",\n lightslategrey: \"#778899\",\n lightsteelblue: \"#b0c4de\",\n lightyellow: \"#ffffe0\",\n lime: \"#00ff00\",\n limegreen: \"#32cd32\",\n linen: \"#faf0e6\",\n magenta: \"#ff00ff\",\n maroon: \"#800000\",\n mediumaquamarine: \"#66cdaa\",\n mediumblue: \"#0000cd\",\n mediumorchid: \"#ba55d3\",\n mediumpurple: \"#9370db\",\n mediumseagreen: \"#3cb371\",\n mediumslateblue: \"#7b68ee\",\n mediumspringgreen: \"#00fa9a\",\n mediumturquoise: \"#48d1cc\",\n mediumvioletred: \"#c71585\",\n midnightblue: \"#191970\",\n mintcream: \"#f5fffa\",\n mistyrose: \"#ffe4e1\",\n moccasin: \"#ffe4b5\",\n navajowhite: \"#ffdead\",\n navy: \"#000080\",\n oldlace: \"#fdf5e6\",\n olive: \"#808000\",\n olivedrab: \"#6b8e23\",\n orange: \"#ffa500\",\n orangered: \"#ff4500\",\n orchid: \"#da70d6\",\n palegoldenrod: \"#eee8aa\",\n palegreen: \"#98fb98\",\n paleturquoise: \"#afeeee\",\n palevioletred: \"#db7093\",\n papayawhip: \"#ffefd5\",\n peachpuff: \"#ffdab9\",\n peru: \"#cd853f\",\n pink: \"#ffc0cb\",\n plum: \"#dda0dd\",\n powderblue: \"#b0e0e6\",\n purple: \"#800080\",\n rebeccapurple: \"#663399\",\n red: \"#ff0000\",\n rosybrown: \"#bc8f8f\",\n royalblue: \"#4169e1\",\n saddlebrown: \"#8b4513\",\n salmon: \"#fa8072\",\n sandybrown: \"#f4a460\",\n seagreen: \"#2e8b57\",\n seashell: \"#fff5ee\",\n sienna: \"#a0522d\",\n silver: \"#c0c0c0\",\n skyblue: \"#87ceeb\",\n slateblue: \"#6a5acd\",\n slategray: \"#708090\",\n slategrey: \"#708090\",\n snow: \"#fffafa\",\n springgreen: \"#00ff7f\",\n steelblue: \"#4682b4\",\n tan: \"#d2b48c\",\n teal: \"#008080\",\n thistle: \"#d8bfd8\",\n tomato: \"#ff6347\",\n turquoise: \"#40e0d0\",\n violet: \"#ee82ee\",\n wheat: \"#f5deb3\",\n white: \"#ffffff\",\n whitesmoke: \"#f5f5f5\",\n yellow: \"#ffff00\",\n yellowgreen: \"#9acd32\",\n});\n",
6
- "/**\n * @fairfox/polly/test/contrast — WCAG contrast helpers for theme/dark-mode\n * tests (polly#141).\n *\n * Theme regressions are easy to ship and hard to catch, and hand-rolled checks\n * keep repeating the same mistakes: averaging RGB channels as a brightness\n * proxy (false passes for equal-luminance hues), reading a single\n * `backgroundColor` and skipping `rgba(0,0,0,0)` (silently dropping every\n * transparent-background element), and picking an arbitrary threshold. WCAG 2.1\n * has a defined answer — relative luminance, 4.5:1 for body text, 3:1 for UI\n * components and large text (SC 1.4.3 / 1.4.11) — and it belongs in one place.\n *\n * The pure functions (`parseColor`, `relativeLuminance`, `contrastRatio`) are\n * transport-agnostic: they work in Node and inside a Playwright/Puppeteer\n * `page.evaluate` bundle alike. `effectiveBackground` and `assertContrast` read\n * the live DOM and are meant for the harness side (or a `page.evaluate` where a\n * real `getComputedStyle` exists).\n */\n\nimport { CSS_NAMED_COLORS } from \"./named-colors\";\n\n/** r, g, b in 0–255; a (alpha) in 0–1. */\nexport interface RGBA {\n r: number;\n g: number;\n b: number;\n a: number;\n}\n\n/** Anything `contrastRatio`/`assertContrast` will accept for a color. */\nexport type ColorInput = string | RGBA | { r: number; g: number; b: number };\n\nconst clamp = (n: number, lo: number, hi: number): number => Math.min(hi, Math.max(lo, n));\nconst clampChannel = (n: number): number => clamp(Math.round(n), 0, 255);\n\n/** Parse one rgb/hsl numeric component that may be a percentage of `scale`. */\nfunction parseComponent(token: string, scale: number): number {\n const t = token.trim();\n if (t.endsWith(\"%\")) {\n return (Number.parseFloat(t) / 100) * scale;\n }\n return Number.parseFloat(t);\n}\n\n/** Parse an alpha token (`0.5` or `50%`) to 0–1. */\nfunction parseAlpha(token: string | undefined): number {\n if (token === undefined) return 1;\n const t = token.trim();\n const value = t.endsWith(\"%\") ? Number.parseFloat(t) / 100 : Number.parseFloat(t);\n return Number.isFinite(value) ? clamp(value, 0, 1) : 1;\n}\n\nfunction parseHex(input: string): RGBA | null {\n const hex = input.slice(1);\n const expand = (h: string): string =>\n h.length === 3 || h.length === 4\n ? h\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n : h;\n const full = expand(hex);\n if (full.length !== 6 && full.length !== 8) return null;\n if (!/^[0-9a-fA-F]+$/.test(full)) return null;\n const r = Number.parseInt(full.slice(0, 2), 16);\n const g = Number.parseInt(full.slice(2, 4), 16);\n const b = Number.parseInt(full.slice(4, 6), 16);\n const a = full.length === 8 ? Number.parseInt(full.slice(6, 8), 16) / 255 : 1;\n return { r, g, b, a };\n}\n\n/** Split the inside of `fn(...)` into channel tokens and an optional alpha,\n * accepting both comma syntax (`r, g, b, a`) and modern slash syntax\n * (`r g b / a`). */\nfunction splitFunctionArgs(inner: string): { parts: string[]; alpha: string | undefined } {\n const [head, alphaTail] = inner.split(\"/\");\n const slashAlpha = alphaTail?.trim();\n const parts = (head ?? \"\")\n .trim()\n .split(/[\\s,]+/)\n .filter(Boolean);\n if (slashAlpha !== undefined) {\n return { parts, alpha: slashAlpha };\n }\n // Comma syntax folds alpha into the 4th part.\n if (parts.length === 4) {\n return { parts: parts.slice(0, 3), alpha: parts[3] };\n }\n return { parts, alpha: undefined };\n}\n\nfunction parseRgb(inner: string): RGBA | null {\n const { parts, alpha } = splitFunctionArgs(inner);\n if (parts.length < 3) return null;\n const r = clampChannel(parseComponent(parts[0] as string, 255));\n const g = clampChannel(parseComponent(parts[1] as string, 255));\n const b = clampChannel(parseComponent(parts[2] as string, 255));\n if (![r, g, b].every(Number.isFinite)) return null;\n return { r, g, b, a: parseAlpha(alpha) };\n}\n\n/** HSL→RGB per CSS. h in degrees, s & l in 0–1. */\nfunction hslToRgb(h: number, s: number, l: number): { r: number; g: number; b: number } {\n const hue = ((h % 360) + 360) % 360;\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const x = c * (1 - Math.abs(((hue / 60) % 2) - 1));\n const m = l - c / 2;\n const [r1, g1, b1] = (() => {\n if (hue < 60) return [c, x, 0];\n if (hue < 120) return [x, c, 0];\n if (hue < 180) return [0, c, x];\n if (hue < 240) return [0, x, c];\n if (hue < 300) return [x, 0, c];\n return [c, 0, x];\n })();\n return {\n r: clampChannel((r1 + m) * 255),\n g: clampChannel((g1 + m) * 255),\n b: clampChannel((b1 + m) * 255),\n };\n}\n\nfunction parseHsl(inner: string): RGBA | null {\n const { parts, alpha } = splitFunctionArgs(inner);\n if (parts.length < 3) return null;\n const h = Number.parseFloat((parts[0] as string).replace(/deg$/, \"\"));\n const s = clamp(parseComponent(parts[1] as string, 100) / 100, 0, 1);\n const l = clamp(parseComponent(parts[2] as string, 100) / 100, 0, 1);\n if (![h, s, l].every(Number.isFinite)) return null;\n return { ...hslToRgb(h, s, l), a: parseAlpha(alpha) };\n}\n\n/**\n * Parse a CSS color string to `{ r, g, b, a }`, or `null` if unrecognised.\n * Handles `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`, `rgb()/rgba()` (comma or\n * space syntax, with optional `/ alpha`), `hsl()/hsla()`, `transparent`, and\n * the full set of CSS named colors. This is the parse `getComputedStyle`\n * output needs (always `rgb()`/`rgba()`) plus the literal forms a test author\n * is likely to pass to `against`.\n */\nexport function parseColor(input: string): RGBA | null {\n if (typeof input !== \"string\") return null;\n const value = input.trim();\n if (value === \"\") return null;\n const lower = value.toLowerCase();\n\n if (lower === \"transparent\") return { r: 0, g: 0, b: 0, a: 0 };\n\n const named = CSS_NAMED_COLORS[lower];\n if (named) return parseHex(named);\n\n if (value.startsWith(\"#\")) return parseHex(value);\n\n const fn = value.match(/^([a-z]+)\\(([^)]*)\\)$/i);\n if (fn) {\n const name = (fn[1] as string).toLowerCase();\n const inner = fn[2] as string;\n if (name === \"rgb\" || name === \"rgba\") return parseRgb(inner);\n if (name === \"hsl\" || name === \"hsla\") return parseHsl(inner);\n }\n return null;\n}\n\n/** Coerce a ColorInput to RGBA (assuming alpha 1 when absent), or null. */\nfunction toRGBA(input: ColorInput): RGBA | null {\n if (typeof input === \"string\") return parseColor(input);\n if (input && typeof input === \"object\" && \"r\" in input) {\n return { r: input.r, g: input.g, b: input.b, a: \"a\" in input ? (input as RGBA).a : 1 };\n }\n return null;\n}\n\n/**\n * WCAG relative luminance (0–1) of an opaque color. Alpha is ignored — composite\n * first (see `compositeOver`) if the color is translucent.\n */\nexport function relativeLuminance(color: { r: number; g: number; b: number }): number {\n const channel = (c: number): number => {\n const cs = c / 255;\n return cs <= 0.03928 ? cs / 12.92 : ((cs + 0.055) / 1.055) ** 2.4;\n };\n return 0.2126 * channel(color.r) + 0.7152 * channel(color.g) + 0.0722 * channel(color.b);\n}\n\n/**\n * WCAG contrast ratio (1–21) between two colors. Accepts CSS strings or RGB(A)\n * objects. Alpha is ignored; composite translucent colors over their background\n * first. Returns `NaN` if either input cannot be parsed.\n */\nexport function contrastRatio(a: ColorInput, b: ColorInput): number {\n const ca = toRGBA(a);\n const cb = toRGBA(b);\n if (!ca || !cb) return Number.NaN;\n const la = relativeLuminance(ca);\n const lb = relativeLuminance(cb);\n const lighter = Math.max(la, lb);\n const darker = Math.min(la, lb);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Alpha-composite `fg` over `bg` (the \"over\" operator), returning an opaque\n * color. `bg` is assumed opaque (its alpha is treated as 1).\n */\nexport function compositeOver(fg: RGBA, bg: RGBA): RGBA {\n const a = clamp(fg.a, 0, 1);\n return {\n r: clampChannel(fg.r * a + bg.r * (1 - a)),\n g: clampChannel(fg.g * a + bg.g * (1 - a)),\n b: clampChannel(fg.b * a + bg.b * (1 - a)),\n a: 1,\n };\n}\n\n/**\n * Resolve the effective (opaque) background an element renders against by\n * walking up the ancestor chain, compositing each translucent background over\n * the next, until an opaque one is reached. Elements with a fully transparent\n * background — most of them — are no longer silently skipped: they contribute\n * their (zero) alpha and the walk continues to the parent. Falls back to white\n * when the chain reaches the root without an opaque background.\n */\nexport function effectiveBackground(element: Element): RGBA {\n const view = element.ownerDocument?.defaultView;\n const getStyle = view?.getComputedStyle?.bind(view);\n const stack: RGBA[] = [];\n\n let node: Element | null = element;\n while (node && getStyle) {\n const parsed = parseColor(getStyle(node).backgroundColor || \"\");\n if (parsed && parsed.a > 0) {\n stack.push(parsed);\n if (parsed.a >= 1) break;\n }\n node = node.parentElement;\n }\n\n // Composite from the deepest opaque layer outward toward the element.\n let result: RGBA = { r: 255, g: 255, b: 255, a: 1 };\n for (let i = stack.length - 1; i >= 0; i--) {\n result = compositeOver(stack[i] as RGBA, result);\n }\n return result;\n}\n\nexport interface AssertContrastOptions {\n /** Minimum acceptable ratio. WCAG: 3 (UI/large text), 4.5 (body), 7 (AAA). */\n min: 3 | 4.5 | 7 | number;\n /**\n * Background to measure against. `\"effective\"` (default) walks the ancestor\n * chain via `effectiveBackground`; an explicit color (string or RGB) skips\n * the walk.\n */\n against?: \"effective\" | ColorInput;\n}\n\nexport interface ContrastResult {\n ratio: number;\n foreground: RGBA;\n background: RGBA;\n min: number;\n passes: boolean;\n}\n\nfunction describeElement(element: Element): string {\n const tag = element.tagName.toLowerCase();\n const id = element.id ? `#${element.id}` : \"\";\n const cls = element.getAttribute(\"class\");\n const classes = cls ? `.${cls.trim().split(/\\s+/).join(\".\")}` : \"\";\n return `${tag}${id}${classes}`;\n}\n\nconst fmt = (c: RGBA): string =>\n c.a >= 1 ? `rgb(${c.r}, ${c.g}, ${c.b})` : `rgba(${c.r}, ${c.g}, ${c.b}, ${c.a})`;\n\n/**\n * Assert an element's text meets a WCAG contrast minimum against its\n * background, throwing a message that names the element, both colors, and the\n * actual ratio. The element's own (possibly translucent) text color is\n * composited over the resolved background before measuring. Returns the\n * computed `ContrastResult` on success so callers can log or assert further.\n */\nexport function assertContrast(element: Element, options: AssertContrastOptions): ContrastResult {\n const view = element.ownerDocument?.defaultView;\n const getStyle = view?.getComputedStyle?.bind(view);\n if (!getStyle) {\n throw new Error(\"assertContrast: no getComputedStyle available on the element's document\");\n }\n\n const fgRaw = parseColor(getStyle(element).color || \"\");\n if (!fgRaw) {\n throw new Error(`assertContrast: could not parse the color of ${describeElement(element)}`);\n }\n\n const against = options.against ?? \"effective\";\n const background =\n against === \"effective\" ? effectiveBackground(element) : toRGBA(against as ColorInput);\n if (!background) {\n throw new Error(\n `assertContrast: could not parse the 'against' color for ${describeElement(element)}`\n );\n }\n\n const foreground = fgRaw.a < 1 ? compositeOver(fgRaw, background) : fgRaw;\n const ratio = contrastRatio(foreground, background);\n const passes = ratio >= options.min;\n\n if (!passes) {\n throw new Error(\n `Contrast ${ratio.toFixed(2)}:1 is below the required ${options.min}:1 for ` +\n `${describeElement(element)} — foreground ${fmt(foreground)} on background ${fmt(background)}.`\n );\n }\n\n return { ratio, foreground, background, min: options.min, passes };\n}\n"
6
+ "/**\n * @fairfox/polly/test/contrast — WCAG contrast helpers for theme/dark-mode\n * tests (polly#141).\n *\n * Theme regressions are easy to ship and hard to catch, and hand-rolled checks\n * keep repeating the same mistakes: averaging RGB channels as a brightness\n * proxy (false passes for equal-luminance hues), reading a single\n * `backgroundColor` and skipping `rgba(0,0,0,0)` (silently dropping every\n * transparent-background element), and picking an arbitrary threshold. WCAG 2.1\n * has a defined answer — relative luminance, 4.5:1 for body text, 3:1 for UI\n * components and large text (SC 1.4.3 / 1.4.11) — and it belongs in one place.\n *\n * The pure functions (`parseColor`, `relativeLuminance`, `contrastRatio`) are\n * transport-agnostic: they work in Node and inside a Playwright/Puppeteer\n * `page.evaluate` bundle alike. `effectiveBackground` and `assertContrast` read\n * the live DOM and are meant for the harness side (or a `page.evaluate` where a\n * real `getComputedStyle` exists).\n */\n\nimport { CSS_NAMED_COLORS } from \"./named-colors\";\n\n/** r, g, b in 0–255; a (alpha) in 0–1. */\nexport interface RGBA {\n r: number;\n g: number;\n b: number;\n a: number;\n}\n\n/** Anything `contrastRatio`/`assertContrast` will accept for a color. */\nexport type ColorInput = string | RGBA | { r: number; g: number; b: number };\n\nconst clamp = (n: number, lo: number, hi: number): number => Math.min(hi, Math.max(lo, n));\nconst clampChannel = (n: number): number => clamp(Math.round(n), 0, 255);\n\n/** Parse one rgb/hsl numeric component that may be a percentage of `scale`. */\nfunction parseComponent(token: string, scale: number): number {\n const t = token.trim();\n if (t.endsWith(\"%\")) {\n return (Number.parseFloat(t) / 100) * scale;\n }\n return Number.parseFloat(t);\n}\n\n/** Parse an alpha token (`0.5` or `50%`) to 0–1. */\nfunction parseAlpha(token: string | undefined): number {\n if (token === undefined) return 1;\n const t = token.trim();\n const value = t.endsWith(\"%\") ? Number.parseFloat(t) / 100 : Number.parseFloat(t);\n return Number.isFinite(value) ? clamp(value, 0, 1) : 1;\n}\n\nfunction parseHex(input: string): RGBA | null {\n const hex = input.slice(1);\n const expand = (h: string): string =>\n h.length === 3 || h.length === 4\n ? h\n .split(\"\")\n .map((c) => c + c)\n .join(\"\")\n : h;\n const full = expand(hex);\n if (full.length !== 6 && full.length !== 8) return null;\n if (!/^[0-9a-fA-F]+$/.test(full)) return null;\n const r = Number.parseInt(full.slice(0, 2), 16);\n const g = Number.parseInt(full.slice(2, 4), 16);\n const b = Number.parseInt(full.slice(4, 6), 16);\n const a = full.length === 8 ? Number.parseInt(full.slice(6, 8), 16) / 255 : 1;\n return { r, g, b, a };\n}\n\n/** Split the inside of `fn(...)` into channel tokens and an optional alpha,\n * accepting both comma syntax (`r, g, b, a`) and modern slash syntax\n * (`r g b / a`). */\nfunction splitFunctionArgs(inner: string): { parts: string[]; alpha: string | undefined } {\n const [head, alphaTail] = inner.split(\"/\");\n const slashAlpha = alphaTail?.trim();\n const parts = (head ?? \"\")\n .trim()\n .split(/[\\s,]+/)\n .filter(Boolean);\n if (slashAlpha !== undefined) {\n return { parts, alpha: slashAlpha };\n }\n // Comma syntax folds alpha into the 4th part.\n if (parts.length === 4) {\n return { parts: parts.slice(0, 3), alpha: parts[3] };\n }\n return { parts, alpha: undefined };\n}\n\nfunction parseRgb(inner: string): RGBA | null {\n const { parts, alpha } = splitFunctionArgs(inner);\n const [rToken, gToken, bToken] = parts;\n if (rToken === undefined || gToken === undefined || bToken === undefined) return null;\n const r = clampChannel(parseComponent(rToken, 255));\n const g = clampChannel(parseComponent(gToken, 255));\n const b = clampChannel(parseComponent(bToken, 255));\n if (![r, g, b].every(Number.isFinite)) return null;\n return { r, g, b, a: parseAlpha(alpha) };\n}\n\n/** HSL→RGB per CSS. h in degrees, s & l in 0–1. */\nfunction hslToRgb(h: number, s: number, l: number): { r: number; g: number; b: number } {\n const hue = ((h % 360) + 360) % 360;\n const c = (1 - Math.abs(2 * l - 1)) * s;\n const x = c * (1 - Math.abs(((hue / 60) % 2) - 1));\n const m = l - c / 2;\n const [r1, g1, b1] = (() => {\n if (hue < 60) return [c, x, 0];\n if (hue < 120) return [x, c, 0];\n if (hue < 180) return [0, c, x];\n if (hue < 240) return [0, x, c];\n if (hue < 300) return [x, 0, c];\n return [c, 0, x];\n })();\n return {\n r: clampChannel((r1 + m) * 255),\n g: clampChannel((g1 + m) * 255),\n b: clampChannel((b1 + m) * 255),\n };\n}\n\nfunction parseHsl(inner: string): RGBA | null {\n const { parts, alpha } = splitFunctionArgs(inner);\n const [hToken, sToken, lToken] = parts;\n if (hToken === undefined || sToken === undefined || lToken === undefined) return null;\n const h = Number.parseFloat(hToken.replace(/deg$/, \"\"));\n const s = clamp(parseComponent(sToken, 100) / 100, 0, 1);\n const l = clamp(parseComponent(lToken, 100) / 100, 0, 1);\n if (![h, s, l].every(Number.isFinite)) return null;\n return { ...hslToRgb(h, s, l), a: parseAlpha(alpha) };\n}\n\n/**\n * Parse a CSS color string to `{ r, g, b, a }`, or `null` if unrecognised.\n * Handles `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa`, `rgb()/rgba()` (comma or\n * space syntax, with optional `/ alpha`), `hsl()/hsla()`, `transparent`, and\n * the full set of CSS named colors. This is the parse `getComputedStyle`\n * output needs (always `rgb()`/`rgba()`) plus the literal forms a test author\n * is likely to pass to `against`.\n */\nexport function parseColor(input: string): RGBA | null {\n if (typeof input !== \"string\") return null;\n const value = input.trim();\n if (value === \"\") return null;\n const lower = value.toLowerCase();\n\n if (lower === \"transparent\") return { r: 0, g: 0, b: 0, a: 0 };\n\n const named = CSS_NAMED_COLORS[lower];\n if (named) return parseHex(named);\n\n if (value.startsWith(\"#\")) return parseHex(value);\n\n const fn = value.match(/^([a-z]+)\\(([^)]*)\\)$/i);\n const [, fnName, fnInner] = fn ?? [];\n if (fnName !== undefined && fnInner !== undefined) {\n const name = fnName.toLowerCase();\n if (name === \"rgb\" || name === \"rgba\") return parseRgb(fnInner);\n if (name === \"hsl\" || name === \"hsla\") return parseHsl(fnInner);\n }\n return null;\n}\n\n/** Coerce a ColorInput to RGBA (assuming alpha 1 when absent), or null. */\nfunction toRGBA(input: ColorInput): RGBA | null {\n if (typeof input === \"string\") return parseColor(input);\n if (input && typeof input === \"object\" && \"r\" in input) {\n return { r: input.r, g: input.g, b: input.b, a: \"a\" in input ? input.a : 1 };\n }\n return null;\n}\n\n/**\n * WCAG relative luminance (0–1) of an opaque color. Alpha is ignored — composite\n * first (see `compositeOver`) if the color is translucent.\n */\nexport function relativeLuminance(color: { r: number; g: number; b: number }): number {\n const channel = (c: number): number => {\n const cs = c / 255;\n return cs <= 0.03928 ? cs / 12.92 : ((cs + 0.055) / 1.055) ** 2.4;\n };\n return 0.2126 * channel(color.r) + 0.7152 * channel(color.g) + 0.0722 * channel(color.b);\n}\n\n/**\n * WCAG contrast ratio (1–21) between two colors. Accepts CSS strings or RGB(A)\n * objects. Alpha is ignored; composite translucent colors over their background\n * first. Returns `NaN` if either input cannot be parsed.\n */\nexport function contrastRatio(a: ColorInput, b: ColorInput): number {\n const ca = toRGBA(a);\n const cb = toRGBA(b);\n if (!ca || !cb) return Number.NaN;\n const la = relativeLuminance(ca);\n const lb = relativeLuminance(cb);\n const lighter = Math.max(la, lb);\n const darker = Math.min(la, lb);\n return (lighter + 0.05) / (darker + 0.05);\n}\n\n/**\n * Alpha-composite `fg` over `bg` (the \"over\" operator), returning an opaque\n * color. `bg` is assumed opaque (its alpha is treated as 1).\n */\nexport function compositeOver(fg: RGBA, bg: RGBA): RGBA {\n const a = clamp(fg.a, 0, 1);\n return {\n r: clampChannel(fg.r * a + bg.r * (1 - a)),\n g: clampChannel(fg.g * a + bg.g * (1 - a)),\n b: clampChannel(fg.b * a + bg.b * (1 - a)),\n a: 1,\n };\n}\n\n/**\n * Resolve the effective (opaque) background an element renders against by\n * walking up the ancestor chain, compositing each translucent background over\n * the next, until an opaque one is reached. Elements with a fully transparent\n * background — most of them — are no longer silently skipped: they contribute\n * their (zero) alpha and the walk continues to the parent. Falls back to white\n * when the chain reaches the root without an opaque background.\n */\nexport function effectiveBackground(element: Element): RGBA {\n const view = element.ownerDocument?.defaultView;\n const getStyle = view?.getComputedStyle?.bind(view);\n const stack: RGBA[] = [];\n\n let node: Element | null = element;\n while (node && getStyle) {\n const parsed = parseColor(getStyle(node).backgroundColor || \"\");\n if (parsed && parsed.a > 0) {\n stack.push(parsed);\n if (parsed.a >= 1) break;\n }\n node = node.parentElement;\n }\n\n // Composite from the deepest opaque layer outward toward the element.\n let result: RGBA = { r: 255, g: 255, b: 255, a: 1 };\n for (let i = stack.length - 1; i >= 0; i--) {\n const layer = stack[i];\n if (layer === undefined) continue;\n result = compositeOver(layer, result);\n }\n return result;\n}\n\nexport interface AssertContrastOptions {\n /** Minimum acceptable ratio. WCAG: 3 (UI/large text), 4.5 (body), 7 (AAA). */\n min: 3 | 4.5 | 7 | number;\n /**\n * Background to measure against. `\"effective\"` (default) walks the ancestor\n * chain via `effectiveBackground`; an explicit color (string or RGB) skips\n * the walk.\n */\n against?: \"effective\" | ColorInput;\n}\n\nexport interface ContrastResult {\n ratio: number;\n foreground: RGBA;\n background: RGBA;\n min: number;\n passes: boolean;\n}\n\nfunction describeElement(element: Element): string {\n const tag = element.tagName.toLowerCase();\n const id = element.id ? `#${element.id}` : \"\";\n const cls = element.getAttribute(\"class\");\n const classes = cls ? `.${cls.trim().split(/\\s+/).join(\".\")}` : \"\";\n return `${tag}${id}${classes}`;\n}\n\nconst fmt = (c: RGBA): string =>\n c.a >= 1 ? `rgb(${c.r}, ${c.g}, ${c.b})` : `rgba(${c.r}, ${c.g}, ${c.b}, ${c.a})`;\n\n/**\n * Assert an element's text meets a WCAG contrast minimum against its\n * background, throwing a message that names the element, both colors, and the\n * actual ratio. The element's own (possibly translucent) text color is\n * composited over the resolved background before measuring. Returns the\n * computed `ContrastResult` on success so callers can log or assert further.\n */\nexport function assertContrast(element: Element, options: AssertContrastOptions): ContrastResult {\n const view = element.ownerDocument?.defaultView;\n const getStyle = view?.getComputedStyle?.bind(view);\n if (!getStyle) {\n throw new Error(\"assertContrast: no getComputedStyle available on the element's document\");\n }\n\n const fgRaw = parseColor(getStyle(element).color || \"\");\n if (!fgRaw) {\n throw new Error(`assertContrast: could not parse the color of ${describeElement(element)}`);\n }\n\n const against = options.against ?? \"effective\";\n const background = against === \"effective\" ? effectiveBackground(element) : toRGBA(against);\n if (!background) {\n throw new Error(\n `assertContrast: could not parse the 'against' color for ${describeElement(element)}`\n );\n }\n\n const foreground = fgRaw.a < 1 ? compositeOver(fgRaw, background) : fgRaw;\n const ratio = contrastRatio(foreground, background);\n const passes = ratio >= options.min;\n\n if (!passes) {\n throw new Error(\n `Contrast ${ratio.toFixed(2)}:1 is below the required ${options.min}:1 for ` +\n `${describeElement(element)} — foreground ${fmt(foreground)} on background ${fmt(background)}.`\n );\n }\n\n return { ratio, foreground, background, min: options.min, passes };\n}\n"
7
7
  ],
8
- "mappings": "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",
9
- "debugId": "38512E3938AFDCFA64756E2164756E21",
8
+ "mappings": "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",
9
+ "debugId": "6107E968AC1112A564756E2164756E21",
10
10
  "names": []
11
11
  }
@@ -0,0 +1,19 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * @fairfox/polly/test/coverage — consumer-facing `polly coverage`.
4
+ *
5
+ * Zero-config: run it in any Polly project and it reports per-file coverage,
6
+ * orphan source files, and (if a Stryker config is present) dead mutate/test
7
+ * globs. Add a `coverage.config.ts` with a `defaultThreshold` to turn the
8
+ * report into an enforced gate, and `exempt` entries to record which
9
+ * higher-tier test covers a unit-thin file.
10
+ *
11
+ * Usage:
12
+ * polly coverage # report (zero-config) or enforce (with config)
13
+ * polly coverage --strict-orphans # fail on source no unit test imports
14
+ * polly coverage --orphans # list the orphan files
15
+ * polly coverage --no-mutate # skip the Stryker target check
16
+ * polly coverage --config <path> # explicit coverage.config.ts
17
+ * bun test --coverage | polly coverage --stdin
18
+ */
19
+ export {};
@@ -0,0 +1,339 @@
1
+ #!/usr/bin/env bun
2
+ import { createRequire } from "node:module";
3
+ var __defProp = Object.defineProperty;
4
+ var __returnValue = (v) => v;
5
+ function __exportSetter(name, newValue) {
6
+ this[name] = __returnValue.bind(null, newValue);
7
+ }
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, {
11
+ get: all[name],
12
+ enumerable: true,
13
+ configurable: true,
14
+ set: __exportSetter.bind(all, name)
15
+ });
16
+ };
17
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
18
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
19
+
20
+ // tools/test/src/coverage-policy/enforce.ts
21
+ var exports_enforce = {};
22
+ __export(exports_enforce, {
23
+ runCoverage: () => runCoverage,
24
+ parseCoverageTable: () => parseCoverageTable,
25
+ hasFailure: () => hasFailure,
26
+ evaluateCoverage: () => evaluateCoverage,
27
+ enforceCoverage: () => enforceCoverage
28
+ });
29
+ import { existsSync as existsSync2 } from "node:fs";
30
+ import { join as join2, resolve as resolve2 } from "node:path";
31
+ import { Glob } from "bun";
32
+ function normalizePath(raw) {
33
+ return raw.replace(/^(?:\.\.\/)+/, "");
34
+ }
35
+ function parseCoverageTable(text, srcDir) {
36
+ const prefix = `${srcDir}/`;
37
+ const rows = [];
38
+ for (const line of text.split(`
39
+ `)) {
40
+ if (!line.includes("|"))
41
+ continue;
42
+ if (line.includes("All files") || line.includes("% Funcs"))
43
+ continue;
44
+ if (line.trim().startsWith("---"))
45
+ continue;
46
+ const cells = line.split("|").map((c) => c.trim());
47
+ if (cells.length < 3)
48
+ continue;
49
+ const file = normalizePath(cells[0] ?? "");
50
+ const funcs = Number(cells[1]);
51
+ const lines = Number(cells[2]);
52
+ if (!file.startsWith(prefix) || Number.isNaN(funcs) || Number.isNaN(lines))
53
+ continue;
54
+ rows.push({ file, funcs, lines });
55
+ }
56
+ return rows;
57
+ }
58
+ async function runCoverage(root, testCwd) {
59
+ const proc = Bun.spawn(["bun", "test", "--coverage"], {
60
+ cwd: join2(root, testCwd),
61
+ stdout: "pipe",
62
+ stderr: "pipe"
63
+ });
64
+ const [out, err] = await Promise.all([
65
+ new Response(proc.stdout).text(),
66
+ new Response(proc.stderr).text()
67
+ ]);
68
+ await proc.exited;
69
+ if (proc.exitCode !== 0) {
70
+ throw new Error(`bun test --coverage exited ${proc.exitCode}
71
+ ${err}`);
72
+ }
73
+ return `${out}
74
+ ${err}`;
75
+ }
76
+ function evaluateRows(rows, config) {
77
+ const t = config.defaultThreshold;
78
+ const exempt = config.exempt ?? {};
79
+ const violations = [];
80
+ const staleExempts = [];
81
+ if (!t)
82
+ return { violations, staleExempts };
83
+ for (const row of rows) {
84
+ if (exempt[row.file]) {
85
+ if (row.lines >= t.lines && row.funcs >= t.funcs)
86
+ staleExempts.push(row.file);
87
+ continue;
88
+ }
89
+ if (row.lines < t.lines) {
90
+ violations.push({ file: row.file, metric: "lines", observed: row.lines, required: t.lines });
91
+ }
92
+ if (row.funcs < t.funcs) {
93
+ violations.push({ file: row.file, metric: "funcs", observed: row.funcs, required: t.funcs });
94
+ }
95
+ }
96
+ return { violations, staleExempts };
97
+ }
98
+ function validateExemptions(root, config) {
99
+ const missingExemptFiles = [];
100
+ const missingClaimedBy = [];
101
+ for (const [file, entry] of Object.entries(config.exempt ?? {})) {
102
+ if (!existsSync2(resolve2(root, file)))
103
+ missingExemptFiles.push(file);
104
+ const claimedBy = entry.claimedBy.trim();
105
+ if (!claimedBy.startsWith("n/a") && !existsSync2(resolve2(root, claimedBy))) {
106
+ missingClaimedBy.push({ file, claimedBy });
107
+ }
108
+ }
109
+ return { missingExemptFiles, missingClaimedBy };
110
+ }
111
+ async function findOrphans(root, srcDir, covered, config) {
112
+ const exempt = config.exempt ?? {};
113
+ const orphans = [];
114
+ const glob = new Glob(`${srcDir}/**/*.{ts,tsx}`);
115
+ for await (const file of glob.scan({ cwd: root, onlyFiles: true })) {
116
+ if (file.endsWith(".d.ts") || /\.test\.tsx?$/.test(file) || file.includes("/__tests__/")) {
117
+ continue;
118
+ }
119
+ if (covered.has(file) || exempt[file])
120
+ continue;
121
+ orphans.push(file);
122
+ }
123
+ return orphans.sort();
124
+ }
125
+ async function evaluateCoverage(root, rows, config) {
126
+ const srcDir = config.srcDir ?? DEFAULT_SRC;
127
+ const { violations, staleExempts } = evaluateRows(rows, config);
128
+ const { missingExemptFiles, missingClaimedBy } = validateExemptions(root, config);
129
+ const orphans = await findOrphans(root, srcDir, new Set(rows.map((r) => r.file)), config);
130
+ return {
131
+ rowCount: rows.length,
132
+ violations,
133
+ staleExempts,
134
+ missingExemptFiles,
135
+ missingClaimedBy,
136
+ orphans,
137
+ enforced: config.defaultThreshold !== undefined
138
+ };
139
+ }
140
+ async function enforceCoverage(root, config, coverageText) {
141
+ const srcDir = config.srcDir ?? DEFAULT_SRC;
142
+ const text = coverageText ?? await runCoverage(root, config.testCwd ?? ".");
143
+ const rows = parseCoverageTable(text, srcDir);
144
+ return evaluateCoverage(root, rows, config);
145
+ }
146
+ function hasFailure(findings, strictOrphans) {
147
+ return findings.violations.length > 0 || findings.staleExempts.length > 0 || findings.missingExemptFiles.length > 0 || findings.missingClaimedBy.length > 0 || strictOrphans && findings.orphans.length > 0;
148
+ }
149
+ var DEFAULT_SRC = "src";
150
+ var init_enforce = () => {};
151
+
152
+ // tools/test/src/coverage-policy/discover.ts
153
+ import { existsSync } from "node:fs";
154
+ import { isAbsolute, join, resolve } from "node:path";
155
+ function isCoverageConfig(value) {
156
+ return typeof value === "object" && value !== null;
157
+ }
158
+ async function importConfig(path) {
159
+ const mod = await import(path);
160
+ const candidate = mod["config"] ?? mod["default"];
161
+ if (!isCoverageConfig(candidate)) {
162
+ throw new Error(`${path} must export \`config\` (a CoverageConfig object)`);
163
+ }
164
+ return candidate;
165
+ }
166
+ async function loadCoverageConfig(root, explicitPath) {
167
+ if (explicitPath) {
168
+ const abs = isAbsolute(explicitPath) ? explicitPath : resolve(root, explicitPath);
169
+ if (!existsSync(abs))
170
+ throw new Error(`coverage config not found: ${abs}`);
171
+ return { config: await importConfig(abs), source: abs };
172
+ }
173
+ for (const name of ["coverage.config.ts", "coverage.config.js"]) {
174
+ const abs = join(root, name);
175
+ if (existsSync(abs))
176
+ return { config: await importConfig(abs), source: abs };
177
+ }
178
+ return { config: {}, source: null };
179
+ }
180
+
181
+ // tools/test/src/coverage-policy/cli.ts
182
+ init_enforce();
183
+
184
+ // tools/test/src/coverage-policy/mutate-targets.ts
185
+ import { existsSync as existsSync3, readFileSync } from "node:fs";
186
+ import { join as join3, resolve as resolve3 } from "node:path";
187
+ import { Glob as Glob2 } from "bun";
188
+ async function findStrykerConfigs(root) {
189
+ const found = [];
190
+ const single = join3(root, "stryker.conf.json");
191
+ if (existsSync3(single))
192
+ found.push(single);
193
+ const glob = new Glob2("stryker/*.{json,conf.json}");
194
+ for await (const rel of glob.scan({ cwd: root, onlyFiles: true })) {
195
+ found.push(join3(root, rel));
196
+ }
197
+ return found.sort();
198
+ }
199
+ function isGlob(pattern) {
200
+ return pattern.includes("*") || pattern.includes("?") || pattern.includes("[");
201
+ }
202
+ async function resolvesToFile(pattern, cwd) {
203
+ if (pattern.startsWith("!"))
204
+ return true;
205
+ if (!isGlob(pattern))
206
+ return existsSync3(resolve3(cwd, pattern));
207
+ const glob = new Glob2(pattern);
208
+ for await (const _ of glob.scan({ cwd, onlyFiles: true }))
209
+ return true;
210
+ return false;
211
+ }
212
+ async function checkField(configPath, field, patterns, cwd) {
213
+ const issues = [];
214
+ for (const pattern of patterns ?? []) {
215
+ if (!await resolvesToFile(pattern, cwd)) {
216
+ issues.push({ config: configPath, field, pattern });
217
+ }
218
+ }
219
+ return issues;
220
+ }
221
+ async function validateMutateTargets(root) {
222
+ const configs = await findStrykerConfigs(root);
223
+ const issues = [];
224
+ for (const configPath of configs) {
225
+ const config = JSON.parse(readFileSync(configPath, "utf8"));
226
+ const testFiles = config.testFiles ?? config.bun?.testFiles;
227
+ issues.push(...await checkField(configPath, "mutate", config.mutate, root));
228
+ issues.push(...await checkField(configPath, "testFiles", testFiles, root));
229
+ }
230
+ return { configs, issues };
231
+ }
232
+
233
+ // tools/test/src/coverage-policy/cli.ts
234
+ function parseArgs(argv) {
235
+ const flag = (name) => argv.includes(name);
236
+ const argValue = (name) => {
237
+ const i = argv.indexOf(name);
238
+ return i >= 0 ? argv[i + 1] : undefined;
239
+ };
240
+ const strictOrphans = flag("--strict-orphans");
241
+ return {
242
+ root: process.cwd(),
243
+ configPath: argValue("--config"),
244
+ strictOrphans,
245
+ listOrphans: strictOrphans || flag("--orphans"),
246
+ stdin: flag("--stdin"),
247
+ mutate: !flag("--no-mutate"),
248
+ help: flag("--help") || flag("-h")
249
+ };
250
+ }
251
+ async function readStdin() {
252
+ const chunks = [];
253
+ for await (const chunk of Bun.stdin.stream())
254
+ chunks.push(new TextDecoder().decode(chunk));
255
+ return chunks.join("");
256
+ }
257
+ function reportPolicy(f) {
258
+ for (const m of f.missingExemptFiles) {
259
+ process.stderr.write(`❌ exempt source missing: ${m}
260
+ `);
261
+ }
262
+ for (const { file, claimedBy } of f.missingClaimedBy) {
263
+ process.stderr.write(`❌ ${file} → claimedBy missing: ${claimedBy}
264
+ `);
265
+ }
266
+ for (const s of f.staleExempts) {
267
+ process.stderr.write(`❌ exempt file now meets the floor — promote it: ${s}
268
+ `);
269
+ }
270
+ for (const v of f.violations) {
271
+ process.stderr.write(`❌ ${v.file} ${v.metric}=${v.observed.toFixed(2)}% (need ≥ ${v.required}%)
272
+ `);
273
+ }
274
+ }
275
+ function reportOrphans(f, args) {
276
+ if (f.orphans.length === 0)
277
+ return;
278
+ process.stderr.write(`${args.strictOrphans ? "❌" : "⚠️ "} ${f.orphans.length} src file(s) no unit test imports
279
+ `);
280
+ if (args.listOrphans)
281
+ for (const o of f.orphans)
282
+ process.stderr.write(` ${o}
283
+ `);
284
+ else
285
+ process.stderr.write(` --orphans to list, --strict-orphans to fail
286
+ `);
287
+ }
288
+ function reportMutate(report) {
289
+ if (report.issues.length === 0)
290
+ return;
291
+ process.stderr.write(`❌ ${report.issues.length} Stryker target(s) resolve to no files:
292
+ `);
293
+ for (const i of report.issues) {
294
+ process.stderr.write(` ${i.config} [${i.field}] ${i.pattern}
295
+ `);
296
+ }
297
+ }
298
+ function showHelp() {
299
+ process.stdout.write(`polly coverage — coverage policy, orphan detection, Stryker target validation
300
+
301
+ ` + ` --strict-orphans fail on source no unit test imports
302
+ ` + ` --orphans list orphan files
303
+ ` + ` --no-mutate skip the Stryker mutate/testFiles check
304
+ ` + ` --config <path> explicit coverage.config.ts
305
+ ` + " --stdin read a `bun test --coverage` table from stdin\n");
306
+ }
307
+ async function main() {
308
+ const args = parseArgs(process.argv.slice(2));
309
+ if (args.help) {
310
+ showHelp();
311
+ return;
312
+ }
313
+ const { config, source } = await loadCoverageConfig(args.root, args.configPath);
314
+ const srcDir = config.srcDir ?? "src";
315
+ const findings = args.stdin ? await Promise.resolve().then(() => (init_enforce(), exports_enforce)).then(async (m) => {
316
+ const rows = parseCoverageTable(await readStdin(), srcDir);
317
+ return m.evaluateCoverage(args.root, rows, config);
318
+ }) : await enforceCoverage(args.root, config);
319
+ reportPolicy(findings);
320
+ reportOrphans(findings, args);
321
+ let mutate = { configs: [], issues: [] };
322
+ if (args.mutate) {
323
+ mutate = await validateMutateTargets(args.root);
324
+ reportMutate(mutate);
325
+ }
326
+ const failed = hasFailure(findings, args.strictOrphans) || mutate.issues.length > 0;
327
+ if (!failed) {
328
+ const mode = findings.enforced ? `floor enforced` : "report-only (no defaultThreshold)";
329
+ const where = source ? "" : " — zero-config";
330
+ const orphanNote = findings.orphans.length ? `, ${findings.orphans.length} orphan` : "";
331
+ const mutateNote = mutate.configs.length ? `, ${mutate.configs.length} stryker config(s) ok` : "";
332
+ process.stdout.write(`✅ coverage ok — ${findings.rowCount} src files, ${mode}${where}${orphanNote}${mutateNote}
333
+ `);
334
+ }
335
+ process.exit(failed ? 1 : 0);
336
+ }
337
+ await main();
338
+
339
+ //# debugId=7B6414401C7DC44464756E2164756E21