From 404bcc59d581ac2b6c93fdd4217664fdd15cf9bb Mon Sep 17 00:00:00 2001 From: ajithakdev Date: Tue, 27 Oct 2026 00:19:29 +0530 Subject: [PATCH 1/5] fix: update PDF export and server dependencies with security advisories --- CHANGELOG.md | 11 +- RESOURCES-AND-GUIDE.md | 3 +- docs/KNOWN-ISSUES.md | 1 + docs/STORE-CHECKLIST.md | 130 +++ package-lock.json | 1731 ++++++++++++++++++++++----------------- package.json | 2 +- 6 files changed, 1110 insertions(+), 768 deletions(-) create mode 100644 docs/STORE-CHECKLIST.md diff --git a/CHANGELOG.md b/CHANGELOG.md index 173c62b..c02aa20 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,14 @@ All notable changes to MeterX are documented here. The format follows [Keep a Ch ## [Unreleased] +## [2027.0.1] - 2026-10-27 + +### Fixed +- Updated the PDF export's libraries (DOMPurify 3.4.17, fflate 0.8.3) and the self-host server's Express dependencies (body-parser 2.4.0 and others) to versions without known security advisories + +### Added +- `docs/STORE-CHECKLIST.md`: what to check before each store upload, built from the Firefox, Edge, Opera and Chrome store policies + ## [2027.0.0] - 2026-10-27 ### Added @@ -173,7 +181,8 @@ First store release, store version 2026.3.19.1 ("Major Rewrite One"). - Express.js server with test file generation - Simple popup UI -[Unreleased]: https://github.com/ajithakdev/meterx/compare/v2027.0.0...HEAD +[Unreleased]: https://github.com/ajithakdev/meterx/compare/v2027.0.1...HEAD +[2027.0.1]: https://github.com/ajithakdev/meterx/compare/v2027.0.0...v2027.0.1 [2027.0.0]: https://github.com/ajithakdev/meterx/compare/v2026.2.0...v2027.0.0 [2026.2.0]: https://github.com/ajithakdev/meterx/compare/v2026.1.0...v2026.2.0 [2026.1.0]: https://github.com/ajithakdev/meterx/compare/v2026.0.0...v2026.1.0 diff --git a/RESOURCES-AND-GUIDE.md b/RESOURCES-AND-GUIDE.md index 089a700..ec1eb72 100644 --- a/RESOURCES-AND-GUIDE.md +++ b/RESOURCES-AND-GUIDE.md @@ -67,7 +67,7 @@ Every link for MeterX, what it is for, and how to run, package and release it. P | Deploy the worker | `cd services/worker && npx wrangler deploy` | | Deploy the site | `npm run deploy -w @meterx/site`: builds the extension and site and uploads to Cloudflare Pages project `meterx-site`. Not connected to git, so merges (docs or code) never deploy | | First site deploy (once) | `npx wrangler pages project create meterx-site --production-branch main`, then `npm run deploy -w @meterx/site`, then in the Cloudflare dashboard: Workers and Pages, `meterx-site`, Custom domains, add `meterx.ajithakdev.com` (Cloudflare adds the CNAME itself). Then set the GitHub repo's website to the site | -| Submit to the stores | `npm run package`, then upload `release/meterx--chromium.zip` to Edge and Opera, and `-firefox.zip` plus `-source.zip` to Firefox. Images are in `brand/png/store/`. All three stores are free; the Chrome Web Store needs a one-time paid developer fee, so it waits | +| Submit to the stores | Follow `docs/STORE-CHECKLIST.md`. `npm run package`, then upload `release/meterx--chromium.zip` to Edge and Opera, and `-firefox.zip` plus `-source.zip` to Firefox. Images are in `brand/png/store/`. All three stores are free; the Chrome Web Store needs a one-time paid developer fee, so it waits | | Bump the version | Edit `version` in the root `package.json` only; the build stamps it everywhere | | Release | Move CHANGELOG Unreleased under the version, commit `chore: release vX.Y.Z`, tag, push the tag, `gh release create`, then deploy the worker, deploy the site and upload the store zips. Merging to `main` deploys nothing | | Regenerate brand images | `npm run build`, then `node brand/build.mjs` (see the header of that file for the env vars) | @@ -92,4 +92,5 @@ Every link for MeterX, what it is for, and how to run, package and release it. P - docs/SITE-PLAN.md: the site's pages, hosting and brand assets - docs/KNOWN-ISSUES.md: open bugs by area - docs/SEO.md: the site's SEO setup and release checklist +- docs/STORE-CHECKLIST.md: what to check and test before every store upload - CHANGELOG.md, CONTRIBUTING.md, TRADEMARK.md, LICENSE diff --git a/docs/KNOWN-ISSUES.md b/docs/KNOWN-ISSUES.md index 26bb3c8..b6c5191 100644 --- a/docs/KNOWN-ISSUES.md +++ b/docs/KNOWN-ISSUES.md @@ -18,6 +18,7 @@ Remove a row when its fix is merged. | #67 | Low | Notify-on-drop fires on every poor result; quality rules in three copies | | #69 | Low | DNS time can come from an earlier test | | #71 | Low | `edge` is never set on results | +| #97 | Med | Results from private windows are saved to history (Firefox policy: must not store private-browsing data) | ## Extension UI diff --git a/docs/STORE-CHECKLIST.md b/docs/STORE-CHECKLIST.md new file mode 100644 index 0000000..5dca47e --- /dev/null +++ b/docs/STORE-CHECKLIST.md @@ -0,0 +1,130 @@ +# Store release checklist + +Go through this list for every release that goes to a browser store. It condenses the official policies of Firefox Add-ons (AMO), Microsoft Edge Add-ons, Opera Add-ons and the Chrome Web Store, and shows where MeterX already meets each rule. Links to the policies are at the bottom. Check them once a quarter, because the stores change their rules without much notice. + +## 1. Build and package + +- [ ] Version bumped in the root `package.json` only, and higher than the version live in every store (stores reject a version they have already seen). +- [ ] `npm run lint`, `npm run build` and `npm test` pass. +- [ ] `manifest.json` description is 132 characters or fewer. +- [ ] `npm run package` made three files in `release/`: `-chromium.zip` (Edge, Opera, Chrome), `-firefox.zip` and `-source.zip`. +- [ ] Mozilla's linter shows 0 errors on the Firefox zip: + ```sh + mkdir -p /tmp/ff && cd /tmp/ff && unzip -oq ~/projects/meterx-server/release/meterx--firefox.zip && npx web-ext@8 lint --source-dir . + ``` + Warnings that are expected and fine: `sidePanel.open` unsupported (guarded in `apps/extension/src/lib/browser.ts`), `innerHTML` in `vendor/charts.js` and the page bundles, and the `data_collection_permissions` keys being newer than `strict_min_version`. +- [ ] The source zip rebuilds into an identical Firefox zip. Mozilla reviewers rebuild it and reject any difference: + ```sh + D=/tmp/repro && rm -rf $D && mkdir -p $D/src $D/a $D/b && cd $D/src \ + && unzip -q ~/projects/meterx-server/release/meterx--source.zip \ + && npm ci && npm run package \ + && (cd $D/a && unzip -q $D/src/release/meterx--firefox.zip) \ + && (cd $D/b && unzip -q ~/projects/meterx-server/release/meterx--firefox.zip) \ + && diff -r $D/a $D/b && echo IDENTICAL + ``` +- [ ] Nothing in the zips that the extension does not use: no source maps, no test files, no `.DS_Store`. List them with `unzip -l`. +- [ ] The bundles are not minified (`minify: false` in `apps/extension/build.mjs`). Opera rejects minified first-party code, and Mozilla and Edge both reject obfuscated code. + +## 2. Test on real browsers + +The Chromium zip is the same for Chrome, Edge and Opera, so one Chromium browser covers the code. Firefox runs a converted manifest (background script instead of a service worker, its own sidebar), so it always gets its own pass. + +Load the build unpacked: `chrome://extensions` or `edge://extensions` with Developer mode on, then Load unpacked on `apps/extension/dist/`; in Firefox, `about:debugging`, This Firefox, Load Temporary Add-on, and pick the Firefox zip. + +In each browser: + +- [ ] Toolbar icon opens the popup. A test finishes in about 20 seconds with download, upload, ping, jitter, packet loss and the bufferbloat grade. +- [ ] The side panel (Firefox: sidebar) opens from the popup and shows history and the chart. +- [ ] PDF, CSV and JSON exports download and open. +- [ ] A share link opens the result on `meterx.ajithakdev.com/r/`. +- [ ] Settings: a schedule and an ISP plan save and are still there after closing and reopening. +- [ ] Custom server `http://localhost:8787` (with `npm run dev:ext` running) asks for permission, then a test runs against it. +- [ ] A scheduled test runs, and a drop in quality shows a notification. Turning notifications off in the OS does not break anything (Edge policy 1.9). +- [ ] The extension console (Firefox: Inspect on `about:debugging`; Chromium: "service worker" link) shows no red errors. +- [ ] Light and dark theme both look right, and the popup has no horizontal scroll (Opera checks this). + +## 3. Rules every store shares + +| Rule | How MeterX meets it | Check each release | +| --- | --- | --- | +| One clear purpose | Internet speed and quality test, nothing else | No unrelated feature crept in | +| Only the permissions it needs | `storage` (settings, history), `alarms` (scheduled tests), `notifications` (quality drop notice), `sidePanel` (Chromium side panel), host access to the default worker only. Other servers are optional and asked for at runtime | A new permission has a feature that uses it today, and the listing says why | +| No remote code | Everything runs from the package. CSP is `script-src 'self'`. Network calls only move test bytes and read `/ip` from the test server | No `eval`, no script loaded from the web | +| Secure transport | Default server is https. Plain http only for localhost, 127.0.0.1 and `.local` | `validateServerUrl` unchanged or still tested | +| Data and privacy | No personal data collected. Results stay in browser storage. The test server sees the IP address, as any server does, and keeps no logs. Shares put the result in the link itself | Privacy policy at `https://meterx.ajithakdev.com/privacy/` still matches what the code does | +| Listing is accurate | Description lists every feature, including scheduled tests and notifications | Screenshots show the current UI, not an old version | +| Works for the reviewer | No account needed. The worker must be up | `curl https://meterx-speedtest.meterx-ajithakdev.workers.dev/health` returns the new version | +| Name and trademarks | "MeterX by ajithakdev". No store or browser name in the extension name | Never add "for Chrome", "Opera" or "Edge" to the name | + +## 4. Firefox Add-ons (AMO) + +- [ ] Upload `-firefox.zip`. When AMO asks whether the code is bundled, say yes and upload `-source.zip`. +- [ ] Notes for reviewers: paste the text from section 8, plus "Build: Node 24, npm 11, run `npm ci && npm run package`; output is `release/meterx--firefox.zip`. No private dependencies; everything installs from npm through `package-lock.json`." +- [ ] `data_collection_permissions` says `none`. That is true only while MeterX sends nothing beyond test traffic. If a feature ever sends data, change the manifest and add an opt-in first. +- [ ] A fix after a rejection contains only that fix (AMO policy 3: no unrelated changes). +- [ ] Private windows: Firefox policy says data from private browsing must not be stored. Open issue #97 tracks skipping history in private windows; until it ships, keep the add-on's private-window setting at its default (off). +- [ ] Listing: website `https://meterx.ajithakdev.com`, support site the GitHub issues page, privacy policy URL as above, license AGPL-3.0. + +## 5. Microsoft Edge Add-ons (Partner Center) + +- [ ] Upload `-chromium.zip`. +- [ ] Search terms: at most 7, all relevant (for example: speed test, internet speed, bufferbloat, ping, latency, wifi test, bandwidth). +- [ ] Notes for certification: the text from section 8. +- [ ] Images from `brand/png/store/`: `edge-logo-300.png`, `promo-440x280.png`, `marquee-1400x560.png`, screenshots at 1280x800. Not stretched or blurry. +- [ ] The description must not mention other browsers (Edge policy 1.1.2). Write "your browser" or "Edge". +- [ ] Privacy policy URL set. Edge prefers a policy that talks about Edge rather than other browsers; ours names no browser, which is fine. +- [ ] Mature content: answer no. + +## 6. Opera Add-ons + +- [ ] Upload `-chromium.zip`. +- [ ] Summary in full, grammatical sentences that say what it does. Description says how to use it and what it looks like. +- [ ] Nothing in the name or images says "Opera" or suggests Opera made it. +- [ ] Icons at every size share one style and are not just a word on a colour. +- [ ] Screenshots are PNG, not interlaced, and the popup screenshot matches the popup's real size. +- [ ] Third-party libraries (`vendor/charts.js`, jsPDF in the bundles) are unmodified releases without known vulnerabilities. Run `npm audit --omit=dev`. +- [ ] Once the listing is public, put its link in `apps/site/src/stores.ts` and the README. + +## 7. Chrome Web Store (when the developer fee is approved) + +- [ ] Single purpose field: "Measures internet speed and connection quality (download, upload, ping, jitter, packet loss and latency under load) from the browser toolbar." +- [ ] Permission justification for each permission, using the table in section 3. +- [ ] Remote code: answer no. +- [ ] Data usage form: no data collected. It must match the privacy policy. +- [ ] Add a Limited Use statement to the privacy page before submitting: "MeterX's use of information follows the Chrome Web Store User Data Policy, including the Limited Use requirements." +- [ ] No keyword stuffing: no word repeated more than 5 times in the description, no lists of places or brands. + +## 8. Notes for reviewers (paste into every store) + +```text +MeterX is an internet speed test. No account or login is needed. + +To test: click the toolbar icon and press Start. The test takes about 20 seconds and +measures download, upload, ping, jitter, packet loss and latency under load against +our test server (https://meterx-speedtest.meterx-ajithakdev.workers.dev), which +streams bytes and stores nothing. Open the side panel from the popup to see history +and exports (PDF, CSV, JSON). + +Permissions: storage (settings and local history), alarms (optional scheduled tests), +notifications (optional notice when quality drops), sidePanel (history view). The +optional host permission is only requested if the user enters their own test server +in settings. + +No personal data is collected or sent anywhere. Source code: +https://github.com/ajithakdev/meterx (AGPL-3.0). +``` + +## 9. After approval + +- [ ] Install from each store on a clean profile and run one test. +- [ ] Update the store links and versions in the README, `RESOURCES-AND-GUIDE.md` and `apps/site/src/stores.ts` if any changed. +- [ ] Add a line to `docs/STATE.md`, and post in the launch log (`~/.claude/launch-log.md`) if the release is announced. + +## Sources + +- Firefox: [Add-on policies](https://extensionworkshop.com/documentation/publish/add-on-policies/), [Source code submission](https://extensionworkshop.com/documentation/publish/source-code-submission/) +- Edge: [Developer policies for Microsoft Edge Add-ons](https://learn.microsoft.com/en-us/legal/microsoft-edge/extensions/developer-policies) +- Opera: [Acceptance criteria](https://help.opera.com/en/extensions/acceptance-criteria/) +- Chrome: [Program policies](https://developer.chrome.com/docs/webstore/program-policies/policies) + +Last checked against these pages: 2026-10-27. diff --git a/package-lock.json b/package-lock.json index beba073..3e0b68d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "meterx", - "version": "2027.0.0", + "version": "2027.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "meterx", - "version": "2027.0.0", + "version": "2027.0.1", "license": "AGPL-3.0", "workspaces": [ "packages/*", @@ -53,24 +53,24 @@ } }, "node_modules/@cloudflare/kv-asset-handler": { - "version": "0.4.2", - "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.4.2.tgz", - "integrity": "sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==", + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.5.0.tgz", + "integrity": "sha512-jxQYkj8dSIzc0cD6cMMNdOc1UVjqSqu8BZdor5s8cGjW2I8BjODt/kWPVdY+u9zj3ms75Q5qaZgnxUad83+eAg==", "dev": true, "license": "MIT OR Apache-2.0", "engines": { - "node": ">=18.0.0" + "node": ">=22.0.0" } }, "node_modules/@cloudflare/unenv-preset": { - "version": "2.16.0", - "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.0.tgz", - "integrity": "sha512-8ovsRpwzPoEqPUzoErAYVv8l3FMZNeBVQfJTvtzP4AgLSRGZISRfuChFxHWUQd3n6cnrwkuTGxT+2cGo8EsyYg==", + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.16.2.tgz", + "integrity": "sha512-JBP1+Z7ZSNG/d4mRP+y8VC5dka3tZVMLEZRvS+rzQ4DGV1EoxRFQckcJTTkXbHSQiTj0DtNI01Zwb/V2fX0mvQ==", "dev": true, "license": "MIT OR Apache-2.0", "peerDependencies": { "unenv": "2.0.0-rc.24", - "workerd": "1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0" + "workerd": ">1.20260305.0 <2.0.0-0" }, "peerDependenciesMeta": { "workerd": { @@ -79,9 +79,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-64": { - "version": "1.20260317.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260317.1.tgz", - "integrity": "sha512-8hjh3sPMwY8M/zedq3/sXoA2Q4BedlGufn3KOOleIG+5a4ReQKLlUah140D7J6zlKmYZAFMJ4tWC7hCuI/s79g==", + "version": "1.20261023.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20261023.1.tgz", + "integrity": "sha512-lsEx0i7nR0PNG/erUFFHmQmiidEhLfDE5uQmAL9421/y4/ycuxDDOSaxu3VGGYFFPK5sduyUeMmjIlX4CwOBFg==", "cpu": [ "x64" ], @@ -96,9 +96,9 @@ } }, "node_modules/@cloudflare/workerd-darwin-arm64": { - "version": "1.20260317.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20260317.1.tgz", - "integrity": "sha512-M/MnNyvO5HMgoIdr3QHjdCj2T1ki9gt0vIUnxYxBu9ISXS/jgtMl6chUVPJ7zHYBn9MyYr8ByeN6frjYxj0MGg==", + "version": "1.20261023.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20261023.1.tgz", + "integrity": "sha512-5e79fHgabdDD8EgmjOhJ1S+xMsf6DbC7fL7IgTBizMnzOp0P2isDn7pYmUxI9AJZE9FVQ4RUilH/I36YNg+GQQ==", "cpu": [ "arm64" ], @@ -113,9 +113,9 @@ } }, "node_modules/@cloudflare/workerd-linux-64": { - "version": "1.20260317.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260317.1.tgz", - "integrity": "sha512-1ltuEjkRcS3fsVF7CxsKlWiRmzq2ZqMfqDN0qUOgbUwkpXsLVJsXmoblaLf5OP00ELlcgF0QsN0p2xPEua4Uug==", + "version": "1.20261023.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20261023.1.tgz", + "integrity": "sha512-VHQ6jKFSihjR2+x1buby8XTdBLLJztVnYiB7OLOTYO8aDXB+c1Rd2410PdgFuFYc4AgFEk6PCJLyCLL5L1KBnw==", "cpu": [ "x64" ], @@ -130,9 +130,9 @@ } }, "node_modules/@cloudflare/workerd-linux-arm64": { - "version": "1.20260317.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260317.1.tgz", - "integrity": "sha512-3QrNnPF1xlaNwkHpasvRvAMidOvQs2NhXQmALJrEfpIJ/IDL2la8g499yXp3eqhG3hVMCB07XVY149GTs42Xtw==", + "version": "1.20261023.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20261023.1.tgz", + "integrity": "sha512-ilpRHzfDN8YFRM5CLRKuFw40x/LJBS89yVdhdQmxehlVDuBqjKpJB++zPFbTgKRRFaNupgnXc8RHktXAIhgCmQ==", "cpu": [ "arm64" ], @@ -147,9 +147,9 @@ } }, "node_modules/@cloudflare/workerd-windows-64": { - "version": "1.20260317.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260317.1.tgz", - "integrity": "sha512-MfZTz+7LfuIpMGTa3RLXHX8Z/pnycZLItn94WRdHr8LPVet+C5/1Nzei399w/jr3+kzT4pDKk26JF/tlI5elpQ==", + "version": "1.20261023.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20261023.1.tgz", + "integrity": "sha512-279WLVjNsMycAwr0EZNiNEf0l2SY6NUtsN80NwZzbTHzakmiN+WSx4w38muxPm+PAU0oSPPAPXKVD+H6Hya8xw==", "cpu": [ "x64" ], @@ -163,13 +163,6 @@ "node": ">=16" } }, - "node_modules/@cloudflare/workers-types": { - "version": "4.20260317.1", - "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20260317.1.tgz", - "integrity": "sha512-+G4eVwyCpm8Au1ex8vQBCuA9wnwqetz4tPNRoB/53qvktERWBRMQnrtvC1k584yRE3emMThtuY0gWshvSJ++PQ==", - "dev": true, - "license": "MIT OR Apache-2.0" - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -184,9 +177,9 @@ } }, "node_modules/@emnapi/runtime": { - "version": "1.9.1", - "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.1.tgz", - "integrity": "sha512-VYi5+ZVLhpgK4hQ0TAjiQiZ6ol0oe4mBx7mVv7IflsiEp0OWoVsp/+f9Vc1hOhE0TtkORVrI1GvzyreqpgWtkA==", + "version": "1.11.3", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.11.3.tgz", + "integrity": "sha512-Xz4Tpyki7XyrpbUK1jR1AhdAdaXyhhY4lZ3neLodmhpuWfy2PAQN5B46sAiU4liOXGLkHypn/qU+jvfWSCYYLA==", "dev": true, "license": "MIT", "optional": true, @@ -637,9 +630,9 @@ } }, "node_modules/@eslint-community/eslint-utils": { - "version": "4.7.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.7.0.tgz", - "integrity": "sha512-dyybb3AcajC7uha6CvhdVRJqaKyn7w2YKqKyAN37NKYgZT36w+iRb0Dymmc5qEJ549c/S31cMMSFd75bteCpCw==", + "version": "4.10.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz", + "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==", "dev": true, "license": "MIT", "dependencies": { @@ -679,26 +672,66 @@ } }, "node_modules/@eslint/config-array": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.20.0.tgz", - "integrity": "sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ==", + "version": "0.21.2", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", + "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.6", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", - "minimatch": "^3.1.2" + "minimatch": "^3.1.5" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/config-array/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/config-helpers": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.2.2.tgz", - "integrity": "sha512-+GPzk8PlG0sPpzdU5ZvIRMPidzAnZDl/s9L+y13iodqvb8leL53bTannOrQ/Im7UkpsmFU5Ily5U60LWixnmLg==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-helpers/node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } @@ -746,20 +779,20 @@ } }, "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", + "version": "3.3.7", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.7.tgz", + "integrity": "sha512-F42g89Qd5oAWtp0k0nnSrjziAKza7w8SVT4mStc18LZMaRb4J1HQAHLCalEtDCxrTuksx7NU9qsmeLwpOfPqWw==", "dev": true, "license": "MIT", "dependencies": { - "ajv": "^6.12.4", + "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", + "js-yaml": "^4.3.2", + "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { @@ -769,6 +802,17 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, "node_modules/@eslint/eslintrc/node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", @@ -782,25 +826,45 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/js": { - "version": "9.26.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.26.0.tgz", - "integrity": "sha512-I9XlJawFdSMvWjDt6wksMCrgns5ggLNfFwFvnShsleWruvXM514Qxk8V246efTw+eo9JABvVz+u3q2RiAowKxQ==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz", + "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/markdown": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@eslint/markdown/-/markdown-6.4.0.tgz", - "integrity": "sha512-J07rR8uBSNFJ9iliNINrchilpkmCihPmTVotpThUeKEn5G8aBBZnkjNBy/zovhJA5LBk1vWU9UDlhqKSc/dViQ==", + "version": "6.6.0", + "resolved": "https://registry.npmjs.org/@eslint/markdown/-/markdown-6.6.0.tgz", + "integrity": "sha512-IsWPy2jU3gaQDlioDC4sT4I4kG1hX1OMWs/q2sWwJrPoMASHW/Z4SDw+6Aql6EsHejGbagYuJbFq9Zvx+Y1b1Q==", "dev": true, "license": "MIT", + "workspaces": [ + "examples/*" + ], "dependencies": { - "@eslint/core": "^0.10.0", - "@eslint/plugin-kit": "^0.2.5", + "@eslint/core": "^0.14.0", + "@eslint/plugin-kit": "^0.3.1", + "github-slugger": "^2.0.0", "mdast-util-from-markdown": "^2.0.2", "mdast-util-frontmatter": "^2.0.1", "mdast-util-gfm": "^3.0.0", @@ -812,9 +876,36 @@ } }, "node_modules/@eslint/markdown/node_modules/@eslint/core": { - "version": "0.10.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.10.0.tgz", - "integrity": "sha512-gFHJ+xBOo4G3WRlR1e/3G8A6/KZAH6zcE/hkLRCZTi/B9avAG365QhFA8uOGzTMqgTghpn7/fSnscW++dpMSAw==", + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.14.0.tgz", + "integrity": "sha512-qIbV0/JZr7iSDjqAc60IqbLdsj9GDt16xQtWD+B78d/HAlvysGdZZ6rpJHGAc2T0FQx1X6thsSPdnoiGKdNtdg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/markdown/node_modules/@eslint/plugin-kit": { + "version": "0.3.5", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.3.5.tgz", + "integrity": "sha512-Z5kJ+wU3oA7MMIqVR9tyZRtjYPr4OC004Q4Rw7pgOKUOKkJfZ3O24nz3WYfGRpMDNmcOi3TwQOmgm7B7Tpii0w==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.15.2", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/markdown/node_modules/@eslint/plugin-kit/node_modules/@eslint/core": { + "version": "0.15.2", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.15.2.tgz", + "integrity": "sha512-78Md3/Rrxh83gCxoUc0EiciuOHsIITzLy53m3d9UyiW8y9Dj2D29FeETqyKA+BRK76tnTp6RXWb3pCay8Oyomg==", "dev": true, "license": "Apache-2.0", "dependencies": { @@ -825,9 +916,9 @@ } }, "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { @@ -849,41 +940,41 @@ } }, "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", "dev": true, "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { - "version": "0.16.6", - "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.6.tgz", - "integrity": "sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw==", + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", "dev": true, "license": "Apache-2.0", "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.3.0" + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, - "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.1.tgz", - "integrity": "sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA==", + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", "dev": true, "license": "Apache-2.0", "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" + "node": ">=18.18.0" } }, "node_modules/@humanwhocodes/module-importer": { @@ -925,9 +1016,9 @@ } }, "node_modules/@img/sharp-darwin-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", - "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.35.5.tgz", + "integrity": "sha512-QRUlFQ0WxvdWyqqG/WtI3iupfD5rBzmCHXSdPsY91sAtVtTo7Q4cb6zOccZ3gqEqkr0f1As1ehLqmEpDsRf+lg==", "cpu": [ "arm64" ], @@ -938,19 +1029,19 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-arm64": "1.2.4" + "@img/sharp-libvips-darwin-arm64": "1.3.4" } }, "node_modules/@img/sharp-darwin-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", - "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.35.5.tgz", + "integrity": "sha512-+BR255RhDlpygUpOc/Jdt1nT6DQ3XG/ERo5wbcdOf5Q320dKtPCKPLR1LJs9VGXRaMa8l1uUa0tkCNOXiAxZUw==", "cpu": [ "x64" ], @@ -961,19 +1052,39 @@ "darwin" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-darwin-x64": "1.2.4" + "@img/sharp-libvips-darwin-x64": "1.3.4" + } + }, + "node_modules/@img/sharp-freebsd-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-freebsd-wasm32/-/sharp-freebsd-wasm32-0.35.5.tgz", + "integrity": "sha512-Y/z91nEZ4uIBX5X3nfTovjU9lHNKFYbL2lpHCLVNmXQK03VIZvXBBt0KxbPGp2SdGSF+2mQU4e+hQaWOt86iAw==", + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "dependencies": { + "@img/sharp-wasm32": "0.35.5" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", - "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.3.4.tgz", + "integrity": "sha512-5R89nBYiRdUlSWJxPhO+GVtaXzXSxKnRu/xqMn3KTA3L9EB9Oy/P+Nn2f2vlhPuUdy/Zusb2DarbyTpGCfEDuw==", "cpu": [ "arm64" ], @@ -988,9 +1099,9 @@ } }, "node_modules/@img/sharp-libvips-darwin-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", - "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.3.4.tgz", + "integrity": "sha512-iR2OKH80yi0U+dUplyh3/xdpFvps6YkCwsXenIJxqxR1v9o+xtKTGbS9H7cps+2Vxjc8B1j96p75NmTGjIhtpQ==", "cpu": [ "x64" ], @@ -1005,13 +1116,16 @@ } }, "node_modules/@img/sharp-libvips-linux-arm": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", - "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.3.4.tgz", + "integrity": "sha512-LmRtTsOHuvM2+wlO2Db37dx5MiZhB0FvSunciw48YjdOkZz9KAiRbm8ujeMOA1INqmei5NapFxYEK1D1ZSidmw==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1022,13 +1136,16 @@ } }, "node_modules/@img/sharp-libvips-linux-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", - "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.3.4.tgz", + "integrity": "sha512-Y3dgX/6lE2QhQb+Gxy0WZxfg9MEm/JBjamZpS2IklP7xIQoKN4hzAm7KcMVGtaVDt3neE9OKBC7vAfonA/Lr1A==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1039,13 +1156,16 @@ } }, "node_modules/@img/sharp-libvips-linux-ppc64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", - "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.3.4.tgz", + "integrity": "sha512-Le6boB8Tai0Nis+gIxIpKx68UDVVIqdR8Tin5Yf1z2LJJQLDJvCDRqRu+jC2qCoD+eIomonmOwB4smBRxfVpYQ==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1056,13 +1176,16 @@ } }, "node_modules/@img/sharp-libvips-linux-riscv64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", - "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.3.4.tgz", + "integrity": "sha512-aHkkIEHPRdQEegJN20MLmGtxYD9R2wQr3Cwpddnu5+YKMt6Uzax7S9h5gpZTo8wyrGuZSlfQ63OevL5mTyOC7Q==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1073,13 +1196,16 @@ } }, "node_modules/@img/sharp-libvips-linux-s390x": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", - "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.3.4.tgz", + "integrity": "sha512-ra/mB6MikESDUO7Yg+Mi95bFBb9GsObURuhnOv3OqknjGe9sZrG8tCe9q0xSIGrtLgvgw0gKnFWcK4blSgQOuQ==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1090,13 +1216,16 @@ } }, "node_modules/@img/sharp-libvips-linux-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", - "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.3.4.tgz", + "integrity": "sha512-GJ//SSXbnwSDes02umB3nDJLFcQzw8a18V8fyhqr6tV515tOEMdImjjxj1AoafMRz56F3PHgftnj1QEKSU1zkw==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1107,13 +1236,16 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", - "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.3.4.tgz", + "integrity": "sha512-hvulFwtjUcagsis6BBxHwGFwWoNZjgYmULGVrZcyfNbjA8hKILbRxGg15/7w5HDyXHXUos/j6baAWqnCyQ2DWA==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1124,13 +1256,16 @@ } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", - "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.3.4.tgz", + "integrity": "sha512-6zXKeE/p39I1AmA3cJG35eyBGNqNddLnUXjhwBnsGjFPWqf5VKkDBEqaEkPDoTEtkxwi2vv8Tcr2mDyP4So7Fg==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ @@ -1141,213 +1276,254 @@ } }, "node_modules/@img/sharp-linux-arm": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", - "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.35.5.tgz", + "integrity": "sha512-LEaXK2WdXVK5ykcw0buWyPMsmLLL2vpHLD6yrNSW+JGEL3BZPA4tpKN6iaMc4AxTTAoaX/sU1rOL51lcIz48ZQ==", "cpu": [ "arm" ], "dev": true, + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm": "1.2.4" + "@img/sharp-libvips-linux-arm": "1.3.4" } }, "node_modules/@img/sharp-linux-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", - "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.35.5.tgz", + "integrity": "sha512-LYVx5JTsOM2CBzmxreh+nl64/3H6Xb09iSLknqH47z2T2DFFxDeFLP5y4dJwe6H7uGQlHPyEEtIqyo3DYsRwdQ==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-arm64": "1.2.4" + "@img/sharp-libvips-linux-arm64": "1.3.4" } }, "node_modules/@img/sharp-linux-ppc64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", - "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.35.5.tgz", + "integrity": "sha512-QVxAAq8evVRI9ia2vqgwrmWucn5Dfv+JdWzj75pD8omHLPSP7f8p20O8jxzjCcuCEQEOtYOZUmX1hkiZ0kdevA==", "cpu": [ "ppc64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-ppc64": "1.2.4" + "@img/sharp-libvips-linux-ppc64": "1.3.4" } }, "node_modules/@img/sharp-linux-riscv64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", - "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.35.5.tgz", + "integrity": "sha512-LtdreXguaavKODPIfzJ4kffx7UNt1omwtK0rch4EBbbSTXPnxWmYSayXdLJw0fJzQ97kHt1gL/yh4tvU+nCyRQ==", "cpu": [ "riscv64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-riscv64": "1.2.4" + "@img/sharp-libvips-linux-riscv64": "1.3.4" } }, "node_modules/@img/sharp-linux-s390x": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", - "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.35.5.tgz", + "integrity": "sha512-UZasTOFiYzotTsGOCu42BfUzP6Tu6Do/947iRm1RsLKvlllxwGcn4RN27LibGWceix4Y+Pmw3jsnTcCQIgWjqA==", "cpu": [ "s390x" ], "dev": true, + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-s390x": "1.2.4" + "@img/sharp-libvips-linux-s390x": "1.3.4" } }, "node_modules/@img/sharp-linux-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", - "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.35.5.tgz", + "integrity": "sha512-SxFtLTeJInhAA9Q836kux2vZNeOBQEx658qvbboZScr0wIARym3IcGmW7KpVD5sbVg0Ojy+udFQdayYIZyoNog==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "glibc" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linux-x64": "1.2.4" + "@img/sharp-libvips-linux-x64": "1.3.4" } }, "node_modules/@img/sharp-linuxmusl-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", - "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.35.5.tgz", + "integrity": "sha512-9HbMclmI1zlNkFRs3z9/eBtDjfD0sGlrX1z6b1qwmiFY5ElDLh4BC0LPBdVp7z1DXFiKlIcznf+ZlsuZzLxQqg==", "cpu": [ "arm64" ], "dev": true, + "libc": [ + "musl" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4" } }, "node_modules/@img/sharp-linuxmusl-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", - "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.35.5.tgz", + "integrity": "sha512-4KOphqB035HrVdqLZfCgMzzERrQkkzOwRhl4OAkRO1YCldbaFjySXMaK534Mo0V+LndnlJk+sbUyLeU0ULyD1A==", "cpu": [ "x64" ], "dev": true, + "libc": [ + "musl" + ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + "@img/sharp-libvips-linuxmusl-x64": "1.3.4" } }, "node_modules/@img/sharp-wasm32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", - "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.35.5.tgz", + "integrity": "sha512-Ptsga1su4tQx+LLF1ECS9U6nz5kmrXKo6XVbtR48Ke3ZRxxgaWBu7IDtEe1quo8hiupwm6WFqxVlXaSf7IINGQ==", + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.11.3" + }, + "engines": { + "node": ">=20.9.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-webcontainers-wasm32": { + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-webcontainers-wasm32/-/sharp-webcontainers-wasm32-0.35.5.tgz", + "integrity": "sha512-hfhF/FmoQyTUkA0bIKFOtw536BQSeBMe6BF6QyWlrPxT754+TFLaZ7sKKTfvvM0yJgKgaYTwnFCIZ/GuDw5SUA==", "cpu": [ "wasm32" ], "dev": true, - "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "license": "Apache-2.0", "optional": true, "dependencies": { - "@emnapi/runtime": "^1.7.0" + "@img/sharp-wasm32": "0.35.5" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", - "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.35.5.tgz", + "integrity": "sha512-X4t7g+7ZA5DKblCBEXGjUqqemj4vczING/5viFwAL8h4N3qYeyjwdCvRLHi4EdOUI+2Z7UFlp1VM+p/AuEtm6Q==", "cpu": [ "arm64" ], @@ -1358,16 +1534,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", - "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.35.5.tgz", + "integrity": "sha512-5Zm82LoBc43nhwNybZlG7Y1KO//Zhsn306fQl29ZOuStHLGTo3BWL83q3cznX0poxSAMuYL1On/BHBxkBeKr6A==", "cpu": [ "ia32" ], @@ -1378,16 +1554,16 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": "^20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", - "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.35.5.tgz", + "integrity": "sha512-x76eH0vEiHlcMQu8Y8IenntaACtddpT6W0wmXtWrnKcnKI7ME5DdgqhAD6SEWOEl1v2zDvkZDhFA9KnURwpfqg==", "cpu": [ "x64" ], @@ -1398,7 +1574,7 @@ "win32" ], "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" @@ -1415,9 +1591,9 @@ } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", "dev": true, "license": "MIT" }, @@ -1456,28 +1632,6 @@ "resolved": "services/worker", "link": true }, - "node_modules/@modelcontextprotocol/sdk": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.11.1.tgz", - "integrity": "sha512-9LfmxKTb1v+vUS1/emSk1f5ePmTLkb9Le9AxOB5T0XM59EUumwcS45z05h7aiZx3GI0Bl7mjb3FMEglYj+acuQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "content-type": "^1.0.5", - "cors": "^2.8.5", - "cross-spawn": "^7.0.3", - "eventsource": "^3.0.2", - "express": "^5.0.1", - "express-rate-limit": "^7.5.0", - "pkce-challenge": "^5.0.0", - "raw-body": "^3.0.0", - "zod": "^3.23.8", - "zod-to-json-schema": "^3.24.1" - }, - "engines": { - "node": ">=18" - } - }, "node_modules/@noble/hashes": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.8.0.tgz", @@ -1945,9 +2099,9 @@ } }, "node_modules/@speed-highlight/core": { - "version": "1.2.15", - "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.15.tgz", - "integrity": "sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==", + "version": "1.2.24", + "resolved": "https://registry.npmjs.org/@speed-highlight/core/-/core-1.2.24.tgz", + "integrity": "sha512-qeW2e1l78afw8VhRPfPQ1Gjj+KU5XFQ/OFV5ti6eTa9bruO7mJyZtA4vw0ofqmA3tKCkROE9xLk3VZoeRc98nw==", "dev": true, "license": "CC0-1.0" }, @@ -2356,32 +2510,6 @@ "typescript": ">=4.8.4 <5.9.0" } }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, "node_modules/@typescript-eslint/utils": { "version": "8.32.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.32.0.tgz", @@ -2425,15 +2553,15 @@ } }, "node_modules/@vitest/expect": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz", - "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.7.tgz", + "integrity": "sha512-E8eBXaKibuvH2pSZErOjdVb5vF4PbKYcrnluBTYxEk1l/VhhwZg1kZQsdtjq+CsF5CFydf2Rdkz7jDHKSisi3w==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" }, @@ -2442,13 +2570,13 @@ } }, "node_modules/@vitest/mocker": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.4.tgz", - "integrity": "sha512-46ryTE9RZO/rfDd7pEqFl7etuyzekzEhUbTW3BvmeO/BcCMEgq59BKhek3dXDWgAj4oMK6OZi+vRr1wPW6qjEQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-3.2.7.tgz", + "integrity": "sha512-Trr0hYO9CM3Wj6ksWHRhK9IZpIY6wTMO5u/MqXurMxT57sWBaOPEtP3Oq60ihZuh5JsiagKfz95OcxdEP6dBrA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/spy": "3.2.4", + "@vitest/spy": "3.2.7", "estree-walker": "^3.0.3", "magic-string": "^0.30.17" }, @@ -2469,9 +2597,9 @@ } }, "node_modules/@vitest/pretty-format": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz", - "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.7.tgz", + "integrity": "sha512-KUHlwqVu0sRlhCdyPdQ/wBoTfRahjUky1MubOmYw9fWfIZy1gNoHpuaaQBPAaMaVYdQYHJLurzj8ECCj5OwTqA==", "dev": true, "license": "MIT", "dependencies": { @@ -2482,13 +2610,13 @@ } }, "node_modules/@vitest/runner": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.4.tgz", - "integrity": "sha512-oukfKT9Mk41LreEW09vt45f8wx7DordoWUZMYdY/cyAk7w5TWkTRCNZYF7sX7n2wB7jyGAl74OxgwhPgKaqDMQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-3.2.7.tgz", + "integrity": "sha512-sB9y4ovltoQP+WaUPwmSxO9WIg9Ig694Di5PalVPsYHklAdE027mehpWF2SQSVq+k6sFgaivbTjTJwZLSHbedA==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/utils": "3.2.4", + "@vitest/utils": "3.2.7", "pathe": "^2.0.3", "strip-literal": "^3.0.0" }, @@ -2497,13 +2625,13 @@ } }, "node_modules/@vitest/snapshot": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.4.tgz", - "integrity": "sha512-dEYtS7qQP2CjU27QBC5oUOxLE/v5eLkGqPE0ZKEIDGMs4vKWe7IjgLOeauHsR0D5YuuycGRO5oSRXnwnmA78fQ==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-3.2.7.tgz", + "integrity": "sha512-7C+MwShwtBSI5Buwoyg3s/iY1eHL9PKAf+O1wVh/TdnjXUtkoL/9YQtre90i4MtNXM6edP1wJ2zOBpfCyhIS7g==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.4", + "@vitest/pretty-format": "3.2.7", "magic-string": "^0.30.17", "pathe": "^2.0.3" }, @@ -2512,9 +2640,9 @@ } }, "node_modules/@vitest/spy": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz", - "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.7.tgz", + "integrity": "sha512-Q2eQGI6d2L/hBtZ0qNuKcAGid68XK6cv1xsoaIma6PaJhHPoqcEJhYpXZ/5myCMqkNgtP6UKuBhbc0nHKnrkuQ==", "dev": true, "license": "MIT", "dependencies": { @@ -2525,13 +2653,13 @@ } }, "node_modules/@vitest/utils": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz", - "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.7.tgz", + "integrity": "sha512-x6BDOd7dyo3PFLY3I9/HJ25X/6OurhGXk2/B9gOZNPF7XDVjeBK4k01lQE5uvDpbuheErh91qYuE1E2OEjK3Rw==", "dev": true, "license": "MIT", "dependencies": { - "@vitest/pretty-format": "3.2.4", + "@vitest/pretty-format": "3.2.7", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" }, @@ -2553,9 +2681,9 @@ } }, "node_modules/acorn": { - "version": "8.14.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.1.tgz", - "integrity": "sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==", + "version": "8.19.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.19.0.tgz", + "integrity": "sha512-oJlA3XiRm7Cyk6qFD2Jn8ak9B7jSy0qy00ADO3+8dpT0LSjFihQYv4C02LFCSYJV3Q37xYwwRy5m+IpIiUzqWw==", "dev": true, "license": "MIT", "bin": { @@ -2576,9 +2704,9 @@ } }, "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", "dev": true, "license": "MIT", "dependencies": { @@ -2664,34 +2792,50 @@ "license": "MIT" }, "node_modules/body-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", - "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.4.0.tgz", + "integrity": "sha512-Ifk1KcLrxaGt1occQagQNFNNOqRt1LFuehEnHHzB8Z6cRgIxRU1TBaiIsUiCdA+gy5PL7Y1IX31DNKkRmxtXjQ==", "license": "MIT", "dependencies": { "bytes": "^3.1.2", - "content-type": "^1.0.5", - "debug": "^4.4.0", - "http-errors": "^2.0.0", - "iconv-lite": "^0.6.3", + "content-type": "^2.1.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", + "iconv-lite": "^0.7.3", "on-finished": "^2.4.1", - "qs": "^6.14.0", - "raw-body": "^3.0.0", - "type-is": "^2.0.0" + "qs": "^6.16.1", + "raw-body": "^3.0.2", + "type-is": "^2.1.0" }, "engines": { "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/body-parser/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.1.7.tgz", + "integrity": "sha512-uZbew1NqdmPDTMJ8ah1y+b+9QEJrfkXFk3RcTQw3X0jW/xRUvFKsg1CfQdSYGdTbXZWExtU3J3ccxtnfw1Fi0g==", "dev": true, "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/braces": { @@ -3110,9 +3254,9 @@ } }, "node_modules/dompurify": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.3.tgz", - "integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==", + "version": "3.4.17", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.4.17.tgz", + "integrity": "sha512-B/pSlYVHQ58kqISD/yDjuJ1yfzI34m1gF4lAod2bwSRhiN4K5Cl3H8Af7hqB2PmTL911tdEudnnXn5FnhRoz0w==", "license": "(MPL-2.0 OR Apache-2.0)", "optional": true, "optionalDependencies": { @@ -3273,34 +3417,33 @@ } }, "node_modules/eslint": { - "version": "9.26.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.26.0.tgz", - "integrity": "sha512-Hx0MOjPh6uK9oq9nVsATZKE/Wlbai7KFjfCuw9UHaguDW3x+HF0O5nIi3ud39TWgrTjTO5nHxmL3R1eANinWHQ==", + "version": "9.39.5", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.5.tgz", + "integrity": "sha512-DgZS62aPLXKlnxILS/AYCoRvHaZeXceIzlXPkkGGzJWSow1aEk0lbTlxUSlyjC8jcaKxAdOnTDz+o1JFSBsyjw==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.20.0", - "@eslint/config-helpers": "^0.2.1", - "@eslint/core": "^0.13.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.26.0", - "@eslint/plugin-kit": "^0.2.8", + "@eslint/config-array": "^0.21.2", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.6", + "@eslint/js": "9.39.5", + "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", - "@modelcontextprotocol/sdk": "^1.8.0", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", + "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.3.0", - "eslint-visitor-keys": "^4.2.0", - "espree": "^10.3.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -3312,10 +3455,9 @@ "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", + "minimatch": "^3.1.5", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "zod": "^3.24.2" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" @@ -3336,9 +3478,9 @@ } }, "node_modules/eslint-scope": { - "version": "8.3.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.3.0.tgz", - "integrity": "sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { @@ -3353,9 +3495,9 @@ } }, "node_modules/eslint-visitor-keys": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz", - "integrity": "sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { @@ -3365,16 +3507,67 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/eslint/node_modules/@eslint/core": { + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/eslint/node_modules/@eslint/plugin-kit": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0", + "levn": "^0.4.1" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/eslint/node_modules/brace-expansion": { + "version": "1.1.21", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.21.tgz", + "integrity": "sha512-9zeA+KLZNNzglF2TPKRQEDyx6Yby7daAkuy8MiPzpXPsYDWi/DRM8jmwUDxokQjYqBpv5DgPiwD4h4ZZSy1Ujw==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", + "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/espree": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.3.0.tgz", - "integrity": "sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.14.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -3448,29 +3641,6 @@ "node": ">= 0.6" } }, - "node_modules/eventsource": { - "version": "3.0.7", - "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", - "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eventsource-parser": "^3.0.1" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/eventsource-parser": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.1.tgz", - "integrity": "sha512-VARTJ9CYeuQYb0pZEPbzi740OWFgpHe7AYJ2WFZVnUDUQp5Dk2yJUgF36YsZ81cOyxT0QxmXD2EQpapAouzWVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.0.0" - } - }, "node_modules/expect-type": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", @@ -3632,9 +3802,9 @@ } }, "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.3.tgz", + "integrity": "sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==", "license": "MIT" }, "node_modules/file-entry-cache": { @@ -3712,24 +3882,24 @@ } }, "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.4.tgz", + "integrity": "sha512-5+ybhBZANEJxaH3X5evAFatUxLfEHSr7n6kYJ+1Qd0mUqr4eu9gIf6GDbWHf8RJijHrjjO8G+la14SlL2SeS1Q==", "dev": true, "license": "ISC" }, "node_modules/form-data": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", - "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "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.2", - "mime-types": "^2.1.12" + "hasown": "^2.0.4", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -3864,6 +4034,13 @@ "node": ">= 0.4" } }, + "node_modules/github-slugger": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/github-slugger/-/github-slugger-2.0.0.tgz", + "integrity": "sha512-IaOQ9puYtjrkq7Y0Ygl9KDZnrf/aiUJYUpVf89y8kyaxbRG7Y1SrX/jaumrv81vc61+kiMempujsM3Yw7w5qcw==", + "dev": true, + "license": "ISC" + }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -3948,9 +4125,9 @@ } }, "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "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==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" @@ -3974,31 +4151,39 @@ } }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "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==", + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.3.tgz", + "integrity": "sha512-IKXpvIzjnC9XTAUbVBcMfGS0EPaIXtW6v+zr+RRp+hqULEpo0owZax6wyRwPOJbWbzjYspQwusTsfVr0ifh4uQ==", "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/ignore": { @@ -4113,10 +4298,20 @@ "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", "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" @@ -5218,37 +5413,37 @@ } }, "node_modules/miniflare": { - "version": "4.20260317.1", - "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-4.20260317.1.tgz", - "integrity": "sha512-A3csI1HXEIfqe3oscgpoRMHdYlkReQKPH/g5JE53vFSjoM6YIAOGAzyDNeYffwd9oQkPWDj9xER8+vpxei8klA==", + "version": "5.20261023.0-beta", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-5.20261023.0-beta.tgz", + "integrity": "sha512-SHY8A98gCvmrnBLqABHRTSh3duAL1z+tHPRZu/tqxHFqVARrZ4oQis/ALYwp3XvzHvs6B/ff/CdcCafqP+cOFA==", "dev": true, "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "0.8.1", - "sharp": "^0.34.5", - "undici": "7.24.4", - "workerd": "1.20260317.1", - "ws": "8.18.0", + "sharp": "0.35.5", + "undici": "7.30.1", + "workerd": "1.20261023.1", + "ws": "8.21.0", "youch": "4.1.0-beta.10" }, - "bin": { - "miniflare": "bootstrap.js" - }, "engines": { - "node": ">=18.0.0" + "node": ">=22.0.0" } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "dev": true, "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.2" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/ms": { @@ -5258,9 +5453,9 @@ "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "version": "3.3.20", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.20.tgz", + "integrity": "sha512-uKdg2G3GNCKQn9byYOpxbGqrT2fGO5KRt5J/8b3pok8rT6qxGWF6hxMyJiEYtAf+FVyYuD9hRaDqX5uPFYJ4ZQ==", "dev": true, "funding": [ { @@ -5433,12 +5628,13 @@ } }, "node_modules/path-to-regexp": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", - "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", "license": "MIT", - "engines": { - "node": ">=16" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/pathe": { @@ -5473,9 +5669,9 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "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": { @@ -5485,20 +5681,10 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/pkce-challenge": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", - "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=16.20.0" - } - }, "node_modules/postcss": { - "version": "8.5.8", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", - "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "version": "8.5.31", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.31.tgz", + "integrity": "sha512-YUbWYCgErwnMAVkjUnYa+KlMBzGpHwiIt/DE7f0G33AuX2LH9UcTk6KgWxd3zRU+pN3u/C+Xl3EnMLM4Hea7Sg==", "dev": true, "funding": [ { @@ -5516,9 +5702,9 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.11", + "nanoid": "^3.3.20", "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" + "source-map-js": "^1.2.2" }, "engines": { "node": "^10 || ^12 || >=14" @@ -5535,9 +5721,9 @@ } }, "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz", + "integrity": "sha512-5nnx0yGyVUcY6t9RnWcARWtwT9F1D8O9rt08htPvnd49W1IgZtmLkhu9WfMzQj1cFxjHIO6connUNVW5k7AVyQ==", "license": "MIT", "dependencies": { "forwarded": "0.2.0", @@ -5545,6 +5731,10 @@ }, "engines": { "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/punycode": { @@ -5558,12 +5748,13 @@ } }, "node_modules/qs": { - "version": "6.15.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", - "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.1.tgz", + "integrity": "sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg==", "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.1.0" + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" }, "engines": { "node": ">=0.6" @@ -5613,18 +5804,18 @@ } }, "node_modules/raw-body": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", - "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "dependencies": { - "bytes": "3.1.2", - "http-errors": "2.0.0", - "iconv-lite": "0.6.3", - "unpipe": "1.0.0" + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" }, "engines": { - "node": ">= 0.8" + "node": ">= 0.10" } }, "node_modules/regenerator-runtime": { @@ -5777,9 +5968,9 @@ "license": "MIT" }, "node_modules/semver": { - "version": "7.7.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", - "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "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": { @@ -5833,48 +6024,53 @@ "license": "ISC" }, "node_modules/sharp": { - "version": "0.34.5", - "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", - "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", + "version": "0.35.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.35.5.tgz", + "integrity": "sha512-Ywn4OnzGukp7CDMrp08RQ50YKmuwG47brZgIVPTvBaaAfQlRlygrRqSrxdCiL9M+LlzLBiJ68IR1QqvzHyjC7g==", "dev": true, - "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "@img/colour": "^1.0.0", + "@img/colour": "^1.1.0", "detect-libc": "^2.1.2", - "semver": "^7.7.3" + "semver": "^7.8.5" }, "engines": { - "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + "node": ">=20.9.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { - "@img/sharp-darwin-arm64": "0.34.5", - "@img/sharp-darwin-x64": "0.34.5", - "@img/sharp-libvips-darwin-arm64": "1.2.4", - "@img/sharp-libvips-darwin-x64": "1.2.4", - "@img/sharp-libvips-linux-arm": "1.2.4", - "@img/sharp-libvips-linux-arm64": "1.2.4", - "@img/sharp-libvips-linux-ppc64": "1.2.4", - "@img/sharp-libvips-linux-riscv64": "1.2.4", - "@img/sharp-libvips-linux-s390x": "1.2.4", - "@img/sharp-libvips-linux-x64": "1.2.4", - "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", - "@img/sharp-libvips-linuxmusl-x64": "1.2.4", - "@img/sharp-linux-arm": "0.34.5", - "@img/sharp-linux-arm64": "0.34.5", - "@img/sharp-linux-ppc64": "0.34.5", - "@img/sharp-linux-riscv64": "0.34.5", - "@img/sharp-linux-s390x": "0.34.5", - "@img/sharp-linux-x64": "0.34.5", - "@img/sharp-linuxmusl-arm64": "0.34.5", - "@img/sharp-linuxmusl-x64": "0.34.5", - "@img/sharp-wasm32": "0.34.5", - "@img/sharp-win32-arm64": "0.34.5", - "@img/sharp-win32-ia32": "0.34.5", - "@img/sharp-win32-x64": "0.34.5" + "@img/sharp-darwin-arm64": "0.35.5", + "@img/sharp-darwin-x64": "0.35.5", + "@img/sharp-freebsd-wasm32": "0.35.5", + "@img/sharp-libvips-darwin-arm64": "1.3.4", + "@img/sharp-libvips-darwin-x64": "1.3.4", + "@img/sharp-libvips-linux-arm": "1.3.4", + "@img/sharp-libvips-linux-arm64": "1.3.4", + "@img/sharp-libvips-linux-ppc64": "1.3.4", + "@img/sharp-libvips-linux-riscv64": "1.3.4", + "@img/sharp-libvips-linux-s390x": "1.3.4", + "@img/sharp-libvips-linux-x64": "1.3.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.3.4", + "@img/sharp-libvips-linuxmusl-x64": "1.3.4", + "@img/sharp-linux-arm": "0.35.5", + "@img/sharp-linux-arm64": "0.35.5", + "@img/sharp-linux-ppc64": "0.35.5", + "@img/sharp-linux-riscv64": "0.35.5", + "@img/sharp-linux-s390x": "0.35.5", + "@img/sharp-linux-x64": "0.35.5", + "@img/sharp-linuxmusl-arm64": "0.35.5", + "@img/sharp-linuxmusl-x64": "0.35.5", + "@img/sharp-webcontainers-wasm32": "0.35.5", + "@img/sharp-win32-arm64": "0.35.5", + "@img/sharp-win32-ia32": "0.35.5", + "@img/sharp-win32-x64": "0.35.5" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + } } }, "node_modules/shebang-command": { @@ -5901,14 +6097,14 @@ } }, "node_modules/side-channel": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", - "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3", - "side-channel-list": "^1.0.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, @@ -5920,13 +6116,13 @@ } }, "node_modules/side-channel-list": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", - "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "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==", "license": "MIT", "dependencies": { "es-errors": "^1.3.0", - "object-inspect": "^1.13.3" + "object-inspect": "^1.13.4" }, "engines": { "node": ">= 0.4" @@ -5980,9 +6176,9 @@ "license": "ISC" }, "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.2.tgz", + "integrity": "sha512-KGj/8Y43x35aZVDtt+J4mK1hoLGHULMYfSkODJNQjNDC3oW1PqPoxMwo0pLUsWM/UEGzON/NxeHywEfNXNP3Vw==", "dev": true, "license": "BSD-3-Clause", "engines": { @@ -6007,9 +6203,9 @@ } }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "engines": { "node": ">= 0.8" @@ -6167,9 +6363,9 @@ } }, "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -6200,9 +6396,9 @@ } }, "node_modules/tinyspy": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz", - "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==", + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.6.tgz", + "integrity": "sha512-u8KszXvGfU68hVcZpRHKG28T0krMuv2G5nDhiHaMLen/gIuFEgIJhaJuO69qjnXg5paSrbPMFfx3brNuN8eVSg==", "dev": true, "license": "MIT", "engines": { @@ -6266,17 +6462,34 @@ } }, "node_modules/type-is": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", - "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", "license": "MIT", "dependencies": { - "content-type": "^1.0.5", + "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" }, "engines": { - "node": ">= 0.6" + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/typescript": { @@ -6317,9 +6530,9 @@ } }, "node_modules/undici": { - "version": "7.24.4", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.24.4.tgz", - "integrity": "sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==", + "version": "7.30.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.1.tgz", + "integrity": "sha512-03KrMbjIFDWQR5RNPrSw3bd1r12JpyFQl9/yxjUbJtqkjmllqC5lFjZh6ujeuJCKr8EOQvRW872Oy/az+nLYxg==", "dev": true, "license": "MIT", "engines": { @@ -6441,13 +6654,13 @@ } }, "node_modules/vite": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", - "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "version": "7.3.7", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.7.tgz", + "integrity": "sha512-AHEzTORMbi7/7aXp7w7KZ5+DCUdSeLsJz0grY8xHpc9DHdYb0vQu6DqJfm2tOw3gDExNgPgnJTXxM6syYFHaCA==", "dev": true, "license": "MIT", "dependencies": { - "esbuild": "^0.27.0", + "esbuild": "^0.27.0 || ^0.28.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", @@ -6539,9 +6752,9 @@ } }, "node_modules/vite/node_modules/@esbuild/aix-ppc64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", - "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", "cpu": [ "ppc64" ], @@ -6556,9 +6769,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.4.tgz", - "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", "cpu": [ "arm" ], @@ -6573,9 +6786,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", - "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", "cpu": [ "arm64" ], @@ -6590,9 +6803,9 @@ } }, "node_modules/vite/node_modules/@esbuild/android-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.4.tgz", - "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", "cpu": [ "x64" ], @@ -6607,9 +6820,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", - "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", "cpu": [ "arm64" ], @@ -6624,9 +6837,9 @@ } }, "node_modules/vite/node_modules/@esbuild/darwin-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", - "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", "cpu": [ "x64" ], @@ -6641,9 +6854,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", - "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", "cpu": [ "arm64" ], @@ -6658,9 +6871,9 @@ } }, "node_modules/vite/node_modules/@esbuild/freebsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", - "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", "cpu": [ "x64" ], @@ -6675,9 +6888,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", - "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", "cpu": [ "arm" ], @@ -6692,9 +6905,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", - "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", "cpu": [ "arm64" ], @@ -6709,9 +6922,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ia32": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", - "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", "cpu": [ "ia32" ], @@ -6726,9 +6939,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-loong64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", - "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", "cpu": [ "loong64" ], @@ -6743,9 +6956,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-mips64el": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", - "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", "cpu": [ "mips64el" ], @@ -6760,9 +6973,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-ppc64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", - "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", "cpu": [ "ppc64" ], @@ -6777,9 +6990,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-riscv64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", - "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", "cpu": [ "riscv64" ], @@ -6794,9 +7007,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-s390x": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", - "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", "cpu": [ "s390x" ], @@ -6811,9 +7024,9 @@ } }, "node_modules/vite/node_modules/@esbuild/linux-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", - "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", "cpu": [ "x64" ], @@ -6828,9 +7041,9 @@ } }, "node_modules/vite/node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", - "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", "cpu": [ "arm64" ], @@ -6845,9 +7058,9 @@ } }, "node_modules/vite/node_modules/@esbuild/netbsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", - "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", "cpu": [ "x64" ], @@ -6862,9 +7075,9 @@ } }, "node_modules/vite/node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", - "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", "cpu": [ "arm64" ], @@ -6879,9 +7092,9 @@ } }, "node_modules/vite/node_modules/@esbuild/openbsd-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", - "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", "cpu": [ "x64" ], @@ -6896,9 +7109,9 @@ } }, "node_modules/vite/node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", - "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", "cpu": [ "arm64" ], @@ -6913,9 +7126,9 @@ } }, "node_modules/vite/node_modules/@esbuild/sunos-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", - "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", "cpu": [ "x64" ], @@ -6930,9 +7143,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-arm64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", - "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", "cpu": [ "arm64" ], @@ -6947,9 +7160,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-ia32": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", - "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", "cpu": [ "ia32" ], @@ -6964,9 +7177,9 @@ } }, "node_modules/vite/node_modules/@esbuild/win32-x64": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", - "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", "cpu": [ "x64" ], @@ -6981,9 +7194,9 @@ } }, "node_modules/vite/node_modules/esbuild": { - "version": "0.27.4", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.4.tgz", - "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -6994,32 +7207,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.4", - "@esbuild/android-arm": "0.27.4", - "@esbuild/android-arm64": "0.27.4", - "@esbuild/android-x64": "0.27.4", - "@esbuild/darwin-arm64": "0.27.4", - "@esbuild/darwin-x64": "0.27.4", - "@esbuild/freebsd-arm64": "0.27.4", - "@esbuild/freebsd-x64": "0.27.4", - "@esbuild/linux-arm": "0.27.4", - "@esbuild/linux-arm64": "0.27.4", - "@esbuild/linux-ia32": "0.27.4", - "@esbuild/linux-loong64": "0.27.4", - "@esbuild/linux-mips64el": "0.27.4", - "@esbuild/linux-ppc64": "0.27.4", - "@esbuild/linux-riscv64": "0.27.4", - "@esbuild/linux-s390x": "0.27.4", - "@esbuild/linux-x64": "0.27.4", - "@esbuild/netbsd-arm64": "0.27.4", - "@esbuild/netbsd-x64": "0.27.4", - "@esbuild/openbsd-arm64": "0.27.4", - "@esbuild/openbsd-x64": "0.27.4", - "@esbuild/openharmony-arm64": "0.27.4", - "@esbuild/sunos-x64": "0.27.4", - "@esbuild/win32-arm64": "0.27.4", - "@esbuild/win32-ia32": "0.27.4", - "@esbuild/win32-x64": "0.27.4" + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" } }, "node_modules/vite/node_modules/fdir": { @@ -7041,9 +7254,9 @@ } }, "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -7054,20 +7267,20 @@ } }, "node_modules/vitest": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.4.tgz", - "integrity": "sha512-LUCP5ev3GURDysTWiP47wRRUpLKMOfPh+yKTx3kVIEiu5KOMeqzpnYNsKyOoVrULivR8tLcks4+lga33Whn90A==", + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-3.2.7.tgz", + "integrity": "sha512-KrxIJ62Fd89gfysR4WotlgZABiz2dqFPgqGzX7s+CwsqLFomRH7777ZcrOD6+WVAh7khPQP41A+BKbpcJFrdEg==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/expect": "3.2.4", - "@vitest/mocker": "3.2.4", - "@vitest/pretty-format": "^3.2.4", - "@vitest/runner": "3.2.4", - "@vitest/snapshot": "3.2.4", - "@vitest/spy": "3.2.4", - "@vitest/utils": "3.2.4", + "@vitest/expect": "3.2.7", + "@vitest/mocker": "3.2.7", + "@vitest/pretty-format": "^3.2.7", + "@vitest/runner": "3.2.7", + "@vitest/snapshot": "3.2.7", + "@vitest/spy": "3.2.7", + "@vitest/utils": "3.2.7", "chai": "^5.2.0", "debug": "^4.4.1", "expect-type": "^1.2.1", @@ -7097,8 +7310,8 @@ "@edge-runtime/vm": "*", "@types/debug": "^4.1.12", "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "@vitest/browser": "3.2.4", - "@vitest/ui": "3.2.4", + "@vitest/browser": "3.2.7", + "@vitest/ui": "3.2.7", "happy-dom": "*", "jsdom": "*" }, @@ -7127,9 +7340,9 @@ } }, "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", "dev": true, "license": "MIT", "engines": { @@ -7183,9 +7396,9 @@ } }, "node_modules/workerd": { - "version": "1.20260317.1", - "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20260317.1.tgz", - "integrity": "sha512-ZuEq1OdrJBS+NV+L5HMYPCzVn49a2O60slQiiLpG44jqtlOo+S167fWC76kEXteXLLLydeuRrluRel7WdOUa4g==", + "version": "1.20261023.1", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20261023.1.tgz", + "integrity": "sha512-NeofJFyaYNpqKpFRJW3ApuonaerPd6/JWlfTtLDYf2A6ygqrDEFnYgEXmc0hhOxqnJsSM+kXJN6VebPojzRrsg==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", @@ -7196,41 +7409,42 @@ "node": ">=16" }, "optionalDependencies": { - "@cloudflare/workerd-darwin-64": "1.20260317.1", - "@cloudflare/workerd-darwin-arm64": "1.20260317.1", - "@cloudflare/workerd-linux-64": "1.20260317.1", - "@cloudflare/workerd-linux-arm64": "1.20260317.1", - "@cloudflare/workerd-windows-64": "1.20260317.1" + "@cloudflare/workerd-darwin-64": "1.20261023.1", + "@cloudflare/workerd-darwin-arm64": "1.20261023.1", + "@cloudflare/workerd-linux-64": "1.20261023.1", + "@cloudflare/workerd-linux-arm64": "1.20261023.1", + "@cloudflare/workerd-windows-64": "1.20261023.1" } }, "node_modules/wrangler": { - "version": "4.76.0", - "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.76.0.tgz", - "integrity": "sha512-Wan+CU5a0tu4HIxGOrzjNbkmxCT27HUmzrMj6kc7aoAnjSLv50Ggcn2Ant7wNQrD6xW3g31phKupZJgTZ8wZfQ==", + "version": "4.153.0", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-4.153.0.tgz", + "integrity": "sha512-Hn1AowKzZao/729rMpdpxBZt/RaKQNJ4GtKtHQcOrwT+J/GIq0iwl680+dhJyO00iSsXaCkxoNcLg3yVIwIvyw==", "dev": true, "license": "MIT OR Apache-2.0", "dependencies": { - "@cloudflare/kv-asset-handler": "0.4.2", - "@cloudflare/unenv-preset": "2.16.0", + "@cloudflare/kv-asset-handler": "0.5.0", + "@cloudflare/unenv-preset": "2.16.2", "blake3-wasm": "2.1.5", - "esbuild": "0.27.3", - "miniflare": "4.20260317.1", + "esbuild": "0.28.2", + "miniflare": "5.20261023.0-beta", "path-to-regexp": "6.3.0", "unenv": "2.0.0-rc.24", - "workerd": "1.20260317.1" + "workerd": "1.20261023.1" }, "bin": { + "cf-wrangler": "bin/cf-wrangler.js", "wrangler": "bin/wrangler.js", "wrangler2": "bin/wrangler.js" }, "engines": { - "node": ">=20.0.0" + "node": ">=22.0.0" }, "optionalDependencies": { - "fsevents": "~2.3.2" + "fsevents": "2.3.3" }, "peerDependencies": { - "@cloudflare/workers-types": "^4.20260317.1" + "@cloudflare/workers-types": "^5.20261023.1" }, "peerDependenciesMeta": { "@cloudflare/workers-types": { @@ -7239,9 +7453,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/aix-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz", - "integrity": "sha512-9fJMTNFTWZMh5qwrBItuziu834eOCUcEqymSH7pY+zoMVEZg3gcPuBNxH1EvfVYe9h0x/Ptw8KBzv7qxb7l8dg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", "cpu": [ "ppc64" ], @@ -7256,9 +7470,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/android-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.3.tgz", - "integrity": "sha512-i5D1hPY7GIQmXlXhs2w8AWHhenb00+GxjxRncS2ZM7YNVGNfaMxgzSGuO8o8SJzRc/oZwU2bcScvVERk03QhzA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", "cpu": [ "arm" ], @@ -7273,9 +7487,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/android-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.3.tgz", - "integrity": "sha512-YdghPYUmj/FX2SYKJ0OZxf+iaKgMsKHVPF1MAq/P8WirnSpCStzKJFjOjzsW0QQ7oIAiccHdcqjbHmJxRb/dmg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", "cpu": [ "arm64" ], @@ -7290,9 +7504,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/android-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.3.tgz", - "integrity": "sha512-IN/0BNTkHtk8lkOM8JWAYFg4ORxBkZQf9zXiEOfERX/CzxW3Vg1ewAhU7QSWQpVIzTW+b8Xy+lGzdYXV6UZObQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", "cpu": [ "x64" ], @@ -7307,9 +7521,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/darwin-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.3.tgz", - "integrity": "sha512-Re491k7ByTVRy0t3EKWajdLIr0gz2kKKfzafkth4Q8A5n1xTHrkqZgLLjFEHVD+AXdUGgQMq+Godfq45mGpCKg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", "cpu": [ "arm64" ], @@ -7324,9 +7538,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/darwin-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.3.tgz", - "integrity": "sha512-vHk/hA7/1AckjGzRqi6wbo+jaShzRowYip6rt6q7VYEDX4LEy1pZfDpdxCBnGtl+A5zq8iXDcyuxwtv3hNtHFg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", "cpu": [ "x64" ], @@ -7341,9 +7555,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/freebsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.3.tgz", - "integrity": "sha512-ipTYM2fjt3kQAYOvo6vcxJx3nBYAzPjgTCk7QEgZG8AUO3ydUhvelmhrbOheMnGOlaSFUoHXB6un+A7q4ygY9w==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", "cpu": [ "arm64" ], @@ -7358,9 +7572,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/freebsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.3.tgz", - "integrity": "sha512-dDk0X87T7mI6U3K9VjWtHOXqwAMJBNN2r7bejDsc+j03SEjtD9HrOl8gVFByeM0aJksoUuUVU9TBaZa2rgj0oA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", "cpu": [ "x64" ], @@ -7375,9 +7589,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/linux-arm": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.3.tgz", - "integrity": "sha512-s6nPv2QkSupJwLYyfS+gwdirm0ukyTFNl3KTgZEAiJDd+iHZcbTPPcWCcRYH+WlNbwChgH2QkE9NSlNrMT8Gfw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", "cpu": [ "arm" ], @@ -7392,9 +7606,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/linux-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.3.tgz", - "integrity": "sha512-sZOuFz/xWnZ4KH3YfFrKCf1WyPZHakVzTiqji3WDc0BCl2kBwiJLCXpzLzUBLgmp4veFZdvN5ChW4Eq/8Fc2Fg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", "cpu": [ "arm64" ], @@ -7409,9 +7623,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/linux-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.3.tgz", - "integrity": "sha512-yGlQYjdxtLdh0a3jHjuwOrxQjOZYD/C9PfdbgJJF3TIZWnm/tMd/RcNiLngiu4iwcBAOezdnSLAwQDPqTmtTYg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", "cpu": [ "ia32" ], @@ -7426,9 +7640,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/linux-loong64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.3.tgz", - "integrity": "sha512-WO60Sn8ly3gtzhyjATDgieJNet/KqsDlX5nRC5Y3oTFcS1l0KWba+SEa9Ja1GfDqSF1z6hif/SkpQJbL63cgOA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", "cpu": [ "loong64" ], @@ -7443,9 +7657,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/linux-mips64el": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.3.tgz", - "integrity": "sha512-APsymYA6sGcZ4pD6k+UxbDjOFSvPWyZhjaiPyl/f79xKxwTnrn5QUnXR5prvetuaSMsb4jgeHewIDCIWljrSxw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", "cpu": [ "mips64el" ], @@ -7460,9 +7674,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/linux-ppc64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.3.tgz", - "integrity": "sha512-eizBnTeBefojtDb9nSh4vvVQ3V9Qf9Df01PfawPcRzJH4gFSgrObw+LveUyDoKU3kxi5+9RJTCWlj4FjYXVPEA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", "cpu": [ "ppc64" ], @@ -7477,9 +7691,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/linux-riscv64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.3.tgz", - "integrity": "sha512-3Emwh0r5wmfm3ssTWRQSyVhbOHvqegUDRd0WhmXKX2mkHJe1SFCMJhagUleMq+Uci34wLSipf8Lagt4LlpRFWQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", "cpu": [ "riscv64" ], @@ -7494,9 +7708,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/linux-s390x": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.3.tgz", - "integrity": "sha512-pBHUx9LzXWBc7MFIEEL0yD/ZVtNgLytvx60gES28GcWMqil8ElCYR4kvbV2BDqsHOvVDRrOxGySBM9Fcv744hw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", "cpu": [ "s390x" ], @@ -7511,9 +7725,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/linux-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.3.tgz", - "integrity": "sha512-Czi8yzXUWIQYAtL/2y6vogER8pvcsOsk5cpwL4Gk5nJqH5UZiVByIY8Eorm5R13gq+DQKYg0+JyQoytLQas4dA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", "cpu": [ "x64" ], @@ -7528,9 +7742,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/netbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.3.tgz", - "integrity": "sha512-sDpk0RgmTCR/5HguIZa9n9u+HVKf40fbEUt+iTzSnCaGvY9kFP0YKBWZtJaraonFnqef5SlJ8/TiPAxzyS+UoA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", "cpu": [ "arm64" ], @@ -7545,9 +7759,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/netbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.3.tgz", - "integrity": "sha512-P14lFKJl/DdaE00LItAukUdZO5iqNH7+PjoBm+fLQjtxfcfFE20Xf5CrLsmZdq5LFFZzb5JMZ9grUwvtVYzjiA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", "cpu": [ "x64" ], @@ -7562,9 +7776,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/openbsd-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.3.tgz", - "integrity": "sha512-AIcMP77AvirGbRl/UZFTq5hjXK+2wC7qFRGoHSDrZ5v5b8DK/GYpXW3CPRL53NkvDqb9D+alBiC/dV0Fb7eJcw==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", "cpu": [ "arm64" ], @@ -7579,9 +7793,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/openbsd-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.3.tgz", - "integrity": "sha512-DnW2sRrBzA+YnE70LKqnM3P+z8vehfJWHXECbwBmH/CU51z6FiqTQTHFenPlHmo3a8UgpLyH3PT+87OViOh1AQ==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", "cpu": [ "x64" ], @@ -7596,9 +7810,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/openharmony-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.3.tgz", - "integrity": "sha512-NinAEgr/etERPTsZJ7aEZQvvg/A6IsZG/LgZy+81wON2huV7SrK3e63dU0XhyZP4RKGyTm7aOgmQk0bGp0fy2g==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", "cpu": [ "arm64" ], @@ -7613,9 +7827,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/sunos-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.3.tgz", - "integrity": "sha512-PanZ+nEz+eWoBJ8/f8HKxTTD172SKwdXebZ0ndd953gt1HRBbhMsaNqjTyYLGLPdoWHy4zLU7bDVJztF5f3BHA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", "cpu": [ "x64" ], @@ -7630,9 +7844,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/win32-arm64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.3.tgz", - "integrity": "sha512-B2t59lWWYrbRDw/tjiWOuzSsFh1Y/E95ofKz7rIVYSQkUYBjfSgf6oeYPNWHToFRr2zx52JKApIcAS/D5TUBnA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", "cpu": [ "arm64" ], @@ -7647,9 +7861,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/win32-ia32": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.3.tgz", - "integrity": "sha512-QLKSFeXNS8+tHW7tZpMtjlNb7HKau0QDpwm49u0vUp9y1WOF+PEzkU84y9GqYaAVW8aH8f3GcBck26jh54cX4Q==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", "cpu": [ "ia32" ], @@ -7664,9 +7878,9 @@ } }, "node_modules/wrangler/node_modules/@esbuild/win32-x64": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.3.tgz", - "integrity": "sha512-4uJGhsxuptu3OcpVAzli+/gWusVGwZZHTlS63hh++ehExkVT8SgiEf7/uC/PclrPPkLhZqGgCTjd0VWLo6xMqA==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", "cpu": [ "x64" ], @@ -7681,9 +7895,9 @@ } }, "node_modules/wrangler/node_modules/esbuild": { - "version": "0.27.3", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.3.tgz", - "integrity": "sha512-8VwMnyGCONIs6cWue2IdpHxHnAjzxnw2Zr7MkVxB2vjmQ2ivqGFb4LEG3SMnv0Gb2F/G/2yA8zUaiL1gywDCCg==", + "version": "0.28.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -7694,32 +7908,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.27.3", - "@esbuild/android-arm": "0.27.3", - "@esbuild/android-arm64": "0.27.3", - "@esbuild/android-x64": "0.27.3", - "@esbuild/darwin-arm64": "0.27.3", - "@esbuild/darwin-x64": "0.27.3", - "@esbuild/freebsd-arm64": "0.27.3", - "@esbuild/freebsd-x64": "0.27.3", - "@esbuild/linux-arm": "0.27.3", - "@esbuild/linux-arm64": "0.27.3", - "@esbuild/linux-ia32": "0.27.3", - "@esbuild/linux-loong64": "0.27.3", - "@esbuild/linux-mips64el": "0.27.3", - "@esbuild/linux-ppc64": "0.27.3", - "@esbuild/linux-riscv64": "0.27.3", - "@esbuild/linux-s390x": "0.27.3", - "@esbuild/linux-x64": "0.27.3", - "@esbuild/netbsd-arm64": "0.27.3", - "@esbuild/netbsd-x64": "0.27.3", - "@esbuild/openbsd-arm64": "0.27.3", - "@esbuild/openbsd-x64": "0.27.3", - "@esbuild/openharmony-arm64": "0.27.3", - "@esbuild/sunos-x64": "0.27.3", - "@esbuild/win32-arm64": "0.27.3", - "@esbuild/win32-ia32": "0.27.3", - "@esbuild/win32-x64": "0.27.3" + "@esbuild/aix-ppc64": "0.28.2", + "@esbuild/android-arm": "0.28.2", + "@esbuild/android-arm64": "0.28.2", + "@esbuild/android-x64": "0.28.2", + "@esbuild/darwin-arm64": "0.28.2", + "@esbuild/darwin-x64": "0.28.2", + "@esbuild/freebsd-arm64": "0.28.2", + "@esbuild/freebsd-x64": "0.28.2", + "@esbuild/linux-arm": "0.28.2", + "@esbuild/linux-arm64": "0.28.2", + "@esbuild/linux-ia32": "0.28.2", + "@esbuild/linux-loong64": "0.28.2", + "@esbuild/linux-mips64el": "0.28.2", + "@esbuild/linux-ppc64": "0.28.2", + "@esbuild/linux-riscv64": "0.28.2", + "@esbuild/linux-s390x": "0.28.2", + "@esbuild/linux-x64": "0.28.2", + "@esbuild/netbsd-arm64": "0.28.2", + "@esbuild/netbsd-x64": "0.28.2", + "@esbuild/openbsd-arm64": "0.28.2", + "@esbuild/openbsd-x64": "0.28.2", + "@esbuild/openharmony-arm64": "0.28.2", + "@esbuild/sunos-x64": "0.28.2", + "@esbuild/win32-arm64": "0.28.2", + "@esbuild/win32-ia32": "0.28.2", + "@esbuild/win32-x64": "0.28.2" } }, "node_modules/wrangler/node_modules/path-to-regexp": { @@ -7736,9 +7950,9 @@ "license": "ISC" }, "node_modules/ws": { - "version": "8.18.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", - "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "version": "8.21.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.21.0.tgz", + "integrity": "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==", "dev": true, "license": "MIT", "engines": { @@ -7809,26 +8023,6 @@ "url": "https://opencollective.com/express" } }, - "node_modules/zod": { - "version": "3.24.4", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.4.tgz", - "integrity": "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg==", - "dev": true, - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-to-json-schema": { - "version": "3.24.5", - "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz", - "integrity": "sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==", - "dev": true, - "license": "ISC", - "peerDependencies": { - "zod": "^3.24.1" - } - }, "node_modules/zwitch": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/zwitch/-/zwitch-2.0.4.tgz", @@ -7880,6 +8074,13 @@ "vitest": "^3.0.0", "wrangler": "^4.8.0" } + }, + "services/worker/node_modules/@cloudflare/workers-types": { + "version": "4.20260702.1", + "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20260702.1.tgz", + "integrity": "sha512-mOhf5TUEB1m2vPrxtqoIGfz0fUC9xyxRDx5gWHy5s+OCo6dcV+g7wI1R7gYCMFohhqF/2y2xeKVwMwCJjfn/WA==", + "dev": true, + "license": "MIT OR Apache-2.0" } } } diff --git a/package.json b/package.json index 85ed467..d92d593 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "meterx", - "version": "2027.0.0", + "version": "2027.0.1", "private": true, "description": "MeterX - Open source internet speed test browser extension", "type": "module", From 82d95f02dc08a7c856dd75cb8d2f562b36cdca78 Mon Sep 17 00:00:00 2001 From: ajithakdev Date: Tue, 27 Oct 2026 00:30:32 +0530 Subject: [PATCH 2/5] feat(extension): Appearance setting for light, dark or match system --- CHANGELOG.md | 1 + CLAUDE.md | 2 +- RESOURCES-AND-GUIDE.md | 2 +- apps/extension/src/lib/theme.test.ts | 41 +++++++++++++++++++++++++++ apps/extension/src/lib/theme.ts | 21 ++++++++++++++ apps/extension/src/pages/options.ts | 13 ++++++++- apps/extension/src/pages/popup.ts | 3 ++ apps/extension/src/pages/sidepanel.ts | 5 +++- apps/extension/static/options.html | 11 +++++++ apps/extension/static/styles/app.css | 31 +++++++++++++++++++- docs/STORE-CHECKLIST.md | 2 +- 11 files changed, 126 insertions(+), 6 deletions(-) create mode 100644 apps/extension/src/lib/theme.test.ts create mode 100644 apps/extension/src/lib/theme.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index c02aa20..851fd1f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ All notable changes to MeterX are documented here. The format follows [Keep a Ch - Updated the PDF export's libraries (DOMPurify 3.4.17, fflate 0.8.3) and the self-host server's Express dependencies (body-parser 2.4.0 and others) to versions without known security advisories ### Added +- Appearance setting: Match system, Light or Dark. Firefox gives extension pages the system's light or dark setting rather than Firefox's own theme, so the popup and sidebar could disagree with the browser; now you can pick - `docs/STORE-CHECKLIST.md`: what to check before each store upload, built from the Firefox, Edge, Opera and Chrome store policies ## [2027.0.0] - 2026-10-27 diff --git a/CLAUDE.md b/CLAUDE.md index e18c0e4..067f97b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -52,7 +52,7 @@ scripts/ package-stores.mjs for store uploads **Measurement**: 4 download and 3 upload streams for a fixed time, ramp-up and tail trimmed, loaded-latency pings for the bufferbloat grade. The rate limits in the worker and server are sized for this burst; raise them if you add streams. -**Storage**: `chrome.storage.sync` holds settings (`serverUrl`, `ispPlan`, `scheduleMinutes`, `notifyOnDrop`); `chrome.storage.local` holds `history` (capped at 200). Scheduled tests use `chrome.alarms`. +**Storage**: `chrome.storage.sync` holds settings (`serverUrl`, `ispPlan`, `scheduleMinutes`, `notifyOnDrop`, `theme`); `chrome.storage.local` holds `history` (capped at 200). Scheduled tests use `chrome.alarms`. Custom server URLs need a runtime host-permission grant (`optional_host_permissions`); the settings page requests it. diff --git a/RESOURCES-AND-GUIDE.md b/RESOURCES-AND-GUIDE.md index ec1eb72..932fbc3 100644 --- a/RESOURCES-AND-GUIDE.md +++ b/RESOURCES-AND-GUIDE.md @@ -50,7 +50,7 @@ Every link for MeterX, what it is for, and how to run, package and release it. P 1. Install MeterX from your browser's store (https://meterx.ajithakdev.com/get/). 2. Click the MeterX icon in the toolbar and press Run test. It takes about twenty seconds. 3. Open the side panel (the panel icon in the popup) to see your history, trend and plan comparison. -4. In settings (the sliders icon), enter your ISP plan, choose a test schedule, or point MeterX at your own server. +4. In settings (the sliders icon), enter your ISP plan, choose a test schedule, pick Light, Dark or Match system under Appearance, or point MeterX at your own server. 5. Export or Share from the popup or side panel to save a PDF, CSV or JSON, or send someone a link. ## Admin guide diff --git a/apps/extension/src/lib/theme.test.ts b/apps/extension/src/lib/theme.test.ts new file mode 100644 index 0000000..b019bfe --- /dev/null +++ b/apps/extension/src/lib/theme.test.ts @@ -0,0 +1,41 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { applyTheme } from './theme'; + +function stub(stored: Record) { + const root = { dataset: {} as Record }; + const listeners: ((c: Record, area: string) => void)[] = []; + vi.stubGlobal('document', { documentElement: root }); + vi.stubGlobal('chrome', { + storage: { sync: { get: async () => stored }, onChanged: { addListener: (f: (typeof listeners)[number]) => listeners.push(f) } }, + }); + return { root, listeners }; +} + +describe('applyTheme', () => { + beforeEach(() => vi.unstubAllGlobals()); + + it.each(['light', 'dark'])('sets data-theme to %s', async (theme) => { + const { root } = stub({ theme }); + await applyTheme(); + expect(root.dataset.theme).toBe(theme); + }); + + it('leaves the system setting in charge when unset or unknown', async () => { + for (const stored of [{}, { theme: 'purple' }]) { + const { root } = stub(stored); + await applyTheme(); + expect(root.dataset.theme).toBeUndefined(); + } + }); + + it('follows later changes in sync storage only', async () => { + const { root, listeners } = stub({}); + await applyTheme(); + listeners[0]({ theme: { newValue: 'dark' } }, 'local'); + expect(root.dataset.theme).toBeUndefined(); + listeners[0]({ theme: { newValue: 'dark' } }, 'sync'); + expect(root.dataset.theme).toBe('dark'); + listeners[0]({ theme: { oldValue: 'dark' } }, 'sync'); + expect(root.dataset.theme).toBeUndefined(); + }); +}); diff --git a/apps/extension/src/lib/theme.ts b/apps/extension/src/lib/theme.ts new file mode 100644 index 0000000..df4ea08 --- /dev/null +++ b/apps/extension/src/lib/theme.ts @@ -0,0 +1,21 @@ +/// + +/** + * Theme override from settings. Unset follows prefers-color-scheme, which some browsers + * (Firefox among them) take from the OS rather than the browser's own theme. + */ +export const THEME_KEY = 'theme'; +export type Theme = 'system' | 'light' | 'dark'; + +function setTheme(theme: unknown): void { + if (theme === 'light' || theme === 'dark') document.documentElement.dataset.theme = theme; + else delete document.documentElement.dataset.theme; +} + +/** Applies the saved theme now and whenever it changes. Pages with charts redraw on that change themselves. */ +export async function applyTheme(): Promise { + chrome.storage.onChanged.addListener((changes, area) => { + if (area === 'sync' && THEME_KEY in changes) setTheme(changes[THEME_KEY].newValue); + }); + setTheme((await chrome.storage.sync.get(THEME_KEY))[THEME_KEY]); +} diff --git a/apps/extension/src/pages/options.ts b/apps/extension/src/pages/options.ts index eddc293..b6514b3 100644 --- a/apps/extension/src/pages/options.ts +++ b/apps/extension/src/pages/options.ts @@ -7,6 +7,9 @@ /// import { VERSION, WORKER_URL, type IspPlan } from '@meterx/shared'; +import { THEME_KEY, applyTheme, type Theme } from '../lib/theme'; + +void applyTheme(); const DEFAULT_URL = WORKER_URL; const ISP_KEY = 'ispPlan'; @@ -45,12 +48,13 @@ document.addEventListener('DOMContentLoaded', async () => { const ispClearBtn = document.getElementById('ispClearBtn') as HTMLButtonElement | null; const scheduleRadios = document.querySelectorAll('input[name="schedule"]'); const notifyCheckbox = document.getElementById('notifyOnDrop') as HTMLInputElement | null; + const themeRadios = document.querySelectorAll('input[name="theme"]'); // Close button closeBtn.addEventListener('click', () => window.close()); // Load ISP plan + schedule prefs - const stored = await chrome.storage.sync.get([ISP_KEY, SCHEDULE_KEY, NOTIFY_KEY]); + const stored = await chrome.storage.sync.get([ISP_KEY, SCHEDULE_KEY, NOTIFY_KEY, THEME_KEY]); const plan = stored[ISP_KEY] as IspPlan | undefined; if (plan && ispDown && ispUp && ispProvider) { ispDown.value = String(plan.downloadMbps); @@ -60,6 +64,8 @@ document.addEventListener('DOMContentLoaded', async () => { const scheduleMinutes: number = stored[SCHEDULE_KEY] ?? 0; scheduleRadios.forEach(r => { r.checked = Number(r.value) === scheduleMinutes || (r.value === 'off' && scheduleMinutes === 0); }); if (notifyCheckbox) notifyCheckbox.checked = !!stored[NOTIFY_KEY]; + const theme: Theme = stored[THEME_KEY] ?? 'system'; + themeRadios.forEach(r => { r.checked = r.value === theme; }); if (ispClearBtn) { ispClearBtn.addEventListener('click', async () => { @@ -253,6 +259,11 @@ document.addEventListener('DOMContentLoaded', async () => { await chrome.storage.sync.set({ [SCHEDULE_KEY]: minutes, [NOTIFY_KEY]: !!notifyCheckbox?.checked }); chrome.runtime.sendMessage({ action: 'rescheduleTests' }); + // Open MeterX pages pick the change up through storage.onChanged + const pickedTheme = Array.from(themeRadios).find(r => r.checked)?.value ?? 'system'; + if (pickedTheme === 'system') await chrome.storage.sync.remove(THEME_KEY); + else await chrome.storage.sync.set({ [THEME_KEY]: pickedTheme }); + // Re-check status with new URL checkStatus(); diff --git a/apps/extension/src/pages/popup.ts b/apps/extension/src/pages/popup.ts index 2bd9777..7914efc 100644 --- a/apps/extension/src/pages/popup.ts +++ b/apps/extension/src/pages/popup.ts @@ -14,9 +14,12 @@ import { exportPdfReport } from '../lib/export-pdf'; import { NONE, fmt, fmtSpeed, getTimeAgo } from '../lib/format'; import { loadHistory } from '../lib/history'; import { loadIspPlan } from '../lib/isp'; +import { applyTheme } from '../lib/theme'; import { bindTooltips } from '../lib/tooltips'; import { MARK_SVG, animatePhase, bindMenu, findingFor, renderLoad, renderReady, scopeLine, setFigure, setQualityPill } from '../lib/ui'; +void applyTheme(); + const $ = (id: string) => document.getElementById(id) as T; const els = { diff --git a/apps/extension/src/pages/sidepanel.ts b/apps/extension/src/pages/sidepanel.ts index 7e38db6..ebe3f38 100644 --- a/apps/extension/src/pages/sidepanel.ts +++ b/apps/extension/src/pages/sidepanel.ts @@ -13,10 +13,13 @@ import { NONE, fmt, fmtSpeed, fmtWhen } from '../lib/format'; import { loadHistory } from '../lib/history'; import { loadIspPlan, planShare } from '../lib/isp'; import { qualityOf } from '../lib/quality'; +import { applyTheme } from '../lib/theme'; import { bindTooltips } from '../lib/tooltips'; import { MARK_SVG, animatePhase, bindMenu, scopeLine, setFigure } from '../lib/ui'; import { median } from '../background/measure'; +void applyTheme(); + interface ChartsApi { line(el: HTMLElement, opts: Record): { legend: HTMLElement | null }; fmt: { dec(d: number): (v: number) => string }; @@ -238,6 +241,6 @@ document.addEventListener('DOMContentLoaded', async () => { let resizing: number | undefined; window.addEventListener('resize', () => { clearTimeout(resizing); resizing = window.setTimeout(refresh, 150); }); chrome.storage.onChanged.addListener((changes, area) => { - if ((area === 'local' && 'history' in changes) || (area === 'sync' && ('ispPlan' in changes || 'serverUrl' in changes))) refresh(); + if ((area === 'local' && 'history' in changes) || (area === 'sync' && ('ispPlan' in changes || 'serverUrl' in changes || 'theme' in changes))) refresh(); }); }); diff --git a/apps/extension/static/options.html b/apps/extension/static/options.html index c4b5103..3412382 100644 --- a/apps/extension/static/options.html +++ b/apps/extension/static/options.html @@ -97,6 +97,17 @@

MeterX settings

+ +
+

Appearance

+
+
+ + + +
+
+
diff --git a/apps/extension/static/styles/app.css b/apps/extension/static/styles/app.css index d281ce3..03ef399 100644 --- a/apps/extension/static/styles/app.css +++ b/apps/extension/static/styles/app.css @@ -58,8 +58,9 @@ --ease: cubic-bezier(.2, .7, .2, 1); } +/* Dark tokens appear twice: once for the system setting, once for the override in settings (src/lib/theme.ts). Keep them in sync. */ @media (prefers-color-scheme: dark) { - :root { + :root:not([data-theme="light"]) { --ground: #0b0c0e; --surface: #151618; --raised: #1f2124; @@ -86,6 +87,34 @@ --axis-ink: #a4a8ae; } } +:root[data-theme="dark"] { + --ground: #0b0c0e; + --surface: #151618; + --raised: #1f2124; + --border: #26292d; + --rule: #1f2124; + --control: #3a3e44; + --text: #f4f5f6; + --muted: #a4a8ae; + --data: #74a6ff; + --data-track: #1b2a44; + --primary: #f4f5f6; + --on-primary: #0b0c0e; + --positive: #4cc38a; + --negative: #f0717a; + --warning: #f0b45a; + --pill-positive-bg: #12291d; --pill-positive-ink: #6fdca5; + --pill-negative-bg: #2e1517; --pill-negative-ink: #ff9aa1; + --pill-warning-bg: #2d2210; --pill-warning-ink: #f4c27a; + --pill-neutral-bg: #1f2124; --pill-neutral-ink: #c9ccd1; + --focus: #74a6ff; + --chart-2: #8d9198; + --chart-muted: #8d9198; + --grid: #26292d; + --axis-ink: #a4a8ae; +} +:root[data-theme="light"] { color-scheme: light; } +:root[data-theme="dark"] { color-scheme: dark; } *, *::before, *::after { box-sizing: border-box; } html { -webkit-text-size-adjust: 100%; } diff --git a/docs/STORE-CHECKLIST.md b/docs/STORE-CHECKLIST.md index 5dca47e..ec755f2 100644 --- a/docs/STORE-CHECKLIST.md +++ b/docs/STORE-CHECKLIST.md @@ -41,7 +41,7 @@ In each browser: - [ ] Custom server `http://localhost:8787` (with `npm run dev:ext` running) asks for permission, then a test runs against it. - [ ] A scheduled test runs, and a drop in quality shows a notification. Turning notifications off in the OS does not break anything (Edge policy 1.9). - [ ] The extension console (Firefox: Inspect on `about:debugging`; Chromium: "service worker" link) shows no red errors. -- [ ] Light and dark theme both look right, and the popup has no horizontal scroll (Opera checks this). +- [ ] Appearance: Light, Dark and Match system each apply to the popup, side panel and settings without reopening them, and the popup has no horizontal scroll (Opera checks this). ## 3. Rules every store shares From 1d964cd6ea800092fbd3d37f64383537d912c76a Mon Sep 17 00:00:00 2001 From: ajithakdev Date: Tue, 27 Oct 2026 01:14:47 +0530 Subject: [PATCH 3/5] docs: README, CONTRIBUTING and SECURITY to the repo docs template, issue forms, docs check --- .github/ISSUE_TEMPLATE/bug_report.md | 29 ----- .github/ISSUE_TEMPLATE/bug_report.yml | 37 ++++++ .github/ISSUE_TEMPLATE/config.yml | 8 ++ .github/ISSUE_TEMPLATE/feature_request.md | 15 --- .github/ISSUE_TEMPLATE/feature_request.yml | 15 +++ CHANGELOG.md | 4 + CLAUDE.md | 2 +- CONTRIBUTING.md | 106 ++++++---------- README.md | 139 ++++++++++++--------- RESOURCES-AND-GUIDE.md | 4 + SECURITY.md | 18 +++ TRADEMARK.md | 2 +- apps/extension/src/lib/quality.ts | 2 +- docs/CODEMAP.md | 2 +- docs/KNOWN-ISSUES.md | 4 +- docs/SITE-PLAN.md | 2 +- docs/STORE-CHECKLIST.md | 2 +- package.json | 11 +- scripts/guidelines.sh | 74 +++++++++++ 19 files changed, 294 insertions(+), 182 deletions(-) delete mode 100644 .github/ISSUE_TEMPLATE/bug_report.md create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml delete mode 100644 .github/ISSUE_TEMPLATE/feature_request.md create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 SECURITY.md create mode 100755 scripts/guidelines.sh diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md deleted file mode 100644 index dbb34ba..0000000 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -name: Bug Report -about: Report a bug in MeterX -title: "[Bug] " -labels: bug ---- - -**Browser & Version:** - - -**Extension Version:** - - -**Steps to Reproduce:** -1. -2. -3. - -**Expected Behavior:** - - -**Actual Behavior:** - - -**Console Errors (if any):** - -``` - -``` diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..4d12f75 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,37 @@ +name: Bug report +description: Something in MeterX doesn't work or shows the wrong result +labels: [bug] +body: + - type: dropdown + id: browser + attributes: + label: Browser + options: [Firefox, Microsoft Edge, Opera, Chrome (unpacked), Brave or Vivaldi (unpacked), Other] + validations: + required: true + - type: input + id: versions + attributes: + label: Browser and MeterX versions + description: MeterX's version is at the bottom of its settings page. + placeholder: Firefox 158.0.1, MeterX 2027.0.1 + validations: + required: true + - type: dropdown + id: server + attributes: + label: Test server + options: [Default (Cloudflare), Custom server] + - type: textarea + id: steps + attributes: + label: What you did and what happened + description: Steps to reproduce, what you expected, and what you saw. A screenshot helps. + validations: + required: true + - type: textarea + id: console + attributes: + label: Console errors + description: "Firefox: about:debugging, This Firefox, Inspect next to MeterX. Chromium: the extensions page, service worker link." + render: text diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..d76849d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,8 @@ +blank_issues_enabled: false +contact_links: + - name: Question or idea to discuss + url: https://github.com/ajithakdev/meterx/discussions + about: Ask how something works or float an idea before opening an issue. + - name: Security problem + url: https://github.com/ajithakdev/meterx/security/advisories/new + about: Report it privately, not in a public issue. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md deleted file mode 100644 index ec24774..0000000 --- a/.github/ISSUE_TEMPLATE/feature_request.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -name: Feature Request -about: Suggest a feature for MeterX -title: "[Feature] " -labels: enhancement ---- - -**Description:** - - -**Use Case:** - - -**Proposed Solution (optional):** - diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..24a188e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,15 @@ +name: Feature request +description: Suggest something MeterX should measure, show or do +labels: [enhancement] +body: + - type: textarea + id: problem + attributes: + label: What are you trying to do? + description: The problem first; a solution is welcome but optional. + validations: + required: true + - type: textarea + id: idea + attributes: + label: Your idea diff --git a/CHANGELOG.md b/CHANGELOG.md index 851fd1f..d9fe402 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,10 @@ All notable changes to MeterX are documented here. The format follows [Keep a Ch ### Added - Appearance setting: Match system, Light or Dark. Firefox gives extension pages the system's light or dark setting rather than Firefox's own theme, so the popup and sidebar could disagree with the browser; now you can pick - `docs/STORE-CHECKLIST.md`: what to check before each store upload, built from the Firefox, Edge, Opera and Chrome store policies +- `SECURITY.md` with what MeterX trusts and how to report privately, issue forms for bugs and features, and `npm run check:docs`, which checks the docs' sections, links, spelling and headings as part of `npm test` + +### Changed +- README rewritten around install, privacy and data, how a test works and a FAQ; CONTRIBUTING covers loading the extension in Firefox and the pull request rules ## [2027.0.0] - 2026-10-27 diff --git a/CLAUDE.md b/CLAUDE.md index 067f97b..f1fcf9a 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -45,7 +45,7 @@ scripts/ package-stores.mjs for store uploads ``` - **Extension** (`apps/extension`): esbuild bundles `src/background/index.ts` (service worker; measurement in `background/runner.ts` and `measure.ts`) and `src/pages/{popup,sidepanel,options}.ts`. Pages are thin: they send `chrome.runtime` messages (`startTest`, `cancelTest`, `getState`, `rescheduleTests`) and listen for `testProgress` / `testComplete` / `testError`. Browser-only helpers live in `src/lib/` (history, isp, export downloads, ui, tooltips, browser). -- **Site** (`apps/site`): `build.mjs` copies `static/`, fills the `` and `` partials, and copies the built extension pages into `dist/app/` with `src/demo-shim.ts` injected before their scripts. The shim stands in for the extension APIs with labelled sample data (`src/sample.ts`); the home page drives the popup's sample test from scroll through `postMessage`. The scroll engine is `static/sc/` (scroll-craft, unmodified). +- **Site** (`apps/site`): `build.mjs` copies `static/`, fills the `` and `` partials, and copies the built extension pages into `dist/app/` with `src/demo-shim.ts` injected before their scripts. The shim stands in for the extension APIs with labeled sample data (`src/sample.ts`); the home page drives the popup's sample test from scroll through `postMessage`. The scroll engine is `static/sc/` (scroll-craft, unmodified). - **Worker and server**: same API, kept in sync. CORS answers extension origins, the site origin and localhost dev ports (`isAllowedOrigin` in shared). **Server API contract**: `GET/HEAD /ping`, `GET /test-file/{1|5|10|25}MB.bin`, `POST /upload` (`application/octet-stream`, body discarded), `GET /health`, `GET /ip`. Sizes come from `ALLOWED_SIZES_MB` / `TEST_FILE_RE` in shared. Responses send `Timing-Allow-Origin` so the extension can read DNS timing. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b3e1ff0..181ba98 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,90 +1,66 @@ # Contributing to MeterX -Here's how to get set up and contribute. +Bug reports, test reports from other browsers and fixes are all welcome. Start with a [good first issue](https://github.com/ajithakdev/meterx/labels/good%20first%20issue), or ask in [Discussions](https://github.com/ajithakdev/meterx/discussions) before a large change. -## Dev Setup +## Setup + +You need Node 22 or newer and npm. ```bash git clone https://github.com/ajithakdev/meterx.git cd meterx npm install -npm run build -``` - -## Load the Extension - -1. Go to `chrome://extensions` (or `edge://extensions`) -2. Enable "Developer mode" -3. Click "Load unpacked" -4. Select `apps/extension/dist/` - -Rebuild with `npm run build` after changes, then hit reload on the extensions page. - -## Run the Worker Locally - -```bash -cd services/worker -npx wrangler dev +npm run dev:ext # builds the extension, runs a local test server on http://localhost:8787 ``` -This starts a local Cloudflare Worker on `http://localhost:8787`. Update the server URL in the extension settings to point here. +`npm run dev:site` builds and serves the website on http://localhost:8788 instead. -## Run the Server Locally +## Load the extension -```bash -npm start -``` +- Chromium (Chrome, Edge, Opera, Brave): open `chrome://extensions`, turn on Developer mode, choose Load unpacked and pick `apps/extension/dist/`. +- Firefox: run `npm run package`, then open `about:debugging`, choose This Firefox, Load Temporary Add-on, and pick `release/meterx--firefox.zip`. `dist/` has the Chromium manifest, so Firefox needs the packaged zip. -Starts the Express.js self-host server. Useful for LAN testing or when you don't want to touch the worker. +To test against your local server, set Custom server to `http://localhost:8787` in MeterX settings. After a change, run `npm run build -w @meterx/extension` and reload the extension. -## Project Structure +## Project layout ```text -apps/extension Browser extension (TypeScript, Manifest V3, esbuild) +apps/extension the extension (TypeScript, Manifest V3, esbuild) apps/site meterx.ajithakdev.com services/worker Cloudflare Worker, the default test server -services/server Express server, for self-hosting -packages/shared Types, API constants and rules every part shares +services/server Express server with the same API, for self-hosting +packages/shared types, API constants and rules every part shares packages/reports PDF, CSV and JSON builders ``` -The full map is in [docs/CODEMAP.md](docs/CODEMAP.md). +Every file is listed in [docs/CODEMAP.md](docs/CODEMAP.md). The worker and the server share one API; change both together. ## Commands | Command | What it does | -|---------|-------------| -| `npm install` | Install all workspace deps | -| `npm run build` | Build all packages | -| `npm start` | Run self-host server | -| `npm test` | Run all tests | -| `npm run lint` | ESLint check | -| `npm run lint:fix` | Auto-fix lint issues | -| `npm -w @meterx/server run test` | Run server tests only | -| `cd services/worker && npx wrangler dev` | Run worker locally | -| `cd services/worker && npx wrangler deploy` | Deploy worker to Cloudflare | - -## Code Style - -- TypeScript everywhere, no plain JS -- ESLint with strict rules, run `npm run lint` before committing -- Prefix unused parameters with `_` -- Keep functions short and focused -- Throw errors, don't return sentinel values -- No `any` unless absolutely necessary - -## Pull Requests - -1. Fork the repo, branch from `main` -2. Make your changes -3. Run `npm run lint && npm run build && npm test`: all must pass -4. Open a PR with a clear description of what changed and why -5. One approval required to merge - -## Reporting Bugs - -Open an issue with: -- Browser and version -- Steps to reproduce -- Expected vs actual behavior -- Console errors if applicable +| --- | --- | +| `npm run build` | Build the extension, site and server | +| `npm test` | Every workspace's tests, the scripts' tests and the docs check | +| `npm run lint` | ESLint for TypeScript, CSS and Markdown | +| `npm run check:docs` | Docs rules only: sections, links, spelling, headings | +| `npm run package` | Store zips in `release/` | +| `npm start` | Self-host server on port 3000 (after a build) | + +## Code style + +- TypeScript, strict. No `any` unless there is no other way. +- Keep pages thin: they send `chrome.runtime` messages, and measurement stays in `apps/extension/src/background/`. +- New logic gets a test next to it (`*.test.ts`). +- Prefix unused parameters with `_`. + +## Pull requests + +1. Branch from `main`. +2. Run `npm run lint && npm run build && npm test`. All three must pass. +3. Write commit messages with a conventional prefix (`feat:`, `fix:`, `docs:`, `chore:`, `refactor:`). +4. Add a line under `## [Unreleased]` in [CHANGELOG.md](CHANGELOG.md) for anything a user would notice. +5. Open the pull request with what changed and why, and how you tested it. Include a screenshot for UI changes. + +## Reporting bugs + +Use the [bug report form](https://github.com/ajithakdev/meterx/issues/new?template=bug_report.yml). It asks for your browser, versions and any console errors. Security problems go through [SECURITY.md](SECURITY.md). diff --git a/README.md b/README.md index 23e91f5..bb1eec9 100644 --- a/README.md +++ b/README.md @@ -1,86 +1,101 @@ -

- MeterX -

+
-

- Your internet, honestly measured.
- A free, open-source speed test that lives in your browser toolbar. -

+MeterX -

- Website · - Install · - Links and how-tos · - Contribute -

+**A free, open-source speed test in your browser toolbar that also tells you what the numbers mean.** + +[![CI](https://github.com/ajithakdev/meterx/actions/workflows/ci.yml/badge.svg)](https://github.com/ajithakdev/meterx/actions/workflows/ci.yml) +[![Release](https://img.shields.io/github/v/release/ajithakdev/meterx)](https://github.com/ajithakdev/meterx/releases/latest) +[![Firefox Add-ons](https://img.shields.io/amo/v/meterx-by-ajithakdev?label=Firefox)](https://addons.mozilla.org/firefox/addon/meterx-by-ajithakdev/) +[![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](LICENSE) ---- +[Install](#install) · [Privacy](#privacy-and-data) · [How it works](#how-it-works) · [FAQ](#faq) · [Website](https://meterx.ajithakdev.com) · [Guide](RESOURCES-AND-GUIDE.md) -Speed tests give you a number. MeterX tells you what it means. +
-Click the icon, wait about twenty seconds, and you get download, upload, ping, jitter, packet loss and the one most tests skip: **latency under load**. That is why your video call stutters while someone else downloads a game, and MeterX grades it from A to F. +Most speed tests stop at a download number. MeterX also measures ping while your line is busy (bufferbloat, graded A to F) and says in plain words whether the connection is good enough for calls, 4K streaming, gaming and big uploads. That busy-line ping is usually why a video call stutters while someone else downloads a game. -No account. No tracking. Your results never leave your browser unless you share them. +MeterX is free, with no account, no ads and no tracking. Your results never leave your browser unless you share them. It is a community project and is not affiliated with Cloudflare, Mozilla, Microsoft, Opera or Google. + +

+ MeterX popup with a sample result: 280 Mbps download, 91.1 Mbps upload, 18 ms ping, grade B latency under load, and Smooth for video calls and gaming +

## Install -| Browser | Where | +| Browser | Get it | | --- | --- | | Firefox | [Firefox Add-ons](https://addons.mozilla.org/firefox/addon/meterx-by-ajithakdev/) | | Microsoft Edge | [Edge Add-ons](https://microsoftedge.microsoft.com/addons/detail/glkpolbkpachomgmmhdkmjnghejbhlfc) | | Opera | Listing link coming soon | -| Chrome, Brave, Vivaldi | Planned | - -Not sure which? [meterx.ajithakdev.com/get](https://meterx.ajithakdev.com/get/) picks the right store for you. - -## What you get - -- **A real test, fast.** Four download and three upload streams for a fixed time, with the slow start trimmed off. -- **Bufferbloat, graded.** Ping measured while the line is busy, A to F. -- **A plain answer.** Calls, 4K streaming, gaming, big uploads: MeterX says in words what your connection can handle. -- **Memory.** Up to 200 results in the side panel, a trend chart, and how much of your ISP plan actually arrives. -- **Tests on a timer.** Every 30 minutes, 2 hours or daily, with a notice when quality drops. -- **Proof you can hand over.** A PDF report with a report ID that changes if the results are edited, or CSV and JSON with units in every column. -- **Share links with no server.** The whole result rides inside the link. -- **Your own server, if you like.** Tests run against Cloudflare's edge by default, or against one you host. - -## Under the hood - -```text -apps/extension the extension: popup, side panel, settings, service worker -apps/site meterx.ajithakdev.com, running the real extension pages on sample data -services/worker Cloudflare Worker test server (streams bytes, stores nothing) -services/server Express server with the same API, for self-hosting -packages/shared the contract: types, API, version, quality rules, share links -packages/reports PDF, CSV and JSON builders -brand/ logo and the script that makes every icon and store image -scripts/ store packaging, local dev runner, code map -``` +| Chrome, Brave, Vivaldi | Planned; see the [FAQ](#faq) | + +[meterx.ajithakdev.com/get](https://meterx.ajithakdev.com/get/) picks the right store for your browser. To check that it works, click the MeterX icon in the toolbar and press **Run test**; a result appears in about 20 seconds. + +## Features + +- Download, upload, ping, jitter and packet loss, with 4 download and 3 upload streams so one slow connection can't hide your real speed. +- Latency under load, graded A to F, plus a plain answer for calls, streaming, gaming and uploads. +- History of up to 200 results in the side panel, with a trend chart and how much of your ISP plan actually arrives. +- Scheduled tests every 30 minutes, 2 hours or day, with an optional notice when quality drops to Poor. +- A PDF report with a report ID that stops matching if someone edits the numbers, plus CSV and JSON with units in every column. +- Share links that carry the whole result inside the link. +- Light, dark or match-system appearance, and your own test server if you'd rather not use the default. -The deep dive is in [CLAUDE.md](CLAUDE.md) and [docs/CODEMAP.md](docs/CODEMAP.md). +## Privacy and data -## Hack on it +| Area | What happens | +| --- | --- | +| Storage | Settings sit in your browser's sync storage and history in local storage (200 results at most). | +| Network | Tests talk only to the test server: by default a Cloudflare Worker that streams bytes and stores nothing. Like any server, it sees your IP address; it uses Cloudflare's request data to label results with your ISP and nearest location. | +| Permissions | `storage`, `alarms` (scheduled tests), `notifications` (quality notice), `sidePanel`, and access to the default server only. A custom server asks for permission when you add it. | +| Telemetry | None. No analytics, no account, no ads. | +| Sharing | A share link puts the result after the `#` in the URL, and browsers never send that part to a server. The page at meterx.ajithakdev.com/r/ reads it in your browser. | +| Checked | Every store upload goes through [docs/STORE-CHECKLIST.md](docs/STORE-CHECKLIST.md): Mozilla's linter, a reproducible build and a manual run in Firefox and Chromium. | + +The full policy is at [meterx.ajithakdev.com/privacy](https://meterx.ajithakdev.com/privacy/). + +## How it works + +1. Ten quick pings to the server measure idle latency, jitter and packet loss. +2. Four parallel streams download for 8 seconds. The first 1.2 seconds (TCP ramp-up) and the last half second are trimmed, so the number is your steady speed. +3. Three parallel streams upload for 8 seconds, trimmed the same way. +4. While each transfer runs, MeterX keeps pinging. The rise over idle latency sets the bufferbloat grade. +5. The result is saved in your browser, and the toolbar badge shows the download speed in its quality color. + +## FAQ + +**Why isn't it on the Chrome Web Store?** +Chrome charges a developer registration fee and MeterX earns nothing, so it waits for now. Brave, Vivaldi and Chrome can load the Chromium zip from the [latest release](https://github.com/ajithakdev/meterx/releases/latest) as an unpacked extension. + +**Why is my result different from another speed test?** +Each test uses its own servers and method. MeterX tests against the nearest Cloudflare location and trims the slow start, so it can read higher than tests that average the whole run, and lower than tests that report a peak. + +**The popup's colors don't match my browser's theme.** +Some browsers, Firefox among them, tell extensions the system's light or dark setting instead of the browser's own. Pick Light or Dark under Appearance in MeterX settings. + +**The popup looks bigger in one browser than another.** +The popup is 360 pixels wide in every browser. Each browser scales pixels to your screen in its own way, so it matches the rest of that browser. + +**Can I use my own server?** +Yes. Run `npm start` from this repo (Express, port 3000) or deploy `services/worker` to your own Cloudflare account, then add it under Custom server in settings. Remote servers need https; plain http works for localhost and `.local` names. + +More fixes are in the [guide](RESOURCES-AND-GUIDE.md#when-something-goes-wrong). + +## Contributing ```bash -git clone https://github.com/ajithakdev/meterx.git -cd meterx -npm install -npm run dev:ext # build the extension, run a local test server on :8787 -npm run dev:site # build and serve the site on :8788 -npm test && npm run lint -npm run package # store uploads in release/ +git clone https://github.com/ajithakdev/meterx.git && cd meterx && npm install +npm run dev:ext # build the extension, run a local test server on :8787 +npm run lint && npm test # what CI runs, plus the docs check ``` -To load it, open `chrome://extensions`, turn on Developer mode, choose Load unpacked and pick `apps/extension/dist/`. In MeterX settings, set Custom server to `http://localhost:8787` to test against your local worker. After a change, run `npm run build -w @meterx/extension` and reload. - -To self-host the test server, run `npm start` (port 3000) or deploy `services/worker` to your own Cloudflare account. +[CONTRIBUTING.md](CONTRIBUTING.md) covers loading the extension, the project layout and the rules a pull request follows. There are [good first issues](https://github.com/ajithakdev/meterx/labels/good%20first%20issue), and questions go to [Discussions](https://github.com/ajithakdev/meterx/discussions). Report security problems privately as [SECURITY.md](SECURITY.md) describes. -## Privacy +## Credits -MeterX collects no personal data. Results stay in your browser and the default test server keeps no logs. Full policy: [meterx.ajithakdev.com/privacy](https://meterx.ajithakdev.com/privacy/). +Made and maintained by Ajith Kumar K ([@ajithakdev](https://github.com/ajithakdev)). The Geist font is by Vercel under the SIL Open Font License. ## License -[AGPL-3.0](LICENSE). The name and logo have their own rules in [TRADEMARK.md](TRADEMARK.md). - -Made by Ajith Kumar K ([@ajithakdev](https://github.com/ajithakdev)). +[AGPL-3.0](LICENSE). The MeterX name and logo have their own rules in [TRADEMARK.md](TRADEMARK.md). Firefox, Edge, Opera, Chrome and Cloudflare are trademarks of their owners. diff --git a/RESOURCES-AND-GUIDE.md b/RESOURCES-AND-GUIDE.md index 932fbc3..c92470a 100644 --- a/RESOURCES-AND-GUIDE.md +++ b/RESOURCES-AND-GUIDE.md @@ -84,6 +84,10 @@ Every link for MeterX, what it is for, and how to run, package and release it. P | Firefox upload rejected for a missing id | Upload the `-firefox.zip` from `npm run package`, not the Chromium zip | | Site build fails with "Build the extension first" | Run `npm run build` from the root, or build `@meterx/extension` before `@meterx/site` | | The demo pages on the site look out of date | Rebuild: the site copies the current extension build into `/app/` | +| Popup or side panel colors don't match the browser's theme | Firefox gives extensions the system's light or dark setting. Pick Light or Dark under Appearance in MeterX settings | +| Popup looks bigger in Firefox than in Chromium | Not a bug: each browser scales CSS pixels its own way (check `devicePixelRatio` in the popup's console). On GNOME, setting Chromium's "Preferred Ozone platform" flag to Wayland often fixes its scale | +| Firefox won't load `apps/extension/dist/` | That folder has the Chromium manifest. Run `npm run package` and load `release/meterx--firefox.zip` on `about:debugging` | +| Someone reports a security problem | Point them to SECURITY.md (private reporting is on under the repo's Security tab) | ## More detail diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..5c3cd39 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,18 @@ +# Security policy + +## Supported versions + +Only the newest release gets fixes: the version live in the Firefox, Edge and Opera stores, and the newest tag on [Releases](https://github.com/ajithakdev/meterx/releases). Security fixes ship as a same-day patch release. + +## What MeterX trusts + +- The extension runs only code from its own package. Its content security policy is `script-src 'self'`, and it loads nothing from the web to run. +- It talks only to the test server: the default Cloudflare Worker, or a server you add in settings. Remote servers must use https; plain http is allowed only for localhost, 127.0.0.1 and `.local` names, and each custom server needs a permission you grant. +- The test server reads nothing it is sent: uploads are discarded, and downloads are generated bytes. It keeps no logs and stores no results. +- Share links carry the result in the URL fragment, which browsers never send to a server. + +A report is in scope if it breaks one of these, for example code running from outside the package, data reaching a server it shouldn't, or the worker or the self-host server storing or leaking anything. + +## Reporting a problem + +Please don't open a public issue. Use [Report a vulnerability](https://github.com/ajithakdev/meterx/security/advisories/new) under the Security tab, or email hello@ajithakdev.com with what you found and how to reproduce it. You'll get a reply within a few days, and a fix or a plan before anything is made public. diff --git a/TRADEMARK.md b/TRADEMARK.md index 2ef311c..1fa8ba7 100644 --- a/TRADEMARK.md +++ b/TRADEMARK.md @@ -1,4 +1,4 @@ -# MeterX Trademark Policy +# MeterX trademark policy ## Ownership diff --git a/apps/extension/src/lib/quality.ts b/apps/extension/src/lib/quality.ts index 1deaab4..b6c39d7 100644 --- a/apps/extension/src/lib/quality.ts +++ b/apps/extension/src/lib/quality.ts @@ -1,5 +1,5 @@ /** - * Quality rules live in @meterx/shared so the site uses the same ones; this file adds the toolbar badge colours. + * Quality rules live in @meterx/shared so the site uses the same ones; this file adds the toolbar badge colors. * @license AGPL-3.0 */ diff --git a/docs/CODEMAP.md b/docs/CODEMAP.md index 7bf9fee..f563234 100644 --- a/docs/CODEMAP.md +++ b/docs/CODEMAP.md @@ -45,7 +45,7 @@ Generated by `node scripts/codemap.mjs`; regenerate after moving or adding files | `apps/extension/src/lib/format.ts` (7) | - | Formatting lives in @meterx/shared so the reports and the site use the same rules. | | `apps/extension/src/lib/history.ts` (29) | `MAX_HISTORY`, `loadHistory`, `saveHistory`, `clearHistory` | Test history in chrome.storage.local (newest first). Written only by the service worker. | | `apps/extension/src/lib/isp.ts` (25) | `loadIspPlan`, `saveIspPlan` | The user's advertised ISP plan, in chrome.storage.sync. | -| `apps/extension/src/lib/quality.ts` (17) | `QUALITY_COLORS` | Quality rules live in @meterx/shared so the site uses the same ones; this file adds the toolbar badge colours. | +| `apps/extension/src/lib/quality.ts` (17) | `QUALITY_COLORS` | Quality rules live in @meterx/shared so the site uses the same ones; this file adds the toolbar badge colors. | | `apps/extension/src/lib/tooltips.ts` (45) | `TOOLTIP_COPY`, `bindTooltips` | Plain-language explanations for metric names, from [data-tip] attributes. | | `apps/extension/src/lib/ui.ts` (133) | `animatePhase`, `MARK_SVG`, `edgeName`, `scopeLine`, `setFigure`, `useCasesOf` | Rendering shared by the popup and the side panel. DOM only, no storage. | | `apps/extension/src/pages/options.ts` (265) | - | MeterX Options Page | diff --git a/docs/KNOWN-ISSUES.md b/docs/KNOWN-ISSUES.md index b6c5191..8de2566 100644 --- a/docs/KNOWN-ISSUES.md +++ b/docs/KNOWN-ISSUES.md @@ -10,9 +10,9 @@ Remove a row when its fix is merged. |---|---|---| | #60 | High | Upload reads 0 Mbps below about 12.6 Mbps (bytes credited only per finished 5 MB request) | | #61 | High | Tests run with the popup closed are never saved | -| #62 | Med | Failed and cancelled tests are saved as history | +| #62 | Med | Failed and canceled tests are saved as history | | #63 | Med | Cancel doesn't stop in-flight transfers or the ping phase | -| #64 | Med | Speeds use mebibits but are labelled Mbps (about 4.6% low) | +| #64 | Med | Speeds use mebibits but are labeled Mbps (about 4.6% low) | | #65 | Med | Failed requests retry in a tight loop and show as 0 Mbps | | #66 | Med | Schedule alarm resets on every browser start | | #67 | Low | Notify-on-drop fires on every poor result; quality rules in three copies | diff --git a/docs/SITE-PLAN.md b/docs/SITE-PLAN.md index 83b3954..49185d7 100644 --- a/docs/SITE-PLAN.md +++ b/docs/SITE-PLAN.md @@ -55,7 +55,7 @@ The idea: **the visitor's own speed becomes the word in the sentence.** A speed | How it works | A full test takes about 20 seconds and here is where the time goes | The extension's progress bar enlarged across the page, segments sized by the real phase lengths: connect under a second, latency 2 seconds, download about 9, upload about 9. This is the pinned story: the bar holds while each phase's explanation scrolls past | | Where it runs | It is in the stores you already use | One sentence of browser names, one lit at a time: "Install MeterX in Firefox, Microsoft Edge, Opera, and soon Chrome." Only browsers with a live listing are named as available | | Questions | Six honest answers | Is it free? What leaves my browser? Why does it differ from other speed tests? What is bufferbloat? Can I run my own server? When is it coming to Chrome? | -| Close | Install it now | The page's colours inverted: "Test from your toolbar." with the install button for the detected browser | +| Close | Install it now | The page's colors inverted: "Test from your toolbar." with the install button for the detected browser | | Footer | It is open source and made by one person | The GitHub repo address as the large line, links to About, Privacy, Changelog and ajithakdev.com, AGPL notice | The live test needs care on the worker side: the site's quick test must stay well inside the worker's rate limits and the free plan's request budget (100,000 requests a day). One quick test is about 10 requests, against about 150 for a full extension test. The site origin must be added to the worker's CORS list. diff --git a/docs/STORE-CHECKLIST.md b/docs/STORE-CHECKLIST.md index ec755f2..bb433eb 100644 --- a/docs/STORE-CHECKLIST.md +++ b/docs/STORE-CHECKLIST.md @@ -80,7 +80,7 @@ In each browser: - [ ] Upload `-chromium.zip`. - [ ] Summary in full, grammatical sentences that say what it does. Description says how to use it and what it looks like. - [ ] Nothing in the name or images says "Opera" or suggests Opera made it. -- [ ] Icons at every size share one style and are not just a word on a colour. +- [ ] Icons at every size share one style and are not just a word on a color. - [ ] Screenshots are PNG, not interlaced, and the popup screenshot matches the popup's real size. - [ ] Third-party libraries (`vendor/charts.js`, jsPDF in the bundles) are unmodified releases without known vulnerabilities. Run `npm audit --omit=dev`. - [ ] Once the listing is public, put its link in `apps/site/src/stores.ts` and the README. diff --git a/package.json b/package.json index d92d593..f89408d 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,17 @@ "build": "npm run build --workspaces --if-present", "start": "node services/server/dist/server.js", "dev": "npm run build --workspaces --if-present && node services/server/dist/server.js", - "test": "npm run test --workspaces --if-present && vitest run scripts", + "test": "npm run test --workspaces --if-present && vitest run scripts && npm run check:docs", "package": "node scripts/package-stores.mjs", "dev:site": "node scripts/dev.mjs site", - "dev:ext": "node scripts/dev.mjs ext" + "dev:ext": "node scripts/dev.mjs ext", + "check:docs": "bash scripts/guidelines.sh" }, - "keywords": ["speed-test", "browser-extension", "network-monitor"], + "keywords": [ + "speed-test", + "browser-extension", + "network-monitor" + ], "author": "ajithakdev (https://github.com/ajithakdev)", "license": "AGPL-3.0", "devDependencies": { diff --git a/scripts/guidelines.sh b/scripts/guidelines.sh new file mode 100755 index 0000000..6976335 --- /dev/null +++ b/scripts/guidelines.sh @@ -0,0 +1,74 @@ +#!/usr/bin/env bash +# Static checks that keep the docs honest: the README template (~/.claude/templates/repo-docs.md), +# Fedora Magazine style rules and the store limits. Offline; reads files only. Run: npm run check:docs +set -uo pipefail + +cd "$(dirname "$0")/.." || exit 1 +# CHANGELOG keeps its history as written, so the style checks skip it. +mapfile -t DOCS < <(git ls-files '*.md' '.github/*.yml' '.github/ISSUE_TEMPLATE/*' | grep -v '^CHANGELOG.md$') +mapfile -t ALL_MD < <(git ls-files '*.md') + +pass=0 failed=0 +check() { local what=$1; shift; if "$@"; then echo " ok: $what"; pass=$((pass + 1)); else echo "FAIL: $what" >&2; failed=$((failed + 1)); fi; } +none() { local what=$1 pat=$2; shift 2; local hits; hits=$(grep -nIE "$pat" "$@" 2>/dev/null); [ -z "$hits" ] || echo "${hits//$'\n'/$'\n' }" | sed '1s/^/ /' >&2; check "$what" test -z "$hits"; } + +echo "== README holds what the template asks for" +for h in 'Install' 'Features' 'Privacy and data' 'How it works' 'FAQ' 'Contributing' 'License'; do + check "README has a '$h' section" grep -q "^## $h\$" README.md +done +check "README links RESOURCES-AND-GUIDE.md near the top" bash -c 'head -20 README.md | grep -q "RESOURCES-AND-GUIDE.md"' +check "README is about 100 to 160 lines" bash -c 'n=$(wc -l < README.md); [ "$n" -ge 60 ] && [ "$n" -le 160 ]' +check "README says what it costs and that results stay local" bash -c 'grep -qi "free" README.md && grep -qi "never leave your browser" README.md' + +echo "== store limits" +check "manifest description is 132 characters or fewer" node -e 'process.exit(require("./apps/extension/static/manifest.json").description.length <= 132 ? 0 : 1)' + +echo "== writing style" +none "no em or en dashes" $'\xe2\x80\x93|\xe2\x80\x94' "${ALL_MD[@]}" +none "no exclusionary terms" '\b(black|white)list|\bslave\b|\bmaster (branch|key|node)' "${DOCS[@]}" +none "US spelling" '\b(colour|behaviour|favourite|organis(e|ed|ing)|recognis(e|ed)|artefact|centred?|licence|initialis(e|ed)|customis(e|ed)|optimis(e|ed)|catalogue|labelled|cancelled)s?\b' "${DOCS[@]}" +none "third-party names styled as their owners do" '\bGithub\b|\bCloudFlare\b|\bFireFox\b|\bChromeium\b|\bJavascript\b|\bTypescript\b' "${DOCS[@]}" +check "headings use sentence case" python3 - "${DOCS[@]}" <<'EOF' +import re, sys +# Words that keep their capitals: names, acronyms, file names and product names. +keep = set("""MeterX Cloudflare Worker Workers Pages Firefox Chrome Chromium Edge Opera Brave Vivaldi Arc Safari +Microsoft Mozilla Google Bing Search Console GitHub Express Node npm AMO PDF CSV JSON ISP API CORS DNS SEO +TL;DR FAQ README CHANGELOG CONTRIBUTING SECURITY HANDOFF CODEMAP STATE STORE CHECKLIST PR CI I Web Store +Add-ons Partner Center Linux Android Fedora GNOME Unreleased Added Changed Fixed Removed Security Deprecated""".split()) +bad = [] +for f in sys.argv[1:]: + if not f.endswith(".md"): continue + for n, line in enumerate(open(f), 1): + m = re.match(r"#{1,6} (.+)", line) + if not m or re.match(r"\[\d", m.group(1)): continue + words = re.findall(r"[A-Za-z][\w;.'-]*", re.sub(r"`[^`]*`", "", m.group(1)))[1:] + caps = [w for w in words if w[0].isupper() and w not in keep and not w.isupper()] + if caps: bad.append(f" {f}:{n}: {line.strip()} ({', '.join(caps)})") +print("\n".join(bad), file=sys.stderr) +sys.exit(1 if bad else 0) +EOF + +echo "== docs hold together" +check "relative links and anchors resolve" python3 - "${ALL_MD[@]}" <<'EOF' +import os, re, sys +def slug(h): return re.sub(r"[^\w\- ]", "", re.sub(r"`", "", h).strip().lower()).replace(" ", "-") +anchors = {f: {slug(m) for m in re.findall(r"^#{1,6} (.+)$", open(f).read(), re.M)} for f in sys.argv[1:]} +bad = [] +for f in anchors: + text = re.sub(r"```.*?```", "", open(f).read(), flags=re.S) + for target in re.findall(r"\]\(([^)\s]+)\)|(?:href|src)=\"([^\"]+)\"", text): + t = target[0] or target[1] + if re.match(r"[a-z]+:", t): continue + path, _, frag = t.partition("#") + p = os.path.normpath(os.path.join(os.path.dirname(f), path)) if path else f + if not os.path.exists(p): bad.append(f" {f}: missing {t}") + elif frag and p in anchors and frag not in anchors[p]: bad.append(f" {f}: no heading for #{frag}") +print("\n".join(bad), file=sys.stderr) +sys.exit(1 if bad else 0) +EOF +check "CHANGELOG has an Unreleased section" grep -q '^## \[Unreleased\]' CHANGELOG.md +check "SECURITY.md exists and explains how to report" grep -qi "report" SECURITY.md + +echo +if [ "$failed" != 0 ]; then echo "FAIL ($failed of $((pass + failed)) checks)"; exit 1; fi +echo "PASS ($pass checks)" From 1d9895d2e92f62bbfcb04b8d20cb30c615536771 Mon Sep 17 00:00:00 2001 From: ajithakdev Date: Tue, 27 Oct 2026 01:16:00 +0530 Subject: [PATCH 4/5] docs: code map for the theme module --- apps/extension/src/lib/theme.ts | 5 +++-- docs/CODEMAP.md | 8 +++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/apps/extension/src/lib/theme.ts b/apps/extension/src/lib/theme.ts index df4ea08..e6e7791 100644 --- a/apps/extension/src/lib/theme.ts +++ b/apps/extension/src/lib/theme.ts @@ -1,9 +1,10 @@ -/// - /** * Theme override from settings. Unset follows prefers-color-scheme, which some browsers * (Firefox among them) take from the OS rather than the browser's own theme. */ + +/// + export const THEME_KEY = 'theme'; export type Theme = 'system' | 'light' | 'dark'; diff --git a/docs/CODEMAP.md b/docs/CODEMAP.md index f563234..de2e51e 100644 --- a/docs/CODEMAP.md +++ b/docs/CODEMAP.md @@ -46,11 +46,12 @@ Generated by `node scripts/codemap.mjs`; regenerate after moving or adding files | `apps/extension/src/lib/history.ts` (29) | `MAX_HISTORY`, `loadHistory`, `saveHistory`, `clearHistory` | Test history in chrome.storage.local (newest first). Written only by the service worker. | | `apps/extension/src/lib/isp.ts` (25) | `loadIspPlan`, `saveIspPlan` | The user's advertised ISP plan, in chrome.storage.sync. | | `apps/extension/src/lib/quality.ts` (17) | `QUALITY_COLORS` | Quality rules live in @meterx/shared so the site uses the same ones; this file adds the toolbar badge colors. | +| `apps/extension/src/lib/theme.ts` (23) | `THEME_KEY`, `Theme`, `applyTheme` | Theme override from settings. Unset follows prefers-color-scheme, which some browsers | | `apps/extension/src/lib/tooltips.ts` (45) | `TOOLTIP_COPY`, `bindTooltips` | Plain-language explanations for metric names, from [data-tip] attributes. | | `apps/extension/src/lib/ui.ts` (133) | `animatePhase`, `MARK_SVG`, `edgeName`, `scopeLine`, `setFigure`, `useCasesOf` | Rendering shared by the popup and the side panel. DOM only, no storage. | -| `apps/extension/src/pages/options.ts` (265) | - | MeterX Options Page | -| `apps/extension/src/pages/popup.ts` (171) | - | Popup: run a test, see the latest result. | -| `apps/extension/src/pages/sidepanel.ts` (244) | - | Side panel: history, trend and the plan comparison. Also opens as a tab | +| `apps/extension/src/pages/options.ts` (276) | - | MeterX Options Page | +| `apps/extension/src/pages/popup.ts` (174) | - | Popup: run a test, see the latest result. | +| `apps/extension/src/pages/sidepanel.ts` (247) | - | Side panel: history, trend and the plan comparison. Also opens as a tab | | `apps/site/build.mjs` (41) | - | | | `apps/site/src/capacity.ts` (30) | `Capacity`, `capacity`, `formatDuration` | What a download speed buys, using the extension's own thresholds: 25 Mbps for a 4K stream, 5 Mbps for HD. | | `apps/site/src/common.ts` (102) | `wireInstall` | Every page: install buttons for the visitor's browser, and section entrances. | @@ -86,6 +87,7 @@ Generated by `node scripts/codemap.mjs`; regenerate after moving or adding files - `apps/extension/src/background/measure.test.ts` - `apps/extension/src/lib/browser.test.ts` - `apps/extension/src/lib/quality.test.ts` +- `apps/extension/src/lib/theme.test.ts` - `apps/site/src/capacity.test.ts` - `apps/site/src/github.test.ts` - `apps/site/src/sample.test.ts` From b890db1ecf7820ed9b8082b50302acb57b0e4c19 Mon Sep 17 00:00:00 2001 From: ajithakdev Date: Tue, 27 Oct 2026 01:26:19 +0530 Subject: [PATCH 5/5] docs: dark-mode README images and live count badges --- CHANGELOG.md | 2 +- README.md | 16 ++++++++++++---- brand/build.mjs | 12 ++++++++---- .../png/readme/screenshot-1-dark-1280x800.png | Bin 0 -> 101576 bytes 4 files changed, 21 insertions(+), 9 deletions(-) create mode 100644 brand/png/readme/screenshot-1-dark-1280x800.png diff --git a/CHANGELOG.md b/CHANGELOG.md index d9fe402..fe5d408 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,7 +15,7 @@ All notable changes to MeterX are documented here. The format follows [Keep a Ch - `SECURITY.md` with what MeterX trusts and how to report privately, issue forms for bugs and features, and `npm run check:docs`, which checks the docs' sections, links, spelling and headings as part of `npm test` ### Changed -- README rewritten around install, privacy and data, how a test works and a FAQ; CONTRIBUTING covers loading the extension in Firefox and the pull request rules +- README rewritten around install, privacy and data, how a test works and a FAQ, with dark-mode images and live store user, download and star counts; CONTRIBUTING covers loading the extension in Firefox and the pull request rules ## [2027.0.0] - 2026-10-27 diff --git a/README.md b/README.md index bb1eec9..dc9ba50 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,17 @@
-MeterX + + + MeterX + **A free, open-source speed test in your browser toolbar that also tells you what the numbers mean.** [![CI](https://github.com/ajithakdev/meterx/actions/workflows/ci.yml/badge.svg)](https://github.com/ajithakdev/meterx/actions/workflows/ci.yml) -[![Release](https://img.shields.io/github/v/release/ajithakdev/meterx)](https://github.com/ajithakdev/meterx/releases/latest) -[![Firefox Add-ons](https://img.shields.io/amo/v/meterx-by-ajithakdev?label=Firefox)](https://addons.mozilla.org/firefox/addon/meterx-by-ajithakdev/) +[![Firefox Add-ons](https://img.shields.io/amo/v/meterx-by-ajithakdev?label=Firefox&logo=firefoxbrowser&logoColor=white)](https://addons.mozilla.org/firefox/addon/meterx-by-ajithakdev/) +[![Edge users](https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fmicrosoftedge.microsoft.com%2Faddons%2Fgetproductdetailsbycrxid%2Fglkpolbkpachomgmmhdkmjnghejbhlfc&query=%24.activeInstallCount&label=Edge%20users&color=0078D7)](https://microsoftedge.microsoft.com/addons/detail/glkpolbkpachomgmmhdkmjnghejbhlfc) +[![Downloads](https://img.shields.io/github/downloads/ajithakdev/meterx/total?label=downloads)](https://github.com/ajithakdev/meterx/releases) +[![Stars](https://img.shields.io/github/stars/ajithakdev/meterx?style=flat)](https://github.com/ajithakdev/meterx/stargazers) [![License: AGPL-3.0](https://img.shields.io/badge/License-AGPL--3.0-blue.svg)](LICENSE) [Install](#install) · [Privacy](#privacy-and-data) · [How it works](#how-it-works) · [FAQ](#faq) · [Website](https://meterx.ajithakdev.com) · [Guide](RESOURCES-AND-GUIDE.md) @@ -18,7 +23,10 @@ Most speed tests stop at a download number. MeterX also measures ping while your MeterX is free, with no account, no ads and no tracking. Your results never leave your browser unless you share them. It is a community project and is not affiliated with Cloudflare, Mozilla, Microsoft, Opera or Google.

- MeterX popup with a sample result: 280 Mbps download, 91.1 Mbps upload, 18 ms ping, grade B latency under load, and Smooth for video calls and gaming + + + MeterX popup with a sample result: 280 Mbps download, 91.1 Mbps upload, 18 ms ping, grade B latency under load, and Smooth for video calls and gaming +

## Install diff --git a/brand/build.mjs b/brand/build.mjs index 0b0fe21..d5a76ad 100644 --- a/brand/build.mjs +++ b/brand/build.mjs @@ -135,14 +135,18 @@ const shots = [ ['settings-light.png', 'Your plan, your schedule, your server', 'Compare with the speeds you pay for, test on a timer, or use your own server.'], ['report.png', 'Reports you can hand to your provider', 'A PDF with averages, a trend and every result, plus CSV and JSON.'], ]; -for (const [i, [file, title, line]] of shots.entries()) { +function shotPage(file, title, line, dark = false) { const data = readFileSync(out('png/ui', file)).toString('base64'); - await render(page(1280, 800, '#f2f4f7', `

${title}

${line}

`, + const [ground, ink, sub, edge] = dark ? [INK.dark, '#f4f5f6', '#a4a8ae', '#26292d'] : ['#f2f4f7', '#0a0a0a', '#5b6068', '#e1e4e8']; + return page(1280, 800, ground, `

${title}

${line}

`, `.c{position:absolute;left:80px;top:0;bottom:0;width:440px;display:flex;flex-direction:column;justify-content:center} - h1{font-size:52px;font-weight:600;letter-spacing:-.035em;line-height:1.02;color:#0a0a0a}p{font-size:22px;line-height:1.4;color:#5b6068;margin-top:20px} + h1{font-size:52px;font-weight:600;letter-spacing:-.035em;line-height:1.02;color:${ink}}p{font-size:22px;line-height:1.4;color:${sub};margin-top:20px} .s{position:absolute;left:600px;right:60px;top:40px;bottom:40px;display:flex;align-items:center;justify-content:center} - .s img{max-width:100%;max-height:100%;border-radius:14px;box-shadow:0 0 0 1px #e1e4e8}`), 1280, 800, `png/store/screenshot-${i + 1}-1280x800.png`); + .s img{max-width:100%;max-height:100%;border-radius:14px;box-shadow:0 0 0 1px ${edge}}`); } +for (const [i, [file, title, line]] of shots.entries()) await render(shotPage(file, title, line), 1280, 800, `png/store/screenshot-${i + 1}-1280x800.png`); +// The README shows this one on GitHub's dark theme. +await render(shotPage('popup-dark.png', shots[0][1], shots[0][2], true), 1280, 800, 'png/readme/screenshot-1-dark-1280x800.png'); await browser.close(); diff --git a/brand/png/readme/screenshot-1-dark-1280x800.png b/brand/png/readme/screenshot-1-dark-1280x800.png new file mode 100644 index 0000000000000000000000000000000000000000..bc3694731897353450edda5cb57b06f8e53d7165 GIT binary patch literal 101576 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 z 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 KyM literal 0 HcmV?d00001