diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..fad5ae1 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,13 @@ +version: 2 +updates: + - package-ecosystem: npm + directory: / + schedule: + interval: monthly + groups: + dev-dependencies: + dependency-type: development + - package-ecosystem: github-actions + directory: / + schedule: + interval: monthly diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..096ce07 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,50 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + +permissions: + contents: read + +jobs: + check: + name: build · verify · test (Node ${{ matrix.node }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + node: [22, 24] + steps: + - uses: actions/checkout@v4 + + - uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node }} + cache: npm + + - run: npm ci + + - name: Build themes from _src + run: npm run build:themes + + - name: Generated themes must be committed + run: git diff --exit-code -- themes/ + + - name: Verify palette (L*, APCA, CVD, ANSI, wavelength, parity, README tables) + run: npm run verify + + - name: Unit + pipeline tests + run: npm test + + - name: Package VSIX + run: npx vsce package --out alone.vsix + + - name: Upload VSIX + if: matrix.node == 24 + uses: actions/upload-artifact@v4 + with: + name: alone-vsix + path: alone.vsix + if-no-files-found: error diff --git a/CHANGELOG.md b/CHANGELOG.md index 0936f30..094b602 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,19 @@ All notable changes to the **Alone** theme will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [Unreleased] + +### Changed — Internal: verifier v2, tests, CI (no palette changes) + +- **Verifier v2** (`scripts/verify-palette.mjs`). New checks alongside the L\* ladder, wavelength band, and key parity: an **APCA Lc** column and per-role floors (body 60 / syntax 40 / special 37 / punctuation 28 / comments 22, overridable per variant via `verify.apcaFloors` — Alone Soft declares a scaled set); **ΔE2000** on every tight ladder gap; a **colour-vision-deficiency** table (Viénot protan/deutan/tritan simulation) over the ten most confusable role pairs, passing on ΔE2000 ≥ 5 or a font-style difference; **ANSI** pairwise ΔE2000 among the eight normal terminal slots (≥ 10); and a **whole-theme wavelength scan** — every chromatic hex in every variant, not just the ten headline roles, must have a dominant wavelength ≥ 575 nm. The new perceptual checks report as **warnings** in this release (the current palette misses several — comments Lc 16, ANSI blue/cyan ΔE 5.2, Variables/Parameter under CVD); they become hard failures with the 2.0.0 retune. Each check's severity is a one-line `POLICY` entry. +- **README tables are now rendered by the verifier.** The L\* ladder, Syntax Colors table, bracket list, contrast table (now WCAG **and** APCA) and a new ANSI table live between `` markers; `node scripts/verify-palette.mjs --write-readme` re-renders them, and the default run fails on drift. +- **Colour math extracted** to `scripts/lib/color.mjs` (sRGB/XYZ/Lab, WCAG, APCA 0.0.98G-4, CIEDE2000, CVD matrices, dominant wavelength) and the generator's substitution/validation rules to `scripts/lib/build.mjs`, so both are unit-testable. `build-themes.mjs` output is byte-identical. +- **Tests** (`npm test`, `node:test`): colour-math sanity (21:1, L\* 100, APCA ±106/108, ΔE2000 red/green ≈ 86.6, sRGB primaries' λd), generator edge cases (bare vs interpolated tokens, unknown/unused tokens, non-object variant roots, path-escaping filenames), build determinism, committed-JSON freshness, `$schema` presence, v1.2.0 snapshot parity (skippable with `PALETTE_CHANGED=1`), and a verifier smoke run. +- **CI** (`.github/workflows/ci.yml`): Node 22 + 24 — `npm ci` → build → `git diff --exit-code themes/` → verify → test → `vsce package` (VSIX uploaded as an artifact). Dependabot for npm and GitHub Actions, monthly. +- `@vscode/vsce` added as a devDependency; `npm run package` builds the VSIX. + +--- + ## [1.3.1] - 2026-08-18 ### Changed — Packaging hygiene (no palette changes) diff --git a/README.md b/README.md index 39aad4f..8211a94 100644 --- a/README.md +++ b/README.md @@ -47,20 +47,24 @@ Pure black (#000000) backgrounds cause halation—a glowing halo effect around b On calibrated OLED and miniLED displays, subtle color differences disappear. **Alone** ensures each syntax element has distinct L\* (perceptual lightness) values, computed in CIE L\*a\*b\* against the editor background `#0C0A09`: + + ```text -L* 81 ████████████████████████████ Operators +L* 81 ███████████████████████████ Operators L* 75 █████████████████████████ Variables L* 73 ████████████████████████ Numbers -L* 68 █████████████████████ Keywords (bold) -L* 65 ████████████████████ Types -L* 62 ███████████████████ Functions -L* 58 █████████████████ Strings (italic) -L* 55 ████████████████ Special / regex -L* 49 ██████████████ Punctuation -L* 36 █████████ Comments (italic) +L* 68 ███████████████████████ Keywords (bold) +L* 65 ██████████████████████ Types +L* 61 ████████████████████ Functions +L* 58 ███████████████████ Strings (italic) +L* 55 ██████████████████ Special +L* 48 ████████████████ Punctuation +L* 36 ████████████ Comments (italic) ``` -The ladder is monotonic descending. The warm-only palette can't deliver large gaps across all ten tiers — Operators (L\* 81) down to Comments (L\* 36) is a ~45-unit range. The largest gaps cluster at the top and bottom (Operators→Variables 5.9, Special→Punctuation 6.7, Punctuation→Comments 12.3); the middle tiers run in the ~3 L\* range and rely on font style (italic strings/comments/interfaces/`defaultLibrary`, bold keywords/escapes) and hue (gold vs olive vs dusty-rose) to carry the differentiation that lightness alone can't. Run `node scripts/verify-palette.mjs` to recompute these against the live theme files; the verifier hard-fails on out-of-order roles and warns on any adjacent gap below 3 L\*. + + +The ladder is monotonic descending. The warm-only palette can't deliver large gaps across all ten tiers — Operators (L\* 81) down to Comments (L\* 36) is a ~45-unit range. The largest gaps cluster at the top and bottom (Operators→Variables 5.9, Special→Punctuation 6.7, Punctuation→Comments 12.3); the middle tiers run in the ~3 L\* range and rely on font style (italic strings/comments/interfaces/`defaultLibrary`, bold keywords/escapes) and hue (gold vs olive vs dusty-rose) to carry the differentiation that lightness alone can't. The ladder above is rendered by `scripts/verify-palette.mjs` from the live theme files; the verifier hard-fails on out-of-order roles and warns on any adjacent gap below 3 L\* (or ΔE2000 below 4). --- @@ -68,32 +72,40 @@ The ladder is monotonic descending. The warm-only palette can't deliver large ga ### Syntax Colors -| Element | Hex | Style | -| -------------------- | --------- | -------- | -| Operators | `#D4C8B8` | Normal | -| Variables | `#C4B8A4` | Normal | -| Numbers | `#E0A850` | Normal | -| Keywords | `#C8A040` | **Bold** | -| Types | `#BC9858` | Normal | -| Interface/Type-param | `#BC9858` | _Italic_ | -| Functions | `#C08868` | Normal | -| Built-in funcs | `#C08868` | _Italic_ | -| Strings | `#9A8B60` | _Italic_ | -| Escapes | `#D4B088` | **Bold** | -| Regex / decorators | `#A87878` | _Italic_ | -| Comments | `#5C544A` | _Italic_ | -| Errors | `#D46A66` | **Bold** | + + +| Element | Hex | Style | +| -------------------- | --------- | ------------------ | +| Operators | `#D4C8B8` | Normal | +| Variables | `#C4B8A4` | Normal | +| Numbers | `#E0A850` | Normal | +| Keywords | `#C8A040` | **Bold** | +| Types | `#BC9858` | Normal | +| Interface/Type-param | `#BC9858` | _Italic_ | +| Functions | `#C08868` | Normal | +| Built-in funcs | `#C08868` | _Italic_ | +| Strings | `#9A8B60` | _Italic_ | +| Escapes | `#D4B088` | **Bold** | +| Regex / decorators | `#A87878` | _Italic_ | +| Comments | `#5C544A` | _Italic_ | +| Errors | `#D46A66` | **Bold** underline | + + ### Bracket Pair Colors Six warm variants for bracket colorization, ordered by perceptual lightness so **depth maps to dimness** — the deeper you nest, the dimmer the bracket: -1. Bright Gold `#E0B868` (L\* ~78) -2. Gold `#C89868` (L\* ~67) + + +1. Bright Gold `#E0B868` (L\* ~77) +2. Gold `#C89868` (L\* ~66) 3. Amber-Brown `#B08458` (L\* ~58) 4. Umber `#967048` (L\* ~50) -5. Dark Umber `#7A5C3C` (L\* ~42) -6. Deep Brown `#604830` (L\* ~34) +5. Dark Umber `#7A5C3C` (L\* ~41) +6. Deep Brown `#604830` (L\* ~33) + + ### Background Hierarchy @@ -290,6 +302,23 @@ Matching terminal themes are included in the `terminal/` directory: All terminal themes use the same warm color palette with intentionally muted blue/cyan (replaced with warm grays). +The sixteen ANSI slots (as shipped in the VS Code theme and mirrored in the terminal files): + + + +| Slot | Hex | L\* | Bright slot | Hex | L\* | +| ------- | --------- | --- | ------------- | --------- | --- | +| Black | `#0C0A09` | 3 | BrightBlack | `#5C544A` | 36 | +| Red | `#B85450` | 48 | BrightRed | `#D46A66` | 57 | +| Green | `#9A8B60` | 58 | BrightGreen | `#C4B078` | 72 | +| Yellow | `#D4A048` | 69 | BrightYellow | `#E8B850` | 77 | +| Blue | `#8B8178` | 55 | BrightBlue | `#A89A8C` | 64 | +| Magenta | `#A87878` | 55 | BrightMagenta | `#C89088` | 65 | +| Cyan | `#9A8B7A` | 59 | BrightCyan | `#B8A898` | 70 | +| White | `#C4B8A4` | 75 | BrightWhite | `#E8DCD0` | 88 | + + + --- ## Language Support @@ -317,18 +346,22 @@ See `samples/` directory for demo files showcasing syntax highlighting. Key contrast ratios against the editor background (`#0C0A09`): -| Element | Color | Contrast Ratio | WCAG Level | -| --------- | --------- | -------------- | ----------------- | -| Operators | `#D4C8B8` | 12.0:1 | AAA | -| Variables | `#C4B8A4` | 10.1:1 | AAA | -| Numbers | `#E0A850` | 9.3:1 | AAA | -| Keywords | `#C8A040` | 8.1:1 | AAA | -| Types | `#BC9858` | 7.3:1 | AAA | -| Functions | `#C08868` | 6.6:1 | AA | -| Strings | `#9A8B60` | 5.9:1 | AA | -| Comments | `#5C544A` | 2.7:1 | — (de-emphasized) | + + +| Element | Color | WCAG | WCAG level | APCA Lc | +| --------- | --------- | ------ | ---------- | ------- | +| Operators | `#D4C8B8` | 12.0:1 | AAA | 74 | +| Variables | `#C4B8A4` | 10.1:1 | AAA | 65 | +| Numbers | `#E0A850` | 9.3:1 | AAA | 61 | +| Keywords | `#C8A040` | 8.1:1 | AAA | 54 | +| Types | `#BC9858` | 7.3:1 | AAA | 49 | +| Functions | `#C08868` | 6.6:1 | AA | 45 | +| Strings | `#9A8B60` | 5.9:1 | AA | 40 | +| Comments | `#5C544A` | 2.7:1 | — | 16 | -Computed against the editor background (`#0C0A09`) using the WCAG 2.x contrast formula. Comments intentionally use lower contrast to de-emphasize them. All primary code elements meet WCAG AA for normal text; five of eight clear AAA. Run `node scripts/verify-palette.mjs` to regenerate. + + +Computed against the editor background (`#0C0A09`) using the WCAG 2.x contrast formula. Comments intentionally use lower contrast to de-emphasize them. APCA Lc is the [APCA](https://github.com/Myndex/SAPC-APCA) (0.0.98G-4) lightness-contrast value; ≥ 60 is the body-text target, ≥ 45 large text, ≥ 30 the floor for non-essential text. All primary code elements meet WCAG AA for normal text; five of eight clear AAA. This table is rendered by `node scripts/verify-palette.mjs --write-readme`. ### Astigmatism Considerations @@ -406,14 +439,18 @@ The source of truth lives under `themes/_src/`: - **`themes/_src/base.yaml`** — the structural skeleton. Every key the variants share (shape, scopes, font styles, and any hex that happens to be identical across all variants) lives here as a literal. Every leaf that varies across variants is written as `${token.name}`. - **`themes/_src/variants/.yaml`** — per-variant bindings: `display`, `filename`, a `verify` block (which wavelength band the variant should pass and whether it's the "standard" used for the L\* ladder / README WCAG checks), and a `tokens:` block supplying the hex/alpha values for that variant's `${token.name}` references. -Two scripts: +The scripts: ```bash -npm run build:themes # regenerate themes/*.json from _src/ -npm run verify # check L*, WCAG, wavelength bands, and key parity -npm run check # both, in sequence +npm run build:themes # regenerate themes/*.json from _src/ +npm run verify # L* ladder, APCA floors, CVD + ANSI ΔE2000, wavelength scan, key parity, README tables +npm run check # both, in sequence +npm test # unit tests (colour math, generator rules) + pipeline tests (determinism, snapshot parity) +node scripts/verify-palette.mjs --write-readme # re-render the verifier-owned README tables after a palette change ``` +Every numeric table in this README that sits between `` markers is rendered by the verifier from the standard variant; edit the palette, run `--write-readme`, and commit the result — the default `verify` fails on drift. CI (`.github/workflows/ci.yml`) runs build → verify → test → `vsce package` on every push and pull request. + To add a new variant: copy an existing variants file, change `display` / `filename` / `verify`, fill in the `tokens:` block with the new palette, then `npm run check`. No edits to `base.yaml` are needed unless the new variant introduces a new key or scope (in which case all variants must add it — the parity check enforces this). To change the **shape** (add a scope, add a colors key, reorder tokenColors entries) edit `base.yaml`; the change applies uniformly to every variant. diff --git a/package-lock.json b/package-lock.json index 3969bff..11743ec 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,20 +1,3792 @@ { "name": "alone", - "version": "1.2.0", + "version": "1.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "alone", + "version": "1.3.1", + "license": "MIT", + "devDependencies": { + "@vscode/vsce": "^3.9.2", + "yaml": "^2.9.0" + }, + "engines": { + "vscode": "^1.74.0" + } + }, + "node_modules/@azu/format-text": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.2.tgz", + "integrity": "sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@azu/style-format": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azu/style-format/-/style-format-1.0.1.tgz", + "integrity": "sha512-AHcTojlNBdD/3/KxIKlg8sxIWHfOtQszLvOpagLTO+bjC3u7SAszu1lf//u7JJC50aUSH+BVWDD/KvaA6Gfn5g==", + "dev": true, + "license": "WTFPL", + "dependencies": { + "@azu/format-text": "^1.0.1" + } + }, + "node_modules/@azure/abort-controller": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.2.0.tgz", + "integrity": "sha512-fNAjWnA/nZ2jz31kxR/AqRaUT8ewHBw/WuBIosK0moMy1C9e5ValbDfFdIxJzVOOYaYkV/b2F1S4H/aHiqfVQg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.11.0.tgz", + "integrity": "sha512-IUZydyTUkDnYdstOW9pFOOUQlBjAepK5teihDE3x6yxsPJs/hsAaaYpeGxdxrgtOiJbBKSjKW7MDk7AEhb4LRg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.11.0.tgz", + "integrity": "sha512-JjQWO6akOck45PH/XBrxzsQGAiKrfFl4m5iggJ0ItMIz5omRufOXWpqCPpdjKN3vKDzlSUvFjaMb7Zwf0gvAdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.25.0.tgz", + "integrity": "sha512-bMs8ekJLjX8wPV+9IPBges1SLPyuDtE9g5gLDWOpxzKcoOFQnpLGkbcT1tdw3FaAmDS1gnPmMmJ6y/T5B96kIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.4.0.tgz", + "integrity": "sha512-eGwxD0AtncrxeBM4tG8R55Pc3rdX1hNW2WibJAgYpCVA6E93mvvVH+LcssoVjOBrSKWS55yEIHsk0X8ctHmfOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.14.0.tgz", + "integrity": "sha512-9n2pWK61veAuN0V20t9lOuoV4CFMdyAZ1ygZzvBGk/pBBJRib/PjL9PLXa/aI2CcPpyHfqVsxxqLCYl6uZlfDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.13.1", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.1.tgz", + "integrity": "sha512-5C/2WD5Vb1lHnZS16dNQRPMjN6oV/Upba+C9nBIs15PmOi6A3ZGs4Lr2u60zw4S04gi+u3cEXiqTVP7M4Pz3kw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^5.5.0", + "@azure/msal-node": "^5.1.0", + "open": "^10.1.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.4.0.tgz", + "integrity": "sha512-rbAE25KUfjU/s3XHUdJgceoCP5dEOpMx85J04kF+QMdta73XkuG9JGHHinch+XIoKpBdqljin+KqURpJriSzLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "5.18.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-5.18.0.tgz", + "integrity": "sha512-SPTeHYZghdEdRddJzNjhH+CI5MSQtquNYwGJnYXfOHIBRXCmrWimBS85OhwXpXFIlrCtNTbBPm5mPAWRNEoktA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.12.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "16.12.0", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.12.0.tgz", + "integrity": "sha512-hgLgfRdbG2AmhXPygebf1KYJEvse86+ZZLWufdiTKaGRYEUqOzHdlf6AS1IiuUCHWbynkgbHc451jSNkbfhWlg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.5.0.tgz", + "integrity": "sha512-A/2WIsuH0vsC6JVkkafjS4kHpi2LDR4AzDT0kJ+oIRtXYeYtvGQ2pwN2X88thQPhSek+82ela3MprsKXWQRrhQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/msal-common": "16.12.0", + "jsonwebtoken": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@secretlint/config-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-creator/-/config-creator-10.2.2.tgz", + "integrity": "sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-loader/-/config-loader-10.2.2.tgz", + "integrity": "sha512-ndjjQNgLg4DIcMJp4iaRD6xb9ijWQZVbd9694Ol2IszBIbGPPkwZHzJYKICbTBmh6AH/pLr0CiCaWdGJU7RbpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "ajv": "^8.17.1", + "debug": "^4.4.1", + "rc-config-loader": "^4.1.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/core": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz", + "integrity": "sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "structured-source": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/formatter/-/formatter-10.2.2.tgz", + "integrity": "sha512-10f/eKV+8YdGKNQmoDUD1QnYL7TzhI2kzyx95vsJKbEa8akzLAR5ZrWIZ3LbcMmBLzxlSQMMccRmi05yDQ5YDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "@textlint/linter-formatter": "^15.2.0", + "@textlint/module-interop": "^15.2.0", + "@textlint/types": "^15.2.0", + "chalk": "^5.4.1", + "debug": "^4.4.1", + "pluralize": "^8.0.0", + "strip-ansi": "^7.1.0", + "table": "^6.9.0", + "terminal-link": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@secretlint/node": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/node/-/node-10.2.2.tgz", + "integrity": "sha512-eZGJQgcg/3WRBwX1bRnss7RmHHK/YlP/l7zOQsrjexYt6l+JJa5YhUmHbuGXS94yW0++3YkEJp0kQGYhiw1DMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-loader": "^10.2.2", + "@secretlint/core": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "@secretlint/source-creator": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "p-map": "^7.0.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/profiler": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz", + "integrity": "sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/resolver": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/resolver/-/resolver-10.2.2.tgz", + "integrity": "sha512-3md0cp12e+Ae5V+crPQYGd6aaO7ahw95s28OlULGyclyyUtf861UoRGS2prnUrKh7MZb23kdDOyGCYb9br5e4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/secretlint-formatter-sarif": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-formatter-sarif/-/secretlint-formatter-sarif-10.2.2.tgz", + "integrity": "sha512-ojiF9TGRKJJw308DnYBucHxkpNovDNu1XvPh7IfUp0A12gzTtxuWDqdpuVezL7/IP8Ua7mp5/VkDMN9OLp1doQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-sarif-builder": "^3.2.0" + } + }, + "node_modules/@secretlint/secretlint-rule-no-dotenv": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz", + "integrity": "sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/secretlint-rule-preset-recommend": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz", + "integrity": "sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/source-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz", + "integrity": "sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2", + "istextorbinary": "^9.5.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/types": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz", + "integrity": "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@textlint/ast-node-types": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.8.0.tgz", + "integrity": "sha512-5CiH9COYmovWmExQgs7763DzX6Gy9zjkjJ7JxCC95wyTcjwQn/8poNF6fv3qzRlmx8CRRde8DHr9FcgAAiPzgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.8.0.tgz", + "integrity": "sha512-+oU3A235NATv6Lzi4xa4kJ65PuNJlIxesaO4AvDhDWA9FWm7y4XKWaoQCW1esgaQQ6dwnUiFKArQ8TcJ86mC4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azu/format-text": "^1.0.2", + "@azu/style-format": "^1.0.1", + "@textlint/module-interop": "15.8.0", + "@textlint/resolver": "15.8.0", + "@textlint/types": "15.8.0", + "debug": "^4.4.3", + "js-yaml": "^4.3.0", + "lodash": "^4.18.1", + "pluralize": "^2.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "table": "^6.9.0", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=20.18.0" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/pluralize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-2.0.0.tgz", + "integrity": "sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/module-interop": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.8.0.tgz", + "integrity": "sha512-rt+OR1WYGoLOY8HkA/aBPrqufF6yUUEsKEAh7XohTsT3lp9IyZFT6zOIbjul9P4FAzsmSPkcrYjVx3Bz/IUfkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/resolver": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.8.0.tgz", + "integrity": "sha512-E88tzfX3K8Jykk+38aJ9cy8RquD8ABVOPTO2rFEESq0wcg8x6/ypdAS8ZgR7OKiGqlRF0hkO/m5PbQwVfKM3VA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/types": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.8.0.tgz", + "integrity": "sha512-Anhc6y5736YIsvqae0U6k0YmB2M/QVHkEeOv2aydAn/WIkdI69dCOiDbe3/+RagS3qstFTSFWJzNRA2lUjv19w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@textlint/ast-node-types": "15.8.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.8", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.8.tgz", + "integrity": "sha512-bLMpVcWZNzq6lYOybwFwOAR1IXKcHnhUNqYeHjl1bET/qE3jFPFH+p8Wrh3rU4xwdnifPxmKNESBYnvnmc75aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@vscode/vsce": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.9.2.tgz", + "integrity": "sha512-XSxMosEEDO6vLxELAHVkwmhC0qe0ijZni2jB9Rcs8kQsW4lhTDQ/wMzmwFs/buotAWSnpmUp/dRWD2ufG3UYKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/identity": "^4.1.0", + "@secretlint/node": "^10.1.2", + "@secretlint/secretlint-formatter-sarif": "^10.1.2", + "@secretlint/secretlint-rule-no-dotenv": "^10.1.2", + "@secretlint/secretlint-rule-preset-recommend": "^10.1.2", + "@vscode/vsce-sign": "^2.0.0", + "azure-devops-node-api": "^12.5.0", + "chalk": "^4.1.2", + "cheerio": "^1.0.0-rc.9", + "cockatiel": "^3.1.2", + "commander": "^12.1.0", + "form-data": "^4.0.0", + "glob": "^13.0.6", + "hosted-git-info": "^4.0.2", + "jsonc-parser": "^3.2.0", + "leven": "^3.1.0", + "markdown-it": "^14.1.0", + "mime": "^1.3.4", + "minimatch": "^10.2.2", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "secretlint": "^10.1.2", + "semver": "^7.5.2", + "tmp": "^0.2.3", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.5.0", + "yauzl": "^3.2.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "keytar": "^7.7.0" + } + }, + "node_modules/@vscode/vsce-sign": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.1.0.tgz", + "integrity": "sha512-9AQrqazrBgTgRSuwleLVXUrIUphY02/SFCh2TKYoLV/xifJAdblhdmEmw5gUrYSPQ3sRwNs9iyCMD14sATEE6g==", + "dev": true, + "hasInstallScript": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optionalDependencies": { + "@vscode/vsce-sign-alpine-arm64": "2.0.6", + "@vscode/vsce-sign-alpine-x64": "2.0.6", + "@vscode/vsce-sign-darwin-arm64": "2.0.6", + "@vscode/vsce-sign-darwin-x64": "2.0.6", + "@vscode/vsce-sign-linux-arm": "2.0.6", + "@vscode/vsce-sign-linux-arm64": "2.0.6", + "@vscode/vsce-sign-linux-x64": "2.0.6", + "@vscode/vsce-sign-win32-arm64": "2.0.6", + "@vscode/vsce-sign-win32-x64": "2.0.6" + } + }, + "node_modules/@vscode/vsce-sign-alpine-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-arm64/-/vsce-sign-alpine-arm64-2.0.6.tgz", + "integrity": "sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-alpine-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-x64/-/vsce-sign-alpine-x64-2.0.6.tgz", + "integrity": "sha512-YoAGlmdK39vKi9jA18i4ufBbd95OqGJxRvF3n6ZbCyziwy3O+JgOpIUPxv5tjeO6gQfx29qBivQ8ZZTUF2Ba0w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.6.tgz", + "integrity": "sha512-5HMHaJRIQuozm/XQIiJiA0W9uhdblwwl2ZNDSSAeXGO9YhB9MH5C4KIHOmvyjUnKy4UCuiP43VKpIxW1VWP4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.6.tgz", + "integrity": "sha512-25GsUbTAiNfHSuRItoQafXOIpxlYj+IXb4/qarrXu7kmbH94jlm5sdWSCKrrREs8+GsXF1b+l3OB7VJy5jsykw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm/-/vsce-sign-linux-arm-2.0.6.tgz", + "integrity": "sha512-UndEc2Xlq4HsuMPnwu7420uqceXjs4yb5W8E2/UkaHBB9OWCwMd3/bRe/1eLe3D8kPpxzcaeTyXiK3RdzS/1CA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.6.tgz", + "integrity": "sha512-cfb1qK7lygtMa4NUl2582nP7aliLYuDEVpAbXJMkDq1qE+olIw/es+C8j1LJwvcRq1I2yWGtSn3EkDp9Dq5FdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.6.tgz", + "integrity": "sha512-/olerl1A4sOqdP+hjvJ1sbQjKN07Y3DVnxO4gnbn/ahtQvFrdhUi0G1VsZXDNjfqmXw57DmPi5ASnj/8PGZhAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-win32-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz", + "integrity": "sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vscode/vsce-sign-win32-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz", + "integrity": "sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz", + "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true, + "license": "Python-2.0" + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/azure-devops-node-api": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz", + "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==", + "dev": true, + "license": "MIT", + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/binaryextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz", + "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, + "node_modules/boundary": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz", + "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==", + "dev": true, + "license": "BSD-2-Clause" + }, + "node_modules/brace-expansion": { + "version": "5.0.9", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.9.tgz", + "integrity": "sha512-ScQ4IuvIEF1TMlP7Zt+vjJ//9zlPb2SDcxWxM3bk8s6t6GGdJ7KO1dCcTidOPJKePW30LE/2cT7wCyPho9/Wxg==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cheerio": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", + "integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.1.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.19.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/cockatiel": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", + "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/default-browser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", + "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", + "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/editions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz", + "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "version-range": "^4.15.0" + }, + "engines": { + "ecmascript": ">= es5", + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "license": "(MIT OR WTFPL)", + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-uri": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.5.tgz", + "integrity": "sha512-gHwA1O9LDIcKunMKhObS/HimwtehO1nPUECKAu5TpKgaO19fcWEl4bliWe1jWxVFvIXztJjjQ4L8XQ1EU9f7Jw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/fs-extra": { + "version": "11.4.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.0.tgz", + "integrity": "sha512-EQsFzMUJkCKGr1ePqlYADkIUmHW1s3ZXr5Yqy6wbGrfUCphpl2maM/kyOIRA2HpP3AaFQTZXD4ldjek+nccddA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "MIT", + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause", + "optional": true + }, + "node_modules/ignore": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz", + "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istextorbinary": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz", + "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "binaryextensions": "^6.11.0", + "editions": "^6.21.0", + "textextensions": "^6.11.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/js-yaml": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.1.tgz", + "integrity": "sha512-CY6crGq313MX8GkwvB7tzgp99vjQxY1++5y10/BKN/GUfHqWaOGQMNZkBvqSzsZKWk/ijwHlWzzkLulsGHhjWQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "dev": true, + "license": "MIT", + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/linkify-it": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/markdown-it": { + "version": "14.3.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.0.tgz", + "integrity": "sha512-RCEsPjR+sr0x+AuYp601tKTkgFG4YEPLCzHST3cQ/fhlJkqAkz1L2/Qbp1j9qw5SBwQHFBoW8+hoN5xssOF0Tw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.5.0", + "linkify-it": "^5.0.2", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdurl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz", + "integrity": "sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==", + "dev": true, + "license": "MIT" + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "optional": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true, + "license": "ISC" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-abi": { + "version": "3.94.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.94.0.tgz", + "integrity": "sha512-W5ZNO5KRPB5TkYmGVD9F6YqhsglXJzE6etpbmT+f6EQElhiX/UTG551cnsRGvLG3fyZEg9HwaDmNmj5nwJ4z9g==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-sarif-builder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.4.0.tgz", + "integrity": "sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "optional": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/open": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.6.tgz", + "integrity": "sha512-I4Prw6ivkd6p8PiYR1tXASOAOBzIJwu0TB7fqaX0c/8c3QAehNYmX57EijyGGGBt3c/BIowGwV03RVBtXvHEVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-semver": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", + "integrity": "sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^5.1.0" + } + }, + "node_modules/parse-semver/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "license": "MIT", + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.5.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz", + "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc-config-loader": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.4.tgz", + "integrity": "sha512-3GiwEzklkbXTDp52UR5nT8iXgYAx1V9ZG/kDZT7p60u2GCv2XTwQq4NzinMoMpNtXhmt3WkhYXcj6HH8HdwCEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "js-yaml": "^4.1.1", + "json5": "^2.2.3", + "require-from-string": "^2.0.2" + } + }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/secretlint": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/secretlint/-/secretlint-10.2.2.tgz", + "integrity": "sha512-xVpkeHV/aoWe4vP4TansF622nBEImzCY73y/0042DuJ29iKIaqgoJ8fGxre3rVSHHbxar4FdJobmTnLp9AU0eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-creator": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/node": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "debug": "^4.4.1", + "globby": "^14.1.0", + "read-pkg": "^9.0.1" + }, + "bin": { + "secretlint": "bin/secretlint.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, "license": "MIT", - "devDependencies": { - "yaml": "^2.9.0" + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" }, "engines": { - "vscode": "^1.74.0" + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "optional": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.23", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.23.tgz", + "integrity": "sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==", + "dev": true, + "license": "CC0-1.0" + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/structured-source": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", + "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boundary": "^2.0.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar-fs": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz", + "integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terminal-link": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-4.0.0.tgz", + "integrity": "sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "supports-hyperlinks": "^3.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/textextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz", + "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-rest-client": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", + "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/underscore": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", + "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz", + "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true, + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/version-range": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz", + "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==", + "dev": true, + "license": "Artistic-2.0", + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC", + "optional": true + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/yaml": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.9.0.tgz", @@ -30,6 +3802,29 @@ "funding": { "url": "https://github.com/sponsors/eemeli" } + }, + "node_modules/yauzl": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.4.0.tgz", + "integrity": "sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3" + } } } } diff --git a/package.json b/package.json index c5d32e1..1247b08 100644 --- a/package.json +++ b/package.json @@ -73,11 +73,14 @@ "markdown": "github", "qna": "marketplace", "devDependencies": { + "@vscode/vsce": "^3.9.2", "yaml": "^2.9.0" }, "scripts": { "build:themes": "node scripts/build-themes.mjs", "verify": "node scripts/verify-palette.mjs", - "check": "npm run build:themes && npm run verify" + "check": "npm run build:themes && npm run verify", + "test": "node --test tests/*.test.mjs", + "package": "vsce package" } } diff --git a/scripts/build-themes.mjs b/scripts/build-themes.mjs index 65b57f5..7620158 100644 --- a/scripts/build-themes.mjs +++ b/scripts/build-themes.mjs @@ -1,23 +1,14 @@ #!/usr/bin/env node /** - * Theme generator. + * Theme generator (CLI). * * Reads themes/_src/base.yaml (structural skeleton with ${token} placeholders * at leaves that vary across variants) and themes/_src/variants/*.yaml (per- * variant token bindings) and emits themes/.json for each variant. * - * Token substitution rule: - * - A leaf string of the form "${name}" is replaced by the variant's - * tokens[name] value verbatim. The replaced value can be any JSON type - * (string, number, boolean) — typically a hex color string. - * - A leaf string containing "${name}" inside other characters (e.g. - * "${accent}80" for an alpha-suffixed color) is replaced by string - * interpolation: each ${name} substring becomes the stringified token - * value. The current base.yaml only uses bare "${name}" leaves, but - * the interpolation path is supported for future hand-edited base.yaml - * entries that want to share a token across alpha variants. - * - Non-string leaves (numbers, booleans) and literal strings (no `${`) - * pass through unchanged. + * The substitution and validation rules live in scripts/lib/build.mjs so they + * can be unit-tested; this file only does discovery, I/O, and the cross- + * variant duplicate-filename check. * * Output ordering: object key order follows base.yaml insertion order * (preserved by the yaml package on parse and by JSON.stringify on emit). @@ -30,69 +21,13 @@ import fs from 'node:fs'; import path from 'node:path'; import url from 'node:url'; import YAML from 'yaml'; +import { validateVariant, buildTheme, serializeTheme } from './lib/build.mjs'; const HERE = path.dirname(url.fileURLToPath(import.meta.url)); const ROOT = path.join(HERE, '..'); const SRC = path.join(ROOT, 'themes', '_src'); const OUT = path.join(ROOT, 'themes'); -const TOKEN_RE = /\$\{([^}]+)\}/g; -const BARE_TOKEN_RE = /^\$\{([^}]+)\}$/; - -// Diagnostic helpers for the variant-shape validation below. `typeof null` -// is "object" and `typeof []` is "object" — both are useless in error -// messages, so describeType normalizes to the labels a user actually -// recognizes ("null", "array", "undefined", "string", …). isPlainObject -// is the dual: matches what `tokens` and the variant root must be. -function describeType(value) { - if (value === undefined) return 'undefined'; - if (value === null) return 'null'; - if (Array.isArray(value)) return 'array'; - return typeof value; -} - -function isPlainObject(value) { - return value !== null && typeof value === 'object' && !Array.isArray(value); -} - -function resolveValue(val, tokens, ctxPath, used) { - if (typeof val !== 'string') return val; - const bare = val.match(BARE_TOKEN_RE); - if (bare) { - const name = bare[1]; - if (!(name in tokens)) { - throw new Error(`unknown token "${name}" at ${ctxPath}`); - } - used.add(name); - return tokens[name]; - } - if (val.includes('${')) { - return val.replace(TOKEN_RE, (_, name) => { - if (!(name in tokens)) { - throw new Error(`unknown token "${name}" in interpolation at ${ctxPath}`); - } - used.add(name); - return String(tokens[name]); - }); - } - return val; -} - -function walk(node, tokens, used, pathArr = []) { - if (node === null || node === undefined) return node; - if (typeof node !== 'object') { - return resolveValue(node, tokens, pathArr.join('.'), used); - } - if (Array.isArray(node)) { - return node.map((item, i) => walk(item, tokens, used, [...pathArr, i])); - } - const out = {}; - for (const [k, v] of Object.entries(node)) { - out[k] = walk(v, tokens, used, [...pathArr, k]); - } - return out; -} - function loadYaml(filePath) { return YAML.parse(fs.readFileSync(filePath, 'utf8')); } @@ -110,66 +45,15 @@ if (variantFiles.length === 0) { } // Track resolved-path → source variant yaml so we can fail fast on -// collisions. We canonicalize via path.resolve so two variants with -// e.g. "foo.json" and "./foo.json" collide as the same destination. -// The error-message contract is "plain JSON names under themes/", so -// we also reject any filename containing a path separator (subdirectories -// are not part of the pipeline's output shape — all theme JSONs live -// flat in themes/). +// collisions. Canonicalized via path.resolve inside validateVariant so two +// variants with e.g. "foo.json" and "./foo.json" collide as the same +// destination. const seenFilenames = new Map(); const OUT_ABS = path.resolve(OUT); for (const f of variantFiles) { const variant = loadYaml(path.join(variantsDir, f)); - - // Variant-shape validation. yaml.parse will hand us whatever's in the - // source — including the document root being null (empty file), a - // scalar (e.g. just "42"), or an array — so guard at the boundary in - // three steps: root must be a mapping, filename must be a non-empty - // string, tokens must be a mapping. Without the root guard, the - // `variant.filename` access on a null root would throw a raw TypeError - // before our validation runs. - if (!isPlainObject(variant)) { - throw new Error( - `${f}: variant root must be a mapping/object (got ${describeType(variant)}).` - ); - } - if (typeof variant.filename !== 'string' || variant.filename.length === 0) { - throw new Error( - `${f}: "filename" must be a non-empty string (got ${describeType(variant.filename)}). ` + - `Variant filenames must be plain JSON names under themes/.` - ); - } - if (!isPlainObject(variant.tokens)) { - throw new Error( - `${f}: "tokens" must be a mapping of token-name → value (got ${describeType(variant.tokens)}).` - ); - } - - // Reject anything containing a path separator. Catches "sub/foo.json" - // (which would have passed the containment check below) and absolute - // paths like "/etc/passwd". On POSIX path.sep === '/' so the second - // disjunct is redundant; on Windows it catches backslash separators. - if (variant.filename.includes('/') || variant.filename.includes(path.sep)) { - throw new Error( - `${f}: filename "${variant.filename}" contains a path separator. ` + - `Variant filenames must be plain JSON names under themes/.` - ); - } - - // Canonical containment check. resolvedOut must live strictly inside - // OUT_ABS — anything else (filename of ".", "..", or a path that - // resolves outside the dir) is rejected. startsWith(OUT_ABS + sep) is - // the canonical pattern for "path inside directory"; it handles the - // empty-relative, parent-dir, and Windows-different-drive cases in - // one check. - const resolvedOut = path.resolve(OUT_ABS, variant.filename); - if (!resolvedOut.startsWith(OUT_ABS + path.sep)) { - throw new Error( - `${f}: filename "${variant.filename}" resolves outside themes/ (${resolvedOut}). ` + - `Variant filenames must be plain JSON names under themes/.` - ); - } + const resolvedOut = validateVariant(variant, f, OUT_ABS); if (seenFilenames.has(resolvedOut)) { const prior = seenFilenames.get(resolvedOut); @@ -180,25 +64,7 @@ for (const f of variantFiles) { } seenFilenames.set(resolvedOut, f); - // Resolve, tracking which tokens were actually referenced by base.yaml. - const used = new Set(); - const built = walk(base, variant.tokens, used); - - // Surface unused tokens — typo'd or stale declarations that silently ship - // wrong values when later edits add a reference under a slightly different - // name. Hard-fail rather than warn: a build that quietly drifts is exactly - // what this pipeline exists to prevent. - const declared = new Set(Object.keys(variant.tokens)); - const unused = [...declared].filter((t) => !used.has(t)); - if (unused.length > 0) { - throw new Error( - `${f}: ${unused.length} declared token(s) are not referenced by base.yaml ` + - `(typo or stale binding): ${unused.slice(0, 8).join(', ')}` + - (unused.length > 8 ? `, …(+${unused.length - 8} more)` : '') - ); - } - - // Match the snapshot format: 2-space indent, trailing newline. - fs.writeFileSync(resolvedOut, JSON.stringify(built, null, 2) + '\n'); + const built = buildTheme(base, variant, f); + fs.writeFileSync(resolvedOut, serializeTheme(built)); console.log(` wrote ${path.relative(ROOT, resolvedOut)} (${variant.display})`); } diff --git a/scripts/lib/build.mjs b/scripts/lib/build.mjs new file mode 100644 index 0000000..ab4b39a --- /dev/null +++ b/scripts/lib/build.mjs @@ -0,0 +1,148 @@ +/** + * Pure core of the theme generator (see scripts/build-themes.mjs for the + * CLI). Kept dependency-free and side-effect-free so tests can exercise the + * token-resolution and variant-validation rules directly. + * + * Token substitution rule: + * - A leaf string of the form "${name}" is replaced by the variant's + * tokens[name] value verbatim. The replaced value can be any JSON type + * (string, number, boolean) — typically a hex color string. + * - A leaf string containing "${name}" inside other characters (e.g. + * "${accent}80" for an alpha-suffixed color) is replaced by string + * interpolation: each ${name} substring becomes the stringified token + * value. + * - Non-string leaves (numbers, booleans) and literal strings (no `${`) + * pass through unchanged. + */ + +import path from 'node:path'; + +const TOKEN_RE = /\$\{([^}]+)\}/g; +const BARE_TOKEN_RE = /^\$\{([^}]+)\}$/; + +// Diagnostic helpers for the variant-shape validation below. `typeof null` +// is "object" and `typeof []` is "object" — both are useless in error +// messages, so describeType normalizes to the labels a user actually +// recognizes ("null", "array", "undefined", "string", …). isPlainObject +// is the dual: matches what `tokens` and the variant root must be. +export function describeType(value) { + if (value === undefined) return 'undefined'; + if (value === null) return 'null'; + if (Array.isArray(value)) return 'array'; + return typeof value; +} + +export function isPlainObject(value) { + return value !== null && typeof value === 'object' && !Array.isArray(value); +} + +export function resolveValue(val, tokens, ctxPath, used) { + if (typeof val !== 'string') return val; + const bare = val.match(BARE_TOKEN_RE); + if (bare) { + const name = bare[1]; + if (!(name in tokens)) { + throw new Error(`unknown token "${name}" at ${ctxPath}`); + } + used.add(name); + return tokens[name]; + } + if (val.includes('${')) { + return val.replace(TOKEN_RE, (_, name) => { + if (!(name in tokens)) { + throw new Error(`unknown token "${name}" in interpolation at ${ctxPath}`); + } + used.add(name); + return String(tokens[name]); + }); + } + return val; +} + +export function walk(node, tokens, used, pathArr = []) { + if (node === null || node === undefined) return node; + if (typeof node !== 'object') { + return resolveValue(node, tokens, pathArr.join('.'), used); + } + if (Array.isArray(node)) { + return node.map((item, i) => walk(item, tokens, used, [...pathArr, i])); + } + const out = {}; + for (const [k, v] of Object.entries(node)) { + out[k] = walk(v, tokens, used, [...pathArr, k]); + } + return out; +} + +/** + * Validate a parsed variant document and resolve its output path. + * + * Steps: root must be a mapping, filename must be a non-empty string with no + * path separators that resolves strictly inside outAbs, tokens must be a + * mapping. `f` is the source filename used in error messages. Returns the + * canonical absolute output path. + */ +export function validateVariant(variant, f, outAbs) { + if (!isPlainObject(variant)) { + throw new Error(`${f}: variant root must be a mapping/object (got ${describeType(variant)}).`); + } + if (typeof variant.filename !== 'string' || variant.filename.length === 0) { + throw new Error( + `${f}: "filename" must be a non-empty string (got ${describeType(variant.filename)}). ` + + `Variant filenames must be plain JSON names under themes/.` + ); + } + if (!isPlainObject(variant.tokens)) { + throw new Error( + `${f}: "tokens" must be a mapping of token-name → value (got ${describeType(variant.tokens)}).` + ); + } + + // Reject anything containing a path separator. Catches "sub/foo.json" + // (which would have passed the containment check below) and absolute + // paths like "/etc/passwd". On POSIX path.sep === '/' so the second + // disjunct is redundant; on Windows it catches backslash separators. + if (variant.filename.includes('/') || variant.filename.includes(path.sep)) { + throw new Error( + `${f}: filename "${variant.filename}" contains a path separator. ` + + `Variant filenames must be plain JSON names under themes/.` + ); + } + + // Canonical containment check. resolvedOut must live strictly inside + // outAbs — anything else (filename of ".", "..", or a path that resolves + // outside the dir) is rejected. + const resolvedOut = path.resolve(outAbs, variant.filename); + if (!resolvedOut.startsWith(outAbs + path.sep)) { + throw new Error( + `${f}: filename "${variant.filename}" resolves outside themes/ (${resolvedOut}). ` + + `Variant filenames must be plain JSON names under themes/.` + ); + } + return resolvedOut; +} + +/** + * Resolve base against a variant's tokens. Throws on unknown tokens and on + * declared-but-unreferenced tokens (typo'd or stale declarations that would + * otherwise silently ship wrong values). Returns the built theme object. + */ +export function buildTheme(base, variant, f) { + const used = new Set(); + const built = walk(base, variant.tokens, used); + const declared = new Set(Object.keys(variant.tokens)); + const unused = [...declared].filter((t) => !used.has(t)); + if (unused.length > 0) { + throw new Error( + `${f}: ${unused.length} declared token(s) are not referenced by base.yaml ` + + `(typo or stale binding): ${unused.slice(0, 8).join(', ')}` + + (unused.length > 8 ? `, …(+${unused.length - 8} more)` : '') + ); + } + return built; +} + +/** Serialize exactly as the pipeline writes it: 2-space indent, trailing newline. */ +export function serializeTheme(theme) { + return JSON.stringify(theme, null, 2) + '\n'; +} diff --git a/scripts/lib/color.mjs b/scripts/lib/color.mjs new file mode 100644 index 0000000..93ee6d3 --- /dev/null +++ b/scripts/lib/color.mjs @@ -0,0 +1,258 @@ +/** + * Colour math shared by the verifier and its tests. + * + * Everything here is pure: hex strings in, numbers (or hex strings) out. + * sRGB is assumed for every hex; alpha suffixes are ignored (the first six + * digits are used). + * + * References + * - sRGB → linear, luminance, WCAG 2.x contrast: W3C WCAG 2.1 §1.4.3 + * - CIE L*a*b* (D65, 2°): CIE 15:2004 + * - CIEDE2000: Sharma, Wu & Dalal (2005) — including the hue-mean edge cases + * - APCA: Somers, APCA 0.0.98G-4 (the SAPC/APCA "G" constants) + * - CVD simulation: Viénot, Brettel & Mollon (1999) linear-RGB matrices for + * protanopia/deuteranopia; Brettel-style tritan matrix in the same form + * - Dominant wavelength: ray from D65 white through the colour's chromaticity + * intersected with a tabulated CIE 1931 spectral locus (5–10 nm steps) + */ + +// ─── sRGB / XYZ / Lab ──────────────────────────────────────────────── + +/** '#RRGGBB' or '#RRGGBBAA' → [r, g, b] in 0..255. */ +export function hexToRgb255(hex) { + const h = hex.replace('#', '').slice(0, 6); + return [0, 2, 4].map((i) => parseInt(h.slice(i, i + 2), 16)); +} + +/** [r, g, b] in 0..255 → '#RRGGBB' (uppercase). */ +export function rgb255ToHex([r, g, b]) { + return '#' + [r, g, b].map((c) => Math.round(c).toString(16).padStart(2, '0')).join('').toUpperCase(); +} + +const srgbToLinear = (c) => (c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)); +const linearToSrgb = (c) => (c <= 0.0031308 ? 12.92 * c : 1.055 * Math.pow(c, 1 / 2.4) - 0.055); + +/** hex → linear-light [r, g, b] in 0..1. */ +export function hexToLinearRgb(hex) { + return hexToRgb255(hex).map((c) => srgbToLinear(c / 255)); +} + +/** linear-light [r, g, b] (0..1, clamped) → '#RRGGBB'. */ +export function linearRgbToHex(lin) { + return rgb255ToHex(lin.map((c) => linearToSrgb(Math.min(1, Math.max(0, c))) * 255)); +} + +/** hex → CIE XYZ (D65, Y=1 for white). */ +export function hexToXyz(hex) { + const [r, g, b] = hexToLinearRgb(hex); + return [ + 0.4124564 * r + 0.3575761 * g + 0.1804375 * b, + 0.2126729 * r + 0.7151522 * g + 0.072175 * b, + 0.0193339 * r + 0.119192 * g + 0.9503041 * b, + ]; +} + +/** WCAG relative luminance (0..1). */ +export function relativeLuminance(hex) { + const [r, g, b] = hexToLinearRgb(hex); + return 0.2126 * r + 0.7152 * g + 0.0722 * b; +} + +const D65 = [0.95047, 1.0, 1.08883]; + +/** hex → CIE L*a*b* (D65 white, 2° observer). */ +export function hexToLab(hex) { + const [X, Y, Z] = hexToXyz(hex); + const f = (t) => (t > 216 / 24389 ? Math.cbrt(t) : ((24389 / 27) * t + 16) / 116); + const [fx, fy, fz] = [X / D65[0], Y / D65[1], Z / D65[2]].map(f); + return [116 * fy - 16, 500 * (fx - fy), 200 * (fy - fz)]; +} + +/** hex → [L*, C*, h°] (h in 0..360). */ +export function hexToLch(hex) { + const [L, a, b] = hexToLab(hex); + return [L, Math.hypot(a, b), ((Math.atan2(b, a) * 180) / Math.PI + 360) % 360]; +} + +/** CIE L* alone (the ladder metric). */ +export function cielabL(hex) { + return hexToLab(hex)[0]; +} + +// ─── Contrast ──────────────────────────────────────────────────────── + +/** WCAG 2.x contrast ratio, ≥ 1. Order of arguments does not matter. */ +export function contrastRatio(a, b) { + const la = relativeLuminance(a); + const lb = relativeLuminance(b); + const [lighter, darker] = la > lb ? [la, lb] : [lb, la]; + return (lighter + 0.05) / (darker + 0.05); +} + +/** + * APCA Lc (0.0.98G-4). Positive for dark-on-light, negative for light-on-dark + * (light text on our near-black backgrounds comes out negative). Callers + * usually want Math.abs(). + */ +export function apcaLc(textHex, bgHex) { + const screenY = (hex) => { + const [r, g, b] = hexToRgb255(hex).map((c) => Math.pow(c / 255, 2.4)); + let y = 0.2126729 * r + 0.7151522 * g + 0.072175 * b; + if (y < 0.022) y += Math.pow(0.022 - y, 1.414); + return y; + }; + const yTxt = screenY(textHex); + const yBg = screenY(bgHex); + let sapc; + if (yBg > yTxt) sapc = (Math.pow(yBg, 0.56) - Math.pow(yTxt, 0.57)) * 1.14; + else sapc = (Math.pow(yBg, 0.65) - Math.pow(yTxt, 0.62)) * 1.14; + if (Math.abs(sapc) < 0.1) return 0; + return (sapc > 0 ? sapc - 0.027 : sapc + 0.027) * 100; +} + +// ─── ΔE2000 ────────────────────────────────────────────────────────── + +/** CIEDE2000 colour difference between two hexes (kL = kC = kH = 1). */ +export function deltaE2000(hexA, hexB) { + const [L1, a1, b1] = hexToLab(hexA); + const [L2, a2, b2] = hexToLab(hexB); + const d2r = Math.PI / 180; + const r2d = 180 / Math.PI; + const C1 = Math.hypot(a1, b1); + const C2 = Math.hypot(a2, b2); + const Cbar = (C1 + C2) / 2; + const G = 0.5 * (1 - Math.sqrt(Math.pow(Cbar, 7) / (Math.pow(Cbar, 7) + Math.pow(25, 7)))); + const a1p = (1 + G) * a1; + const a2p = (1 + G) * a2; + const C1p = Math.hypot(a1p, b1); + const C2p = Math.hypot(a2p, b2); + const hp = (a, b) => { + if (a === 0 && b === 0) return 0; + const h = Math.atan2(b, a) * r2d; + return h < 0 ? h + 360 : h; + }; + const h1p = hp(a1p, b1); + const h2p = hp(a2p, b2); + const dLp = L2 - L1; + const dCp = C2p - C1p; + let dhp; + if (C1p * C2p === 0) dhp = 0; + else if (Math.abs(h2p - h1p) <= 180) dhp = h2p - h1p; + else dhp = h2p - h1p > 180 ? h2p - h1p - 360 : h2p - h1p + 360; + const dHp = 2 * Math.sqrt(C1p * C2p) * Math.sin((dhp / 2) * d2r); + const Lbp = (L1 + L2) / 2; + const Cbp = (C1p + C2p) / 2; + let hbp; + if (C1p * C2p === 0) hbp = h1p + h2p; + else if (Math.abs(h1p - h2p) <= 180) hbp = (h1p + h2p) / 2; + else hbp = h1p + h2p < 360 ? (h1p + h2p + 360) / 2 : (h1p + h2p - 360) / 2; + const T = + 1 - + 0.17 * Math.cos((hbp - 30) * d2r) + + 0.24 * Math.cos(2 * hbp * d2r) + + 0.32 * Math.cos((3 * hbp + 6) * d2r) - + 0.2 * Math.cos((4 * hbp - 63) * d2r); + const dTheta = 30 * Math.exp(-Math.pow((hbp - 275) / 25, 2)); + const RC = 2 * Math.sqrt(Math.pow(Cbp, 7) / (Math.pow(Cbp, 7) + Math.pow(25, 7))); + const SL = 1 + (0.015 * Math.pow(Lbp - 50, 2)) / Math.sqrt(20 + Math.pow(Lbp - 50, 2)); + const SC = 1 + 0.045 * Cbp; + const SH = 1 + 0.015 * Cbp * T; + const RT = -Math.sin(2 * dTheta * d2r) * RC; + return Math.sqrt( + Math.pow(dLp / SL, 2) + Math.pow(dCp / SC, 2) + Math.pow(dHp / SH, 2) + RT * (dCp / SC) * (dHp / SH) + ); +} + +// ─── Colour-vision deficiency ──────────────────────────────────────── + +const CVD_MATRICES = { + protan: [ + [0.11238, 0.88762, 0], + [0.11238, 0.88762, 0], + [0.00401, -0.00401, 1], + ], + deutan: [ + [0.29275, 0.70725, 0], + [0.29275, 0.70725, 0], + [-0.02234, 0.02234, 1], + ], + tritan: [ + [1, 0.14461, -0.14461], + [0, 0.85924, 0.14076], + [0, 0.85924, 0.14076], + ], +}; + +export const CVD_TYPES = Object.keys(CVD_MATRICES); + +/** Simulate dichromatic vision. type ∈ 'protan' | 'deutan' | 'tritan'. Returns hex. */ +export function simulateCvd(hex, type) { + const m = CVD_MATRICES[type]; + if (!m) throw new Error(`unknown CVD type: ${type}`); + const v = hexToLinearRgb(hex); + return linearRgbToHex(m.map((row) => row[0] * v[0] + row[1] * v[1] + row[2] * v[2])); +} + +// ─── Dominant wavelength ───────────────────────────────────────────── + +// CIE 1931 2° spectral locus (x, y) at selected wavelengths. +const LOCUS = [ + [380, 0.1741, 0.005], [400, 0.1733, 0.0048], [420, 0.1714, 0.0051], [440, 0.1644, 0.0109], + [450, 0.1566, 0.0177], [460, 0.144, 0.0297], [470, 0.1241, 0.0578], [480, 0.0913, 0.1327], + [490, 0.0454, 0.295], [500, 0.0082, 0.5384], [510, 0.0139, 0.7502], [520, 0.0743, 0.8338], + [530, 0.1547, 0.8059], [540, 0.2296, 0.7543], [550, 0.3016, 0.6923], [555, 0.3373, 0.6589], + [560, 0.3731, 0.6245], [565, 0.4087, 0.5896], [570, 0.4441, 0.5547], [575, 0.4788, 0.5202], + [580, 0.5125, 0.4866], [585, 0.5448, 0.4544], [590, 0.5752, 0.4242], [595, 0.6029, 0.3965], + [600, 0.627, 0.3725], [605, 0.6482, 0.3514], [610, 0.6658, 0.334], [620, 0.6915, 0.3083], + [630, 0.7079, 0.292], [640, 0.719, 0.2809], [650, 0.726, 0.274], [700, 0.7347, 0.2653], +]; +const WHITE_XY = [0.3127, 0.329]; + +/** + * Dominant wavelength in nm and excitation purity (0..1) for a hex. + * Returns { lambda: NaN, purity: NaN } for neutrals (chroma too small to + * define a direction) and for colours on the purple line (no spectral + * intersection — magentas/purples). Callers should treat NaN as "not + * spectral" rather than as an error. + */ +export function dominantWavelength(hex) { + const [X, Y, Z] = hexToXyz(hex); + const s = X + Y + Z; + if (s === 0) return { lambda: NaN, purity: NaN }; + const x = X / s; + const y = Y / s; + const dx = x - WHITE_XY[0]; + const dy = y - WHITE_XY[1]; + if (Math.hypot(dx, dy) < 1e-4) return { lambda: NaN, purity: NaN }; + let best = null; + for (let i = 0; i < LOCUS.length - 1; i++) { + const [l1, x1, y1] = LOCUS[i]; + const [l2, x2, y2] = LOCUS[i + 1]; + const ex = x2 - x1; + const ey = y2 - y1; + const den = dx * ey - dy * ex; + if (Math.abs(den) < 1e-12) continue; + // Solve W + t·d = P1 + u·(P2 − P1). t > 0 → same side as the colour. + const t = ((x1 - WHITE_XY[0]) * ey - (y1 - WHITE_XY[1]) * ex) / den; + const u = ((x1 - WHITE_XY[0]) * dy - (y1 - WHITE_XY[1]) * dx) / den; + if (t > 0 && u >= 0 && u <= 1) { + // Excitation purity = |WC| / |WS|. Every sRGB colour lies inside the + // locus (t ≥ 1), so this is ≤ 1 up to float noise; clamp so callers + // can rely on the textbook [0, 1] range. + best = { lambda: l1 + u * (l2 - l1), purity: Math.min(1, 1 / t) }; + } + } + return best ? { lambda: Math.round(best.lambda), purity: best.purity } : { lambda: NaN, purity: NaN }; +} + +// ─── Misc helpers ──────────────────────────────────────────────────── + +/** True for '#RRGGBB' or '#RRGGBBAA'. */ +export function isHexColor(value) { + return typeof value === 'string' && /^#(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value); +} + +/** Uppercase '#RRGGBB' with any alpha suffix dropped. */ +export function normalizeHex(hex) { + return '#' + hex.replace('#', '').slice(0, 6).toUpperCase(); +} diff --git a/scripts/lib/readme.mjs b/scripts/lib/readme.mjs new file mode 100644 index 0000000..ab692d1 --- /dev/null +++ b/scripts/lib/readme.mjs @@ -0,0 +1,53 @@ +/** + * Markdown helpers for verifier-rendered README sections. + * + * A rendered block lives between two HTML-comment markers: + * + * + * + * ...generated markdown... + * + * + * + * The blank lines are part of the contract so the markdown that follows the + * start comment begins a fresh block rather than being swallowed into the + * HTML comment block. + */ + +export function startMarker(name) { + return ``; +} +export function endMarker(name) { + return ``; +} + +/** Full block text for a section body (body should not have surrounding blank lines). */ +export function renderBlock(name, body) { + return `${startMarker(name)}\n\n${body.replace(/\s+$/, '')}\n\n${endMarker(name)}`; +} + +/** Locate a marker block. Returns { start, end } indexes into `text` (inclusive of markers) or null. */ +export function findBlock(text, name) { + const s = text.indexOf(startMarker(name)); + if (s === -1) return null; + const e = text.indexOf(endMarker(name), s); + if (e === -1) throw new Error(`README: found ${startMarker(name)} without a matching end marker`); + return { start: s, end: e + endMarker(name).length }; +} + +/** Replace the block named `name` with `body`. Throws if the block is absent. */ +export function replaceBlock(text, name, body) { + const loc = findBlock(text, name); + if (!loc) throw new Error(`README: missing marker block "${name}" (${startMarker(name)} … ${endMarker(name)})`); + return text.slice(0, loc.start) + renderBlock(name, body) + text.slice(loc.end); +} + +/** GitHub-flavoured markdown table with padded columns. */ +export function mdTable(headers, rows) { + const cols = headers.length; + const widths = headers.map((h, i) => Math.max(h.length, ...rows.map((r) => String(r[i]).length))); + const line = (cells) => '| ' + cells.map((c, i) => String(c).padEnd(widths[i])).join(' | ') + ' |'; + const sep = '| ' + widths.map((w) => '-'.repeat(w)).join(' | ') + ' |'; + if (rows.some((r) => r.length !== cols)) throw new Error('mdTable: ragged rows'); + return [line(headers), sep, ...rows.map(line)].join('\n'); +} diff --git a/scripts/lib/theme-roles.mjs b/scripts/lib/theme-roles.mjs new file mode 100644 index 0000000..b8e290d --- /dev/null +++ b/scripts/lib/theme-roles.mjs @@ -0,0 +1,193 @@ +/** + * Extract the headline syntax roles, font styles, ANSI slots and every hex + * value from a built theme JSON. Pure functions; no I/O. + */ + +import { isHexColor, normalizeHex } from './color.mjs'; + +/** Foreground of a semanticTokenColors entry (string or { foreground }). */ +export function semanticForeground(theme, key) { + const entry = theme.semanticTokenColors?.[key]; + const hex = typeof entry === 'string' ? entry : entry?.foreground; + if (!hex) throw new Error(`semanticTokenColors["${key}"] not found or missing foreground`); + return hex; +} + +/** Style flags of a semanticTokenColors entry. */ +export function semanticStyle(theme, key) { + const entry = theme.semanticTokenColors?.[key]; + if (entry === undefined) throw new Error(`semanticTokenColors["${key}"] not found`); + if (typeof entry === 'string') return normalizeStyle({}); + return normalizeStyle({ + bold: entry.bold === true, + italic: entry.italic === true, + underline: entry.underline === true, + strikethrough: entry.strikethrough === true, + }); +} + +/** Foreground of the tokenColors entry with the given `name`. */ +export function tokenForeground(theme, name) { + const entry = theme.tokenColors.find((t) => t.name === name); + const hex = entry?.settings?.foreground; + if (!hex) throw new Error(`tokenColors entry "${name}" not found or missing foreground`); + return hex; +} + +/** Style flags of the tokenColors entry with the given `name`. */ +export function tokenStyle(theme, name) { + const entry = theme.tokenColors.find((t) => t.name === name); + if (!entry) throw new Error(`tokenColors entry "${name}" not found`); + const words = (entry.settings?.fontStyle ?? '').split(/\s+/).filter(Boolean); + return normalizeStyle({ + bold: words.includes('bold'), + italic: words.includes('italic'), + underline: words.includes('underline'), + strikethrough: words.includes('strikethrough'), + }); +} + +function normalizeStyle({ bold = false, italic = false, underline = false, strikethrough = false } = {}) { + return { bold, italic, underline, strikethrough }; +} + +/** True if two style objects differ in any flag (a visual cue beyond colour). */ +export function stylesDiffer(a, b) { + return a.bold !== b.bold || a.italic !== b.italic || a.underline !== b.underline || a.strikethrough !== b.strikethrough; +} + +/** Human label for a style, in the README's own vocabulary. */ +export function styleLabel(style) { + const parts = []; + if (style.bold) parts.push('**Bold**'); + if (style.italic) parts.push('_Italic_'); + if (style.underline) parts.push('underline'); + if (style.strikethrough) parts.push('strikethrough'); + return parts.length ? parts.join(' ') : 'Normal'; +} + +/** + * The ten-tier L* ladder, in documented order (top = brightest). Each entry + * names its source so the same list drives every variant. + */ +export const LADDER_ROLES = [ + { name: 'Operators', sem: 'operator' }, + { name: 'Variables', sem: 'variable' }, + { name: 'Numbers', sem: 'number' }, + { name: 'Keywords', sem: 'keyword' }, + { name: 'Types', sem: 'type' }, + { name: 'Functions', sem: 'function' }, + { name: 'Strings', sem: 'string' }, + { name: 'Special', sem: 'decorator' }, + { name: 'Punctuation', tm: 'Punctuation' }, + { name: 'Comments', sem: 'comment' }, +]; + +/** + * The README "Syntax Colors" table — a superset of the ladder with the + * font-style-differentiated roles included. + */ +export const SYNTAX_TABLE_ROLES = [ + { name: 'Operators', sem: 'operator' }, + { name: 'Variables', sem: 'variable' }, + { name: 'Numbers', sem: 'number' }, + { name: 'Keywords', sem: 'keyword' }, + { name: 'Types', sem: 'type' }, + { name: 'Interface/Type-param', sem: 'interface' }, + { name: 'Functions', sem: 'function' }, + { name: 'Built-in funcs', sem: 'function.defaultLibrary' }, + { name: 'Strings', sem: 'string' }, + { name: 'Escapes', tm: 'Escape Characters' }, + { name: 'Regex / decorators', sem: 'regexp' }, + { name: 'Comments', sem: 'comment' }, + { name: 'Errors', tm: 'Invalid Illegal' }, +]; + +/** Roles that behave like identifiers for the APCA "body text" floor. */ +export const IDENTIFIER_ROLES = ['Operators', 'Variables', 'Numbers', 'Keywords', 'Types', 'Functions']; + +/** Extra roles used by the CVD pair check. */ +const CVD_EXTRA_ROLES = [ + { name: 'Escapes', tm: 'Escape Characters' }, + { name: 'Errors', tm: 'Invalid Illegal' }, + { name: 'Parameter', sem: 'parameter' }, + { name: 'DefaultLib', sem: 'variable.defaultLibrary' }, +]; + +/** + * Pairs whose confusability matters most: adjacent ladder tiers that share + * hue family, plus the red-vs-warm pairs a red-green observer collapses. + * Each pair passes if ΔE2000 under simulation stays ≥ threshold OR the two + * roles differ in font style. + */ +export const CVD_PAIRS = [ + ['Types', 'Functions'], + ['Functions', 'Strings'], + ['Keywords', 'Types'], + ['Numbers', 'Keywords'], + ['Strings', 'Special'], + ['Errors', 'Functions'], + ['Errors', 'Special'], + ['Escapes', 'Variables'], + ['DefaultLib', 'Types'], + ['Variables', 'Parameter'], +]; + +/** Resolve a role descriptor to { name, hex, style } against a theme. */ +export function resolveRole(theme, role) { + if (role.sem) { + return { name: role.name, hex: semanticForeground(theme, role.sem), style: semanticStyle(theme, role.sem) }; + } + return { name: role.name, hex: tokenForeground(theme, role.tm), style: tokenStyle(theme, role.tm) }; +} + +/** name → { name, hex, style } for the ladder + CVD extras. */ +export function roleMap(theme) { + const out = {}; + for (const r of [...LADDER_ROLES, ...CVD_EXTRA_ROLES]) out[r.name] = resolveRole(theme, r); + return out; +} + +export const ANSI_SLOTS = [ + 'Black', 'Red', 'Green', 'Yellow', 'Blue', 'Magenta', 'Cyan', 'White', + 'BrightBlack', 'BrightRed', 'BrightGreen', 'BrightYellow', 'BrightBlue', 'BrightMagenta', 'BrightCyan', 'BrightWhite', +]; + +/** [{ slot, key, hex }] for the 16 terminal.ansi* colours. */ +export function ansiColors(theme) { + return ANSI_SLOTS.map((slot) => { + const key = `terminal.ansi${slot}`; + const hex = theme.colors?.[key]; + if (!hex) throw new Error(`colors["${key}"] missing`); + return { slot, key, hex }; + }); +} + +/** Six bracket-pair foregrounds in depth order. */ +export function bracketColors(theme) { + return [1, 2, 3, 4, 5, 6].map((i) => { + const key = `editorBracketHighlight.foreground${i}`; + const hex = theme.colors?.[key]; + if (!hex) throw new Error(`colors["${key}"] missing`); + return { depth: i, key, hex }; + }); +} + +/** + * Every hex-valued leaf in the theme: [{ path, hex }]. `hex` is normalized + * (uppercase, alpha stripped); `path` is dotted (tokenColors.12.settings.foreground). + */ +export function allHexes(theme) { + const out = []; + const visit = (node, p) => { + if (typeof node === 'string') { + if (isHexColor(node)) out.push({ path: p, hex: normalizeHex(node) }); + return; + } + if (node && typeof node === 'object') { + for (const [k, v] of Object.entries(node)) visit(v, p ? `${p}.${k}` : k); + } + }; + visit(theme, ''); + return out; +} diff --git a/scripts/verify-palette.mjs b/scripts/verify-palette.mjs index 1f654e3..9374b95 100644 --- a/scripts/verify-palette.mjs +++ b/scripts/verify-palette.mjs @@ -3,88 +3,108 @@ * Palette verifier for the Alone theme family. * * Discovers every variant under themes/_src/variants/*.yaml, loads the - * corresponding built JSON from themes/, and checks: + * corresponding built JSON from themes/, and runs these checks. Each check + * has a severity in POLICY below — `fail` exits non-zero, `warn` is reported + * but does not fail the run. (v2 introduced the perceptual checks as + * warnings; they flip to `fail` once the palette meets them.) * - * 1. L* ladder (on the "standard" variant) — every headline syntax - * role's L* descends in the documented order (monotonic). Out-of- - * order pairs hard-fail. Adjacent gaps below TIGHT_GAP_THRESHOLD - * (3 L*) emit a soft warning rather than failing, because the - * warm-only palette can't deliver large gaps across all ten tiers — - * the middle runs in the ~3 L* range and relies on font style + hue. - * 2. WCAG contrast ratios in README.md match computed values against - * the editor background (within ±WCAG_TOLERANCE), checked on the - * "standard" variant. - * 3. Wavelength constraint — per-variant. Each variant declares its - * wavelength band via `verify.wavelengthBand` in its yaml. Bands: - * warm — no syntax hex in the blue/cyan band (B>R and B>G) - * red-amber — long-wave dominant; R≥G≥B and R-B ≥ 0x40 - * red-only — strict mono-red; R≥G+0x30 and R≥B+0x60 - * Default is `warm` if unspecified. (Tighter bands are placeholders - * for future scotopic-red variants; no current variant uses them.) - * 4. Variant key parity — every variant declares the same set of - * `colors.*` keys and `semanticTokenColors.*` keys. + * 1. L* ladder (standard variant) — the ten headline roles descend in the + * documented order. Out-of-order → fail. Adjacent gaps below + * TIGHT_GAP_L (3 L*) or ΔE2000 below TIGHT_GAP_DE → tightGap. + * 2. APCA floors (every variant) — |Lc| against editor.background per + * role class (body / syntax / special / punctuation / comments). Floors + * come from APCA_FLOORS, overridable per variant via + * `verify.apcaFloors` in its yaml (Soft runs a scaled set). + * 3. CVD confusability (every variant) — for each pair in CVD_PAIRS the + * ΔE2000 after protan/deutan simulation must stay ≥ CVD_MIN_DE, or the + * two roles must differ in font style. + * 4. ANSI palette (every variant) — pairwise ΔE2000 among the eight + * normal slots ≥ ANSI_MIN_DE. + * 5. Wavelength — (a) the ten roles pass the variant's declared band + * (`verify.wavelengthBand`: warm | red-amber | red-only; fail), and + * (b) every hex anywhere in the theme with chroma ≥ SCAN_MIN_CHROMA has a + * dominant wavelength ≥ SCAN_MIN_NM (wavelengthScan). + * 6. Variant key parity — every variant declares the same `colors.*` and + * `semanticTokenColors.*` key sets. + * 7. README tables — the blocks between `` + * markers in README.md are rendered from the standard variant. Default + * mode fails on drift; `--write-readme` rewrites them in place. * - * Exits non-zero on any failure with a per-check report. - * - * Usage: node scripts/verify-palette.mjs + * Usage: node scripts/verify-palette.mjs [--write-readme] */ import fs from 'node:fs'; import path from 'node:path'; import url from 'node:url'; import YAML from 'yaml'; +import { + cielabL, contrastRatio, apcaLc, deltaE2000, simulateCvd, dominantWavelength, + hexToLch, hexToRgb255, isHexColor, +} from './lib/color.mjs'; +import { + LADDER_ROLES, SYNTAX_TABLE_ROLES, CVD_PAIRS, resolveRole, roleMap, stylesDiffer, + styleLabel, ansiColors, bracketColors, allHexes, +} from './lib/theme-roles.mjs'; +import { mdTable, replaceBlock, findBlock } from './lib/readme.mjs'; const HERE = path.dirname(url.fileURLToPath(import.meta.url)); const ROOT = path.join(HERE, '..'); const THEMES = path.join(ROOT, 'themes'); const VARIANTS_DIR = path.join(THEMES, '_src', 'variants'); +const README = path.join(ROOT, 'README.md'); -const TIGHT_GAP_THRESHOLD = 3.0; -const WCAG_TOLERANCE = 0.05; - -// ─── Color math ────────────────────────────────────────────────────── -function hexToLinearRgb(hex) { - const h = hex.replace('#', '').slice(0, 6); - const r = parseInt(h.slice(0, 2), 16) / 255; - const g = parseInt(h.slice(2, 4), 16) / 255; - const b = parseInt(h.slice(4, 6), 16) / 255; - const lin = (c) => (c <= 0.04045 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)); - return [lin(r), lin(g), lin(b)]; -} +const WRITE_README = process.argv.includes('--write-readme'); -function relativeLuminance(hex) { - const [r, g, b] = hexToLinearRgb(hex); - return 0.2126 * r + 0.7152 * g + 0.0722 * b; -} +// ─── Policy & thresholds ───────────────────────────────────────────── +// Severity per check. Flip a `warn` to `fail` once the palette meets it. +const POLICY = { + ladderOrder: 'fail', + tightGap: 'warn', + apca: 'warn', + cvd: 'warn', + ansi: 'warn', + wavelengthBand: 'fail', + wavelengthScan: 'warn', + parity: 'fail', + readme: 'fail', +}; -function cielab_L(hex) { - const Y = relativeLuminance(hex); - const f = (t) => (t > Math.pow(6 / 29, 3) ? Math.cbrt(t) : (1 / 3) * Math.pow(29 / 6, 2) * t + 4 / 29); - return 116 * f(Y) - 16; -} +const TIGHT_GAP_L = 3.0; +const TIGHT_GAP_DE = 4.0; +const CVD_MIN_DE = 5.0; +const ANSI_MIN_DE = 10.0; +const SCAN_MIN_NM = 575; +const SCAN_MIN_CHROMA = 3.0; -function contrastRatio(fg, bg) { - const lFg = relativeLuminance(fg); - const lBg = relativeLuminance(bg); - const [lighter, darker] = lFg > lBg ? [lFg, lBg] : [lBg, lFg]; - return (lighter + 0.05) / (darker + 0.05); -} +// APCA |Lc| floors by role class (standard variant). Lc is compared after +// rounding to the nearest integer, matching how APCA levels are quoted. +const APCA_FLOORS = { body: 60, syntax: 40, special: 37, punctuation: 28, comments: 22 }; +const APCA_CLASS = { + Operators: 'body', Variables: 'body', + Numbers: 'syntax', Keywords: 'syntax', Types: 'syntax', Functions: 'syntax', Strings: 'syntax', + Special: 'special', Punctuation: 'punctuation', Comments: 'comments', +}; -function rgbChannels(hex) { - const h = hex.replace('#', '').slice(0, 6); - return { - r: parseInt(h.slice(0, 2), 16), - g: parseInt(h.slice(2, 4), 16), - b: parseInt(h.slice(4, 6), 16), - }; +// ─── Reporting ─────────────────────────────────────────────────────── +let failures = 0; +let warnings = 0; +const tally = {}; +function finding(check, message) { + const sev = POLICY[check]; + if (sev === 'fail') { + failures++; + console.log(` ✗ ${message}`); + } else { + warnings++; + console.log(` ⚠ ${message}`); + } + tally[check] = (tally[check] ?? 0) + 1; } +const f1 = (n) => n.toFixed(1); +const lc = (hex, bg) => Math.round(Math.abs(apcaLc(hex, bg))); // ─── Discover variants ─────────────────────────────────────────────── -const variantFiles = fs - .readdirSync(VARIANTS_DIR) - .filter((f) => f.endsWith('.yaml')) - .sort(); - +const variantFiles = fs.readdirSync(VARIANTS_DIR).filter((f) => f.endsWith('.yaml')).sort(); if (variantFiles.length === 0) { console.error(`No variants found under ${VARIANTS_DIR}`); process.exit(1); @@ -98,138 +118,164 @@ const variants = variantFiles.map((f) => { throw new Error(`${f} references ${meta.filename} but it does not exist — run npm run build:themes first`); } const theme = JSON.parse(fs.readFileSync(jsonPath, 'utf8')); + const bg = theme.colors?.['editor.background']; + if (!isHexColor(bg)) { + throw new Error(`${f}: colors["editor.background"] must be a 6- or 8-digit hex (got ${JSON.stringify(bg)})`); + } return { sourceFile: f, display: meta.display ?? theme.name, filename: meta.filename, band: meta.verify?.wavelengthBand ?? 'warm', isStandard: meta.verify?.isStandard === true, + apcaFloors: { ...APCA_FLOORS, ...(meta.verify?.apcaFloors ?? {}) }, + bg, theme, }; }); -// "Standard" variant is the one used for the L* ladder + README WCAG checks. -// Marked via verify.isStandard: true. Fall back to source filename "alone.yaml". -// Multiple isStandard declarations are ambiguous (which variant pegs the -// ladder?) — reject rather than silently pick the first. +// "Standard" variant pegs the L* ladder and the README tables. Marked via +// verify.isStandard: true; falls back to alone.yaml. Multiple declarations +// are ambiguous — reject rather than silently pick the first. const standardCandidates = variants.filter((v) => v.isStandard); -let standard; if (standardCandidates.length > 1) { console.error( `Ambiguous standard: ${standardCandidates.length} variants declare verify.isStandard: true — ` + - standardCandidates.map((v) => v.sourceFile).join(', ') + - '. Exactly one variant should be marked standard.' + standardCandidates.map((v) => v.sourceFile).join(', ') + '. Exactly one variant should be marked standard.' ); process.exit(1); } -if (standardCandidates.length === 1) { - standard = standardCandidates[0]; -} else { - standard = variants.find((v) => v.sourceFile === 'alone.yaml'); -} +const standard = standardCandidates[0] ?? variants.find((v) => v.sourceFile === 'alone.yaml'); if (!standard) { console.error('No "standard" variant — set verify.isStandard: true on one variant yaml.'); process.exit(1); } +// Report the standard first, then the rest in file order. +variants.sort((a, b) => (a === standard ? -1 : b === standard ? 1 : 0)); + console.log(`Found ${variants.length} variant(s): ${variants.map((v) => v.display).join(', ')}`); console.log(`Standard = ${standard.display} (${standard.sourceFile})\n`); -const BG = standard.theme.colors?.['editor.background']; -// {6,8} accepts 7 too, but a 7-digit hex string isn't a valid color (RGB or RGBA -// only). Use explicit alternation to reject 7-char strings. -if (typeof BG !== 'string' || !/^#(?:[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(BG)) { - console.error( - `Standard variant (${standard.sourceFile}) is missing or has a malformed ` + - `colors["editor.background"] — got ${JSON.stringify(BG)}. ` + - `All contrast math is computed against this value, so it must be a 6- or 8-digit hex string.` - ); - process.exit(1); -} - -// ─── 1. L* ladder (standard only) ──────────────────────────────────── -const sem = standard.theme.semanticTokenColors; -const fg = (v) => (typeof v === 'string' ? v : v.foreground); -function tokenForegroundByName(theme, name) { - const entry = theme.tokenColors.find((t) => t.name === name); - const hex = entry?.settings?.foreground; - if (!hex) throw new Error(`tokenColors entry "${name}" not found or missing foreground`); - return hex; -} - -const ROLES = { - Operators: fg(sem['operator']), - Variables: fg(sem['variable']), - Numbers: fg(sem['number']), - Keywords: fg(sem['keyword']), - Types: fg(sem['type']), - Functions: fg(sem['function']), - Strings: fg(sem['string']), - Special: fg(sem['decorator']), - Punctuation: tokenForegroundByName(standard.theme, 'Punctuation'), - Comments: fg(sem['comment']), -}; - -const ladder = Object.entries(ROLES).map(([name, hex]) => ({ - name, hex, L: cielab_L(hex), contrast: contrastRatio(hex, BG), -})); +// ─── 1. L* ladder (standard) ───────────────────────────────────────── +const BG = standard.bg; +const ladder = LADDER_ROLES.map((r) => { + const { name, hex, style } = resolveRole(standard.theme, r); + return { name, hex, style, L: cielabL(hex), wcag: contrastRatio(hex, BG), lc: lc(hex, BG) }; +}); -console.log(`L* ladder for ${standard.display} (against bg ${BG}):\n`); -console.log(' Role Hex L* Δ Contrast'); -console.log(' ──────────────────────────────────────────────'); +console.log(`1. L* ladder — ${standard.display} against ${BG}\n`); +console.log(' Role Hex L* Δ WCAG APCA'); +console.log(' ───────────────────────────────────────────────────'); for (let i = 0; i < ladder.length; i++) { - const { name, hex, L, contrast } = ladder[i]; - const gap = i === 0 ? '' : (ladder[i - 1].L - L).toFixed(1).padStart(4); - console.log(` ${name.padEnd(13)} ${hex} ${L.toFixed(1).padStart(5)} ${gap} ${contrast.toFixed(2)}:1`); + const { name, hex, L, wcag, lc: apca } = ladder[i]; + const gap = i === 0 ? '' : f1(ladder[i - 1].L - L).padStart(4); + console.log(` ${name.padEnd(13)} ${hex} ${f1(L).padStart(5)} ${gap.padStart(4)} ${f1(wcag).padStart(5)} Lc ${String(apca).padStart(2)}`); } - -let failures = 0; -let warnings = 0; - +console.log(''); for (let i = 1; i < ladder.length; i++) { const prev = ladder[i - 1]; const cur = ladder[i]; - const pairKey = `${prev.name}→${cur.name}`; const gap = prev.L - cur.L; + const dE = deltaE2000(prev.hex, cur.hex); if (gap < 0) { - console.log(`\n ✗ ladder out of order: ${cur.name} (L*${cur.L.toFixed(1)}) > ${prev.name} (L*${prev.L.toFixed(1)})`); - failures++; - } else if (gap < TIGHT_GAP_THRESHOLD) { - console.log(`\n ⚠ tight gap (<${TIGHT_GAP_THRESHOLD}): ${pairKey} = ${gap.toFixed(1)} L* — rely on font style + hue`); - warnings++; + finding('ladderOrder', `ladder out of order: ${cur.name} (L*${f1(cur.L)}) > ${prev.name} (L*${f1(prev.L)})`); + } else if (gap < TIGHT_GAP_L || dE < TIGHT_GAP_DE) { + const why = [gap < TIGHT_GAP_L ? `${f1(gap)} L* < ${TIGHT_GAP_L}` : null, dE < TIGHT_GAP_DE ? `ΔE00 ${f1(dE)} < ${TIGHT_GAP_DE}` : null] + .filter(Boolean).join(', '); + finding('tightGap', `tight gap ${prev.name}→${cur.name}: ${why} — relies on font style + hue`); } } +if (!tally.ladderOrder && !tally.tightGap) console.log(' ✓ monotonic with comfortable gaps'); -// ─── 2. Headline WCAG contrast claims from README ──────────────────── -const README_CONTRAST = { - Variables: 10.1, - Keywords: 8.1, - Functions: 6.6, - Strings: 5.9, - Comments: 2.7, -}; +// ─── 2. APCA floors (per variant) ──────────────────────────────────── +console.log('\n2. APCA floors (|Lc| vs editor.background, rounded)\n'); +for (const v of variants) { + const roles = roleMap(v.theme); + const misses = []; + const cells = []; + for (const r of LADDER_ROLES) { + const cls = APCA_CLASS[r.name]; + const floor = v.apcaFloors[cls]; + const value = lc(roles[r.name].hex, v.bg); + cells.push(`${r.name} ${value}`); + if (value < floor) misses.push(`${r.name} Lc ${value} < ${floor} (${cls})`); + } + console.log(` ${v.display.padEnd(16)} ${cells.join(' · ')}`); + for (const m of misses) finding('apca', `${v.display}: ${m}`); +} +if (!tally.apca) console.log(' ✓ every role clears its floor in every variant'); -console.log('\nWCAG contrast vs README claims:\n'); -console.log(' Role Computed Claimed Δ'); -console.log(' ───────────────────────────────────'); -for (const [name, claimed] of Object.entries(README_CONTRAST)) { - const row = ladder.find((r) => r.name === name); - const delta = row.contrast - claimed; - const ok = Math.abs(delta) <= WCAG_TOLERANCE ? '✓' : '✗'; - console.log(` ${name.padEnd(10)} ${row.contrast.toFixed(2).padStart(6)} ${claimed.toFixed(1).padStart(4)} ${delta >= 0 ? '+' : ''}${delta.toFixed(2)} ${ok}`); - if (Math.abs(delta) > WCAG_TOLERANCE) failures++; +// ─── 3. CVD confusability (per variant) ────────────────────────────── +function cvdRows(roles) { + return CVD_PAIRS.map(([a, b]) => { + const A = roles[a]; + const B = roles[b]; + const sim = Object.fromEntries( + ['protan', 'deutan', 'tritan'].map((t) => [t, deltaE2000(simulateCvd(A.hex, t), simulateCvd(B.hex, t))]) + ); + const styled = stylesDiffer(A.style, B.style); + const worst = Math.min(sim.protan, sim.deutan); + return { a, b, normal: deltaE2000(A.hex, B.hex), sim, styled, worst, ok: styled || worst >= CVD_MIN_DE }; + }); } +const cvdLine = (r) => + ` ${`${r.a} vs ${r.b}`.padEnd(26)} ${f1(r.normal).padStart(5)} ${f1(r.sim.protan).padStart(5)} ` + + `${f1(r.sim.deutan).padStart(5)} ${f1(r.sim.tritan).padStart(5)} ${r.styled ? 'differs' : 'same'}${r.ok ? '' : ' ✗'}`; -// ─── 3. Wavelength check per variant ───────────────────────────────── +console.log(`\n3. Colour-vision deficiency — ΔE2000 after simulation (min ${CVD_MIN_DE} unless font style differs)\n`); +console.log(' Pair normal protan deutan tritan style'); +console.log(' ─────────────────────────────────────────────────────────────────'); +for (const v of variants) { + const rows = cvdRows(roleMap(v.theme)); + const bad = rows.filter((r) => !r.ok); + if (v === standard) { + for (const r of rows) console.log(cvdLine(r)); + } else { + console.log(` — ${v.display}: ${bad.length ? `${bad.length} pair(s) collapse` : '✓ all pairs separable'}`); + for (const r of bad) console.log(cvdLine(r)); + } + for (const r of bad) { + finding('cvd', `${v.display}: ${r.a} vs ${r.b} collapse under red-green simulation (ΔE00 ${f1(r.worst)}) with identical font style`); + } +} + +// ─── 4. ANSI palette (per variant) ─────────────────────────────────── +console.log(`\n4. Terminal ANSI — pairwise ΔE2000 among the normal 8 (min ${ANSI_MIN_DE})\n`); +for (const v of variants) { + const ansi = ansiColors(v.theme); + const normal = ansi.slice(0, 8); + if (v === standard) { + console.log(' Slot Hex L* λd'); + console.log(' ─────────────────────────────────'); + for (const { slot, hex } of ansi) { + const { lambda } = dominantWavelength(hex); + const [, C] = hexToLch(hex); + console.log(` ${slot.padEnd(14)} ${hex} ${f1(cielabL(hex)).padStart(4)} ${C < SCAN_MIN_CHROMA ? 'neutral' : `${lambda} nm`}`); + } + console.log(''); + } + let worst = Infinity; + for (let i = 0; i < normal.length; i++) { + for (let j = i + 1; j < normal.length; j++) { + const d = deltaE2000(normal[i].hex, normal[j].hex); + worst = Math.min(worst, d); + if (d < ANSI_MIN_DE) finding('ansi', `${v.display}: ansi ${normal[i].slot} vs ${normal[j].slot} ΔE00 ${f1(d)} < ${ANSI_MIN_DE}`); + } + } + console.log(` ${v.display.padEnd(16)} closest normal pair ΔE00 ${f1(worst)}`); +} + +// ─── 5. Wavelength (per variant) ───────────────────────────────────── function bandCheck(band, hex) { - const { r, g, b } = rgbChannels(hex); + const [r, g, b] = hexToRgb255(hex); switch (band) { case 'warm': // No syntax hex in the blue/cyan band (B > R and B > G). return !(b > r && b > g); case 'red-amber': // Long-wave dominant: R ≥ G ≥ B, with R-B headroom ≥ 0x40. - return r >= g && g >= b && (r - b) >= 0x40; + return r >= g && g >= b && r - b >= 0x40; case 'red-only': // Strict mono-red: R dominates both G and B by wide margins. return r >= g + 0x30 && r >= b + 0x60; @@ -238,78 +284,151 @@ function bandCheck(band, hex) { } } -function rolesForVariant(theme) { - // Same headline-role extraction as the standard, but per-variant. - const s = theme.semanticTokenColors; - return { - Operators: fg(s['operator']), - Variables: fg(s['variable']), - Numbers: fg(s['number']), - Keywords: fg(s['keyword']), - Types: fg(s['type']), - Functions: fg(s['function']), - Strings: fg(s['string']), - Special: fg(s['decorator']), - Punctuation: tokenForegroundByName(theme, 'Punctuation'), - Comments: fg(s['comment']), - }; -} - -console.log('\nWavelength check (per variant):\n'); +console.log(`\n5. Wavelength — role band check + whole-theme scan (λd ≥ ${SCAN_MIN_NM} nm for chroma ≥ ${SCAN_MIN_CHROMA})\n`); for (const v of variants) { - const roles = rolesForVariant(v.theme); - const offenders = Object.entries(roles).filter(([_, hex]) => !bandCheck(v.band, hex)); - if (offenders.length === 0) { - console.log(` ✓ ${v.display.padEnd(20)} band=${v.band.padEnd(10)} all syntax roles in band`); - } else { - console.log(` ✗ ${v.display} band=${v.band}: ${offenders.length} role(s) outside band:`); - for (const [name, hex] of offenders) { - console.log(` ${name} (${hex})`); - failures++; + const roles = roleMap(v.theme); + const offenders = LADDER_ROLES.map((r) => roles[r.name]).filter(({ hex }) => !bandCheck(v.band, hex)); + for (const { name, hex } of offenders) finding('wavelengthBand', `${v.display}: ${name} (${hex}) outside band "${v.band}"`); + + const hexes = allHexes(v.theme); + const byHex = new Map(); + for (const { path: p, hex } of hexes) { + if (!byHex.has(hex)) byHex.set(hex, []); + byHex.get(hex).push(p); + } + let scanned = 0; + let scanOffenders = 0; + for (const [hex, paths] of byHex) { + const [, C] = hexToLch(hex); + if (C < SCAN_MIN_CHROMA) continue; + scanned++; + const { lambda } = dominantWavelength(hex); + if (Number.isNaN(lambda) || lambda < SCAN_MIN_NM) { + scanOffenders++; + finding('wavelengthScan', `${v.display}: ${hex} λd=${Number.isNaN(lambda) ? 'non-spectral' : lambda + ' nm'} (C* ${f1(C)}) at ${paths.slice(0, 3).join(', ')}${paths.length > 3 ? ` (+${paths.length - 3})` : ''}`); } } + const mark = offenders.length || scanOffenders ? ' ' : '✓'; + console.log(` ${mark} ${v.display.padEnd(16)} band=${v.band.padEnd(9)} roles ${offenders.length ? `${offenders.length} outside` : 'in band'}; scanned ${byHex.size} unique hexes (${scanned} chromatic), ${scanOffenders} below ${SCAN_MIN_NM} nm`); } -// ─── 4. Variant key parity ─────────────────────────────────────────── -console.log('\nVariant key parity:\n'); -function keySet(obj) { - return new Set(Object.keys(obj)); -} +// ─── 6. Variant key parity ─────────────────────────────────────────── +console.log('\n6. Variant key parity\n'); +const keySet = (obj) => new Set(Object.keys(obj)); function diffSets(a, b, label) { const onlyA = [...a].filter((k) => !b.has(k)); const onlyB = [...b].filter((k) => !a.has(k)); if (onlyA.length || onlyB.length) { - console.log(` ✗ ${label}:`); - if (onlyA.length) console.log(` only in left: ${onlyA.slice(0, 5).join(', ')}${onlyA.length > 5 ? '…' : ''}`); - if (onlyB.length) console.log(` only in right: ${onlyB.slice(0, 5).join(', ')}${onlyB.length > 5 ? '…' : ''}`); + const detail = [ + onlyA.length ? `only in left: ${onlyA.slice(0, 5).join(', ')}${onlyA.length > 5 ? '…' : ''}` : null, + onlyB.length ? `only in right: ${onlyB.slice(0, 5).join(', ')}${onlyB.length > 5 ? '…' : ''}` : null, + ].filter(Boolean).join('; '); + finding('parity', `${label}: ${detail}`); return false; } return true; } - -// All variants must match the standard's key set, both for colors and -// semanticTokenColors. N-way: standard vs each non-standard. const stdColors = keySet(standard.theme.colors); const stdSem = keySet(standard.theme.semanticTokenColors); - let parityOk = true; for (const v of variants) { if (v === standard) continue; - const vColors = keySet(v.theme.colors); - const vSem = keySet(v.theme.semanticTokenColors); - parityOk = diffSets(stdColors, vColors, `colors: ${standard.display} vs ${v.display}`) && parityOk; - parityOk = diffSets(stdSem, vSem, `semanticTokenColors: ${standard.display} vs ${v.display}`) && parityOk; + parityOk = diffSets(stdColors, keySet(v.theme.colors), `colors: ${standard.display} vs ${v.display}`) && parityOk; + parityOk = diffSets(stdSem, keySet(v.theme.semanticTokenColors), `semanticTokenColors: ${standard.display} vs ${v.display}`) && parityOk; } if (parityOk) console.log(` ✓ all ${variants.length} variants share identical key sets`); -else failures++; + +// ─── 7. README tables ──────────────────────────────────────────────── +function wcagLevel(ratio) { + if (ratio >= 7) return 'AAA'; + if (ratio >= 4.5) return 'AA'; + if (ratio >= 3) return 'AA (large)'; + return '—'; +} + +function renderReadmeSections() { + const t = standard.theme; + const sections = {}; + + // Ladder — plain-text bar chart, one line per tier. + const styleNote = (s) => (s.bold ? ' (bold)' : s.italic ? ' (italic)' : ''); + sections.ladder = '```text\n' + ladder + .map(({ name, style, L }) => `L* ${String(Math.round(L)).padStart(2)} ${'█'.repeat(Math.round(L / 3)).padEnd(28)} ${name}${styleNote(style)}`) + .join('\n') + '\n```'; + + // Syntax colours — Element | Hex | Style. + sections['syntax-colors'] = mdTable( + ['Element', 'Hex', 'Style'], + SYNTAX_TABLE_ROLES.map((r) => { + const { name, hex, style } = resolveRole(t, r); + return [name, `\`${hex}\``, styleLabel(style)]; + }) + ); + + // Bracket pairs — ordered list, depth → dimness. + const BRACKET_NAMES = ['Bright Gold', 'Gold', 'Amber-Brown', 'Umber', 'Dark Umber', 'Deep Brown']; + sections['bracket-colors'] = bracketColors(t) + .map(({ depth, hex }) => `${depth}. ${BRACKET_NAMES[depth - 1]} \`${hex}\` (L\\* ~${Math.round(cielabL(hex))})`) + .join('\n'); + + // Contrast — WCAG and APCA side by side for the eight quoted roles. + const CONTRAST_ROLES = ['Operators', 'Variables', 'Numbers', 'Keywords', 'Types', 'Functions', 'Strings', 'Comments']; + sections.contrast = mdTable( + ['Element', 'Color', 'WCAG', 'WCAG level', 'APCA Lc'], + CONTRAST_ROLES.map((name) => { + const row = ladder.find((r) => r.name === name); + return [name, `\`${row.hex}\``, `${f1(row.wcag)}:1`, wcagLevel(row.wcag), String(row.lc)]; + }) + ); + + // ANSI — the sixteen terminal slots with L*. + sections.ansi = mdTable( + ['Slot', 'Hex', 'L\\*', 'Bright slot', 'Hex', 'L\\*'], + ansiColors(t).slice(0, 8).map(({ slot, hex }, i) => { + const bright = ansiColors(t)[i + 8]; + return [slot, `\`${hex}\``, String(Math.round(cielabL(hex))), bright.slot, `\`${bright.hex}\``, String(Math.round(cielabL(bright.hex)))]; + }) + ); + return sections; +} + +console.log(`\n7. README tables (${WRITE_README ? 'writing' : 'checking'} verifier-rendered blocks)\n`); +{ + const sections = renderReadmeSections(); + let readme = fs.readFileSync(README, 'utf8'); + const original = readme; + const drifted = []; + for (const [name, body] of Object.entries(sections)) { + if (!findBlock(readme, name)) { + finding('readme', `README.md is missing the "${name}" marker block — add … where the table belongs`); + continue; + } + const next = replaceBlock(readme, name, body); + if (next !== readme) drifted.push(name); + readme = next; + } + if (WRITE_README) { + if (readme !== original) { + fs.writeFileSync(README, readme); + console.log(` ✓ rewrote ${drifted.length} block(s): ${drifted.join(', ')}`); + } else { + console.log(' ✓ README already current'); + } + } else if (drifted.length) { + finding('readme', `README.md tables out of date (${drifted.join(', ')}) — run: node scripts/verify-palette.mjs --write-readme`); + } else if (!tally.readme) { + console.log(' ✓ README tables match the palette'); + } +} // ─── Result ────────────────────────────────────────────────────────── console.log(''); +const summary = Object.entries(tally).map(([k, n]) => `${k} ×${n}`).join(', '); if (failures > 0) { - console.log(`✗ ${failures} check(s) failed (${warnings} warning${warnings === 1 ? '' : 's'})`); + console.log(`✗ ${failures} failure(s), ${warnings} warning(s)${summary ? ` — ${summary}` : ''}`); process.exit(1); } else if (warnings > 0) { - console.log(`✓ all checks passed (${warnings} tight-gap warning${warnings === 1 ? '' : 's'})`); + console.log(`✓ all hard checks passed with ${warnings} warning(s) — ${summary}`); } else { console.log('✓ all checks passed'); } diff --git a/tests/build.test.mjs b/tests/build.test.mjs new file mode 100644 index 0000000..0639bd7 --- /dev/null +++ b/tests/build.test.mjs @@ -0,0 +1,72 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import path from 'node:path'; +import { resolveValue, walk, validateVariant, buildTheme, describeType, serializeTheme } from '../scripts/lib/build.mjs'; + +const OUT = path.resolve('/tmp/alone-themes-out'); + +test('resolveValue: bare token returns the token value verbatim (any JSON type)', () => { + const used = new Set(); + assert.equal(resolveValue('${accent}', { accent: '#D4A048' }, 'x', used), '#D4A048'); + assert.equal(resolveValue('${flag}', { flag: true }, 'x', used), true); + assert.equal(resolveValue('${n}', { n: 3 }, 'x', used), 3); + assert.deepEqual([...used].sort(), ['accent', 'flag', 'n']); +}); + +test('resolveValue: embedded tokens interpolate as strings', () => { + const used = new Set(); + assert.equal(resolveValue('${accent}80', { accent: '#D4A048' }, 'x', used), '#D4A04880'); + assert.equal(resolveValue('${a}-${b}', { a: 1, b: 'z' }, 'x', used), '1-z'); +}); + +test('resolveValue: literals and non-strings pass through', () => { + const used = new Set(); + assert.equal(resolveValue('#0C0A09', {}, 'x', used), '#0C0A09'); + assert.equal(resolveValue(42, {}, 'x', used), 42); + assert.equal(used.size, 0); +}); + +test('resolveValue: unknown tokens throw with the JSON path', () => { + assert.throws(() => resolveValue('${nope}', {}, 'colors.editor.background', new Set()), /unknown token "nope" at colors\.editor\.background/); + assert.throws(() => resolveValue('${nope}80', {}, 'colors.x', new Set()), /unknown token "nope" in interpolation at colors\.x/); +}); + +test('walk: resolves nested objects and arrays, preserving key order', () => { + const base = { name: '${name}', colors: { 'editor.background': '${bg}', 'x': '#111111' }, tokenColors: [{ settings: { foreground: '${fg}' } }] }; + const out = walk(base, { name: 'T', bg: '#000000', fg: '#FFFFFF' }, new Set()); + assert.deepEqual(out, { name: 'T', colors: { 'editor.background': '#000000', x: '#111111' }, tokenColors: [{ settings: { foreground: '#FFFFFF' } }] }); + assert.deepEqual(Object.keys(out), ['name', 'colors', 'tokenColors']); +}); + +test('validateVariant: rejects non-object roots with a readable type name', () => { + for (const [root, label] of [[null, 'null'], [[], 'array'], ['42', 'string'], [undefined, 'undefined']]) { + assert.throws(() => validateVariant(root, 'v.yaml', OUT), new RegExp(`variant root must be a mapping/object \\(got ${label}\\)`)); + } + assert.equal(describeType(null), 'null'); + assert.equal(describeType([]), 'array'); +}); + +test('validateVariant: filename and tokens shape', () => { + assert.throws(() => validateVariant({ tokens: {} }, 'v.yaml', OUT), /"filename" must be a non-empty string \(got undefined\)/); + assert.throws(() => validateVariant({ filename: '', tokens: {} }, 'v.yaml', OUT), /"filename" must be a non-empty string/); + assert.throws(() => validateVariant({ filename: 'a.json', tokens: [] }, 'v.yaml', OUT), /"tokens" must be a mapping .* \(got array\)/); + assert.throws(() => validateVariant({ filename: 'a.json' }, 'v.yaml', OUT), /"tokens" must be a mapping/); +}); + +test('validateVariant: filenames must be plain names inside the output dir', () => { + assert.throws(() => validateVariant({ filename: 'sub/a.json', tokens: {} }, 'v.yaml', OUT), /contains a path separator/); + assert.throws(() => validateVariant({ filename: '/etc/passwd', tokens: {} }, 'v.yaml', OUT), /contains a path separator/); + assert.throws(() => validateVariant({ filename: '..', tokens: {} }, 'v.yaml', OUT), /resolves outside themes\//); + assert.throws(() => validateVariant({ filename: '.', tokens: {} }, 'v.yaml', OUT), /resolves outside themes\//); + assert.equal(validateVariant({ filename: 'a.json', tokens: {} }, 'v.yaml', OUT), path.join(OUT, 'a.json')); +}); + +test('buildTheme: unused declared tokens are a hard error', () => { + const base = { colors: { bg: '${bg}' } }; + assert.throws(() => buildTheme(base, { tokens: { bg: '#000', stale: '#fff' } }, 'v.yaml'), /1 declared token\(s\) are not referenced by base\.yaml .*stale/); + assert.deepEqual(buildTheme(base, { tokens: { bg: '#000' } }, 'v.yaml'), { colors: { bg: '#000' } }); +}); + +test('serializeTheme: 2-space indent with trailing newline', () => { + assert.equal(serializeTheme({ a: 1 }), '{\n "a": 1\n}\n'); +}); diff --git a/tests/color.test.mjs b/tests/color.test.mjs new file mode 100644 index 0000000..c72fd17 --- /dev/null +++ b/tests/color.test.mjs @@ -0,0 +1,73 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { + contrastRatio, cielabL, apcaLc, deltaE2000, simulateCvd, dominantWavelength, + hexToLab, hexToLch, isHexColor, normalizeHex, linearRgbToHex, hexToLinearRgb, +} from '../scripts/lib/color.mjs'; + +const near = (actual, expected, tol, msg) => + assert.ok(Math.abs(actual - expected) <= tol, `${msg ?? ''} expected ${expected}±${tol}, got ${actual}`); + +test('WCAG contrast: white on black is 21:1, identical colours 1:1, symmetric', () => { + near(contrastRatio('#FFFFFF', '#000000'), 21, 1e-9); + near(contrastRatio('#000000', '#FFFFFF'), 21, 1e-9); + near(contrastRatio('#808080', '#808080'), 1, 1e-9); +}); + +test('CIE L*: white = 100, black = 0, mid-grey ≈ 53.6, ignores alpha suffix', () => { + near(cielabL("#FFFFFF"), 100, 1e-4); + near(cielabL("#000000"), 0, 1e-4); + near(cielabL('#808080'), 53.59, 0.05); + near(cielabL('#80808080'), cielabL('#808080'), 1e-12); +}); + +test('Lab: neutrals have ~zero a*/b*; sRGB red has positive a*', () => { + const [, a, b] = hexToLab('#808080'); + near(a, 0, 0.05); + near(b, 0, 0.05); + assert.ok(hexToLab('#FF0000')[1] > 60); + const [, C] = hexToLch('#808080'); + near(C, 0, 0.05); +}); + +test('APCA Lc: black-on-white ≈ +106, white-on-black ≈ −108, same-colour ≈ 0', () => { + near(apcaLc('#000000', '#FFFFFF'), 106.04, 0.5); + near(apcaLc('#FFFFFF', '#000000'), -107.88, 0.5); + assert.equal(apcaLc('#777777', '#777777'), 0); +}); + +test('ΔE2000: identity is 0, symmetric, red-vs-green ≈ 86.6', () => { + assert.equal(deltaE2000('#C08868', '#C08868'), 0); + near(deltaE2000('#C08868', '#9A8B60'), deltaE2000('#9A8B60', '#C08868'), 1e-9); + near(deltaE2000('#FF0000', '#00FF00'), 86.6, 0.5); + // small step in lightness only ≈ ΔL*/S_L + assert.ok(deltaE2000('#808080', '#828282') < 1.5); +}); + +test('CVD simulation: neutrals survive unchanged; red and green converge for protan/deutan', () => { + assert.equal(simulateCvd('#808080', 'protan'), '#808080'); + assert.equal(simulateCvd('#808080', 'deutan'), '#808080'); + const before = deltaE2000('#B85450', '#9A8B60'); + const after = deltaE2000(simulateCvd('#B85450', 'deutan'), simulateCvd('#9A8B60', 'deutan')); + assert.ok(after < before, `expected red/green to converge (${before} → ${after})`); + assert.throws(() => simulateCvd('#808080', 'nope'), /unknown CVD type/); +}); + +test('dominant wavelength: sRGB primaries land near their textbook λd; neutrals and purples are non-spectral', () => { + near(dominantWavelength('#FF0000').lambda, 611, 4, 'red'); + near(dominantWavelength('#00FF00').lambda, 549, 4, 'green'); + near(dominantWavelength('#0000FF').lambda, 464, 4, 'blue'); + assert.ok(Number.isNaN(dominantWavelength('#808080').lambda)); + assert.ok(Number.isNaN(dominantWavelength('#FF00FF').lambda)); + // Alone's palette lives in the amber band + const { lambda, purity } = dominantWavelength('#D4A048'); + assert.ok(lambda >= 575 && lambda <= 585, `amber λd ${lambda}`); + assert.ok(purity > 0 && purity < 1); +}); + +test('linear round-trip and hex helpers', () => { + assert.equal(linearRgbToHex(hexToLinearRgb('#C08868')), '#C08868'); + assert.ok(isHexColor('#0C0A09') && isHexColor('#0C0A0980')); + assert.ok(!isHexColor('#0C0A0') && !isHexColor('0C0A09') && !isHexColor('#0C0A098')); + assert.equal(normalizeHex('#c08868ff'), '#C08868'); +}); diff --git a/tests/pipeline.test.mjs b/tests/pipeline.test.mjs new file mode 100644 index 0000000..2b9263a --- /dev/null +++ b/tests/pipeline.test.mjs @@ -0,0 +1,69 @@ +/** + * End-to-end checks on the real _src tree: the build is deterministic, the + * committed themes/*.json are what the build produces, and (unless the + * palette has intentionally moved — PALETTE_CHANGED=1) each variant is still + * value-identical to the immutable v1.2.0 snapshot apart from `$schema`. + */ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import fs from 'node:fs'; +import path from 'node:path'; +import url from 'node:url'; +import { execFileSync } from 'node:child_process'; +import YAML from 'yaml'; +import { validateVariant, buildTheme, serializeTheme } from '../scripts/lib/build.mjs'; + +const ROOT = path.resolve(path.dirname(url.fileURLToPath(import.meta.url)), '..'); +const SRC = path.join(ROOT, 'themes', '_src'); +const THEMES = path.join(ROOT, 'themes'); +const SNAPSHOT = path.join(THEMES, '_snapshot'); + +const base = YAML.parse(fs.readFileSync(path.join(SRC, 'base.yaml'), 'utf8')); +const variantFiles = fs.readdirSync(path.join(SRC, 'variants')).filter((f) => f.endsWith('.yaml')).sort(); +const variants = variantFiles.map((f) => ({ f, doc: YAML.parse(fs.readFileSync(path.join(SRC, 'variants', f), 'utf8')) })); + +test('there is at least one variant and every variant validates', () => { + assert.ok(variants.length >= 3); + for (const { f, doc } of variants) validateVariant(doc, f, path.resolve(THEMES)); +}); + +test('build is deterministic: two passes are byte-identical', () => { + for (const { f, doc } of variants) { + const a = serializeTheme(buildTheme(base, doc, f)); + const b = serializeTheme(buildTheme(base, doc, f)); + assert.equal(a, b, f); + } +}); + +test('committed themes/*.json match a fresh build (run npm run build:themes if this fails)', () => { + for (const { f, doc } of variants) { + const built = serializeTheme(buildTheme(base, doc, f)); + const onDisk = fs.readFileSync(path.join(THEMES, doc.filename), 'utf8'); + assert.equal(onDisk, built, `${doc.filename} is stale relative to ${f}`); + } +}); + +test('every generated theme carries the color-theme $schema as its first key', () => { + for (const { f, doc } of variants) { + const built = buildTheme(base, doc, f); + assert.equal(Object.keys(built)[0], '$schema', f); + assert.equal(built.$schema, 'vscode://schemas/color-theme'); + } +}); + +test('v1.2.0 snapshot parity (skip with PALETTE_CHANGED=1 on intentional palette changes)', { skip: process.env.PALETTE_CHANGED === '1' }, () => { + for (const { f, doc } of variants) { + const snapPath = path.join(SNAPSHOT, doc.filename); + if (!fs.existsSync(snapPath)) continue; // variants added after v1.2.0 have no snapshot + const built = buildTheme(base, doc, f); + delete built.$schema; + const snap = JSON.parse(fs.readFileSync(snapPath, 'utf8')); + assert.deepEqual(built, snap, `${doc.filename} diverges from themes/_snapshot (intentional? set PALETTE_CHANGED=1)`); + } +}); + +test('verifier exits 0 on the committed palette and reports README tables current', () => { + const out = execFileSync(process.execPath, [path.join(ROOT, 'scripts', 'verify-palette.mjs')], { encoding: 'utf8' }); + assert.match(out, /README tables match the palette/); + assert.match(out, /✓ all (hard )?checks passed/); +}); diff --git a/tests/readme.test.mjs b/tests/readme.test.mjs new file mode 100644 index 0000000..d454ae0 --- /dev/null +++ b/tests/readme.test.mjs @@ -0,0 +1,20 @@ +import { test } from 'node:test'; +import assert from 'node:assert/strict'; +import { mdTable, renderBlock, findBlock, replaceBlock } from '../scripts/lib/readme.mjs'; + +test('mdTable pads columns and rejects ragged rows', () => { + const t = mdTable(['A', 'Long'], [['x', '1'], ['yy', '22']]); + assert.equal(t, '| A | Long |\n| -- | ---- |\n| x | 1 |\n| yy | 22 |'); + assert.throws(() => mdTable(['A'], [['x', 'y']]), /ragged/); +}); + +test('marker blocks: render, find, replace; blank-line padding is preserved', () => { + const doc = 'before\n\n\n\nold\n\n\n\nafter\n'; + assert.ok(findBlock(doc, 't')); + assert.equal(findBlock(doc, 'missing'), null); + const next = replaceBlock(doc, 't', 'new body\n'); + assert.equal(next, 'before\n\n' + renderBlock('t', 'new body') + '\n\nafter\n'); + assert.equal(renderBlock('t', 'x'), '\n\nx\n\n'); + assert.throws(() => replaceBlock(doc, 'missing', 'x'), /missing marker block "missing"/); + assert.throws(() => findBlock('\nno end', 't'), /without a matching end marker/); +}); diff --git a/themes/_src/variants/alone-soft.yaml b/themes/_src/variants/alone-soft.yaml index 019d82e..57b2c86 100644 --- a/themes/_src/variants/alone-soft.yaml +++ b/themes/_src/variants/alone-soft.yaml @@ -8,6 +8,15 @@ display: Alone Soft filename: alone-soft-color-theme.json verify: wavelengthBand: warm + # Soft is deliberately ~0.7× the standard's contrast (extreme dark + # adaptation), so its APCA floors are scaled down from the verifier's + # defaults (body 60 / syntax 40 / special 37 / punctuation 28 / comments 22). + apcaFloors: + body: 43 + syntax: 27 + special: 24 + punctuation: 18 + comments: 17 tokens: name: Alone Soft semanticTokenColors.variable: "#9D9383"