From 7c67955d4b7bb9c46256c122115893673d3a6395 Mon Sep 17 00:00:00 2001 From: muhammad-wei Date: Thu, 3 Sep 2026 13:44:13 +0800 Subject: [PATCH 1/3] Add GasPulse submission (Open Innovation track) Ethereum address gas-consumption trend and activity-score API for trading agents, deployed at https://api.gaspulse.win. Review commit 0b5562319a3e1e96adeef5e1231bd4cbd2dd3030, verified live: health check and deployment proof both bound to that commit, capability tested against real Etherscan data, offline and --online validate:submission both pass. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_012SF7wRZMuBk8ihrx4xrgYe --- .../visioneer-gaspulse/RIGHTS.md | 16 + .../visioneer-gaspulse/SUBMISSION.md | 94 + .../visioneer-gaspulse/source/.dockerignore | 6 + .../visioneer-gaspulse/source/.env.example | 5 + .../visioneer-gaspulse/source/.gitignore | 4 + .../visioneer-gaspulse/source/Dockerfile | 22 + .../visioneer-gaspulse/source/LICENSE | 21 + .../visioneer-gaspulse/source/README.md | 126 + .../source/package-lock.json | 2545 +++++++++++++++++ .../visioneer-gaspulse/source/package.json | 27 + .../visioneer-gaspulse/source/src/config.ts | 5 + .../source/src/lib/activityScore.ts | 61 + .../source/src/lib/etherscan.ts | 62 + .../source/src/lib/gasTrend.ts | 71 + .../source/src/routes/activity.ts | 88 + .../source/src/routes/health.ts | 6 + .../source/src/routes/verification.ts | 10 + .../visioneer-gaspulse/source/src/server.ts | 34 + .../visioneer-gaspulse/source/src/types.ts | 50 + .../source/test/activity.route.test.ts | 93 + .../source/test/activityScore.test.ts | 59 + .../source/test/gasTrend.test.ts | 63 + .../visioneer-gaspulse/source/tsconfig.json | 15 + .../visioneer-gaspulse/submission.json | 10 + .../visioneer-gaspulse/verification/README.md | 82 + 25 files changed, 3575 insertions(+) create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/RIGHTS.md create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/SUBMISSION.md create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/.dockerignore create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/.env.example create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/.gitignore create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/Dockerfile create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/LICENSE create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/README.md create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/package-lock.json create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/package.json create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/src/config.ts create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/activityScore.ts create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/etherscan.ts create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/gasTrend.ts create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/activity.ts create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/health.ts create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/verification.ts create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/src/server.ts create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/src/types.ts create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/test/activity.route.test.ts create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/test/activityScore.test.ts create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/test/gasTrend.test.ts create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/tsconfig.json create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/submission.json create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/verification/README.md diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/RIGHTS.md b/submissions/mcp-hackathon/visioneer-gaspulse/RIGHTS.md new file mode 100644 index 0000000..c08eef9 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/RIGHTS.md @@ -0,0 +1,16 @@ +# Submission rights declaration + +Project: `GasPulse` +Submission slug: `visioneer-gaspulse` +Submitter: `Visioneer (muhammad-wei, https://github.com/muhammad-wei)` +Date: `2026-09-03` + +The submitter confirms that they own, or have sufficient authorization for, the source code, dependencies, service, data, branding, and other materials submitted in this pull request. + +Subject to the official program terms, the submitter authorizes X-Agent to retain, reproduce, audit, test, archive, and publish the submitted program artifact for judging, fraud prevention, dispute handling, ecosystem submission, and post-award accountability. Closing the pull request, deleting a fork, or deleting an external repository does not revoke the official archive rights attached to an accepted and rewarded entry. + +Third-party components and their licenses: `fastify@4.29.1 (MIT), zod@3.25.76 (MIT), dotenv@16.6.1 (BSD-2-Clause), typescript@5.9.3 (Apache-2.0), tsx@4.23.13 (MIT), vitest@2.1.9 (MIT). Runtime data source: Etherscan API (api.etherscan.io), used under Etherscan's public API terms — no Etherscan code or data is redistributed, only queried live per request.` + +Exceptions or restrictions: `none` + +This template is an operational declaration, not a substitute for event terms reviewed by qualified counsel. diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/SUBMISSION.md b/submissions/mcp-hackathon/visioneer-gaspulse/SUBMISSION.md new file mode 100644 index 0000000..644a407 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/SUBMISSION.md @@ -0,0 +1,94 @@ +# GasPulse + +> Replace every angle-bracket placeholder before opening the pull request. + +## Capability + +- **One-line description:** Given an Ethereum mainnet address, returns a recent gas-consumption + trend and a deterministic 0–100 activity/liveliness score, so a trading agent can quickly gauge + how active an address currently is. +- **Who it helps:** AI trading agents (and their operators) that need a fast, structured signal + on address activity before acting — e.g. deciding whether a counterparty or watched address is + currently live. +- **Capability boundary:** Read-only Ethereum mainnet data sourced from Etherscan. Computes gas + trend and an activity score from an address's most recent transactions (bounded — see Known + risks below). It does **not** perform fraud, risk, compliance, or security analysis of any + kind: `activityScore` reflects only the recency, frequency, and consistency of transactions, + never trustworthiness or danger. + +## Live API + +- **API base URL:** https://api.gaspulse.win/v1 +- **Health-check URL:** https://api.gaspulse.win/health +- **Authentication:** none +- **Rate limits / known limits:** bounded by the Etherscan free tier (~5 req/s upstream); + GasPulse caches the raw per-address transaction list for 60 seconds in memory to absorb + repeated agent calls. Upstream requests time out after 8s server-side, surfaced to the caller + as `429`/`502` (see error taxonomy below and in `source/README.md`). +- **API contract:** `GET /v1/address/{address}/activity?windowDays=30` — full request/response + shapes, field meanings, and the activity-score methodology are documented in + `source/README.md` (reproduced with a live example in `verification/README.md`). + +## Source and reproducibility + +- **Source repository:** https://github.com/muhammad-wei/gaspulse +- **Review commit:** `0b5562319a3e1e96adeef5e1231bd4cbd2dd3030` +- **Source submitted in this PR:** `source/` +- **Run tests:** `npm ci && npm test` +- **Run locally:** `npm ci && cp .env.example .env` (fill in `ETHERSCAN_API_KEY`) `&& npm run dev` +- **Deploy:** `npm run build && node dist/server.js` with `GIT_COMMIT` and `ETHERSCAN_API_KEY` set + in the environment; a Docker alternative (`docker build --build-arg GIT_COMMIT=$(git rev-parse HEAD) ...`) + is documented in `source/README.md`. +- **Version binding:** `GIT_COMMIT` is injected into the process environment at deploy time + (Docker: `--build-arg GIT_COMMIT` baked to `ENV`; this live deployment: a systemd service + `Environment=` line set to the exact commit above) and read by `/health` and + `/.well-known/xagent-verification.json` at request time — never read from `.git` or hardcoded. + +The API must expose: + +```json +// GET https://api.gaspulse.win/health +{"status":"ok","commit":"0b5562319a3e1e96adeef5e1231bd4cbd2dd3030"} +``` + +```json +// GET /.well-known/xagent-verification.json on the same API origin +{"schemaVersion":1,"slug":"visioneer-gaspulse","commit":"0b5562319a3e1e96adeef5e1231bd4cbd2dd3030"} +``` + +## Verification + +The reproducible call instructions and redacted example responses are in `verification/README.md`. + +- **Health-check result:** `GET https://api.gaspulse.win/health` → + `{"status":"ok","commit":"0b5562319a3e1e96adeef5e1231bd4cbd2dd3030"}` +- **Capability call:** `GET https://api.gaspulse.win/v1/address/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/activity?windowDays=30` + → `200` with gas trend and activity score for that address. +- **Expected error behavior:** malformed address → `400 invalid_address`; out-of-range + `windowDays` → `400 invalid_window`; Etherscan rate-limited → `429 upstream_rate_limited`; + Etherscan unreachable/erroring → `502 upstream_unavailable`. An address with no transactions + returns `200` with every field zeroed, never `404` — deterministic either way. + +## Security and data handling + +- **Data collected:** none stored persistently. The Ethereum address in the request path is + forwarded to Etherscan's public API to look up that address's public on-chain transaction + history; results are cached in-process for 60 seconds, then discarded. +- **Purpose and retention:** the fetched transaction data is used only to compute the response + for that single request; nothing is logged or persisted beyond the 60-second in-memory cache. +- **Third parties / outbound network calls:** Etherscan API (`api.etherscan.io`) — the only + outbound call GasPulse makes. +- **Secrets:** No secrets are committed. `ETHERSCAN_API_KEY` is supplied via environment variable + only (`.env.example` ships an empty placeholder). This API requires no auth, so no review + credential needs to be shared. +- **Known risks / restrictions:** Ethereum mainnet only. Gas trend and `firstSeen`/`lastSeen` are + computed from the most recent 1,000 transactions Etherscan returns for the address — + extremely long-lived, high-volume addresses may have earlier history not reflected. + `activityScore` is deliberately not a risk/fraud/security signal (see Capability boundary). + +## Support + +- **Team / builder:** Visioneer +- **Contact:** https://github.com/muhammad-wei +- **License / rights:** MIT (`source/LICENSE`). Submitter confirms ownership/authorization to + submit and license this code for review and deployment — see `RIGHTS.md`. diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/.dockerignore b/submissions/mcp-hackathon/visioneer-gaspulse/source/.dockerignore new file mode 100644 index 0000000..a9777a7 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/.dockerignore @@ -0,0 +1,6 @@ +node_modules +dist +.git +.env +test +*.md diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/.env.example b/submissions/mcp-hackathon/visioneer-gaspulse/source/.env.example new file mode 100644 index 0000000..0dd9189 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/.env.example @@ -0,0 +1,5 @@ +# Free API key from https://etherscan.io/apis — required to query transaction history. +ETHERSCAN_API_KEY= + +# Optional overrides +PORT=8080 diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/.gitignore b/submissions/mcp-hackathon/visioneer-gaspulse/source/.gitignore new file mode 100644 index 0000000..3b24e4a --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/.gitignore @@ -0,0 +1,4 @@ +node_modules +dist +.env +*.log diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/Dockerfile b/submissions/mcp-hackathon/visioneer-gaspulse/source/Dockerfile new file mode 100644 index 0000000..76aa01c --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/Dockerfile @@ -0,0 +1,22 @@ +FROM node:20-slim AS build +WORKDIR /app +COPY package.json package-lock.json ./ +RUN npm ci +COPY tsconfig.json ./ +COPY src ./src +RUN npm run build + +FROM node:20-slim +WORKDIR /app +ENV NODE_ENV=production +COPY package.json package-lock.json ./ +RUN npm ci --omit=dev +COPY --from=build /app/dist ./dist + +# Baked at build time — never read from a mutable file or set at runtime. +ARG GIT_COMMIT +ENV GIT_COMMIT=${GIT_COMMIT} +ENV PORT=8080 + +EXPOSE 8080 +CMD ["node", "dist/server.js"] diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/LICENSE b/submissions/mcp-hackathon/visioneer-gaspulse/source/LICENSE new file mode 100644 index 0000000..b6ef61a --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Visioneer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/README.md b/submissions/mcp-hackathon/visioneer-gaspulse/source/README.md new file mode 100644 index 0000000..5b8dc4c --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/README.md @@ -0,0 +1,126 @@ +# GasPulse + +Ethereum address gas-consumption trend and activity-score API, built for AI trading agents +that need a fast, deterministic read on how active an address currently is. + +## Capability boundary + +- **Does**: given an Ethereum mainnet address, returns recent gas-usage trend and a 0–100 + activity/liveliness score derived from public on-chain transaction history (via Etherscan). +- **Does not**: perform fraud, risk, compliance, or security analysis of any kind. `activityScore` + reflects only the recency, frequency, and consistency of transactions — never trustworthiness, + danger, or a security signal. If you need risk/compliance screening, this is not that tool. + +## API + +### `GET /health` + +```json +{"status":"ok","commit":"<40-char-sha>"} +``` + +### `GET /.well-known/xagent-verification.json` + +```json +{"schemaVersion":1,"slug":"visioneer-gaspulse","commit":"<40-char-sha>"} +``` + +### `GET /v1/address/{address}/activity?windowDays=30` + +- `address` (path, required) — a `0x`-prefixed 40-hex-character Ethereum address. +- `windowDays` (query, optional) — integer, `1`–`180`, default `30`. + +Success (`200`) — an address with no activity still returns `200` with zeroed fields, never +`404`, so agents get a deterministic shape either way: + +```json +{ + "address": "0x0000000000000000000000000000000000dead", + "network": "ethereum-mainnet", + "windowDays": 30, + "asOf": "2026-09-02T00:00:00.000Z", + "txCount": 12, + "firstSeen": "2024-01-05T10:20:00.000Z", + "lastSeen": "2026-09-01T08:00:00.000Z", + "gasTrend": { + "totalGasUsed": "252000", + "avgGasPriceGwei": 14.2, + "direction": "increasing", + "buckets": [ + { "periodStart": "...", "periodEnd": "...", "txCount": 3, "gasUsed": "63000", "avgGasPriceGwei": 11.1 } + ] + }, + "activityScore": { "value": 78, "recency": 90, "frequency": 70, "consistency": 65 }, + "dataSource": "etherscan" +} +``` + +**Errors** — stable `{"error":{"code":..., "message":...}}` shape: + +| HTTP | code | meaning | +| --- | --- | --- | +| 400 | `invalid_address` | address is not a well-formed `0x` + 40 hex chars string | +| 400 | `invalid_window` | `windowDays` is missing bounds or not an integer 1–180 | +| 429 | `upstream_rate_limited` | Etherscan rate limit hit — retry after a short delay | +| 502 | `upstream_unavailable` | Etherscan request failed or timed out | + +**Rate limits / caching**: bounded by the Etherscan free tier (~5 req/s). GasPulse caches the +raw transaction list per address for 60 seconds in memory to absorb repeated agent calls. + +**Known limitations**: gas trend and `firstSeen`/`lastSeen` are computed from the most recent +1,000 transactions Etherscan returns for the address; extremely long-lived, high-volume +addresses may have earlier history that isn't reflected. Ethereum mainnet only. + +## Setup + +Requirements: Node.js `>=18.17`, a free Etherscan API key (https://etherscan.io/apis). + +```bash +npm ci +cp .env.example .env # fill in ETHERSCAN_API_KEY +npm run dev +``` + +## Test + +```bash +npm test +``` + +## Build and run + +```bash +npm run build +npm start +``` + +## Docker + +The deployed commit is baked into the image at build time (never read from a mutable file or +hardcoded) via `--build-arg GIT_COMMIT`: + +```bash +docker build --build-arg GIT_COMMIT=$(git rev-parse HEAD) -t gaspulse . +docker run --rm -p 8080:8080 --env-file .env gaspulse +``` + +## Deploy + +Any always-on container host works (the image is stock Node 20 + Fastify, no host-specific +code): build the image above, push it to the host's registry, deploy with `PORT=8080` exposed +and `/health` as the health-check path, and point DNS/TLS termination at port 443. Avoid +free tiers that sleep on idle — the hard uptime requirement rules those out. + +## Activity score methodology + +`activityScore.value` is `0.4 * recency + 0.4 * frequency + 0.2 * consistency`, each on a 0–100 +scale, computed only from transactions inside `windowDays`: + +- **recency** — decays linearly from 100 (last transaction was just now) to 0 (last transaction + was `windowDays` ago or there was none). +- **frequency** — `100 * txCount / (windowDays / 3)`, clamped to 100. One transaction every 3 + days earns full credit. +- **consistency** — `100 * (1 - min(1, coefficient of variation of the gaps between consecutive + transactions))`; requires at least 3 in-window transactions, otherwise `0`. + +See `src/lib/activityScore.ts` and `src/lib/gasTrend.ts` for the exact implementation. diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/package-lock.json b/submissions/mcp-hackathon/visioneer-gaspulse/source/package-lock.json new file mode 100644 index 0000000..79da9ae --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/package-lock.json @@ -0,0 +1,2545 @@ +{ + "name": "gaspulse", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "gaspulse", + "version": "0.1.0", + "dependencies": { + "dotenv": "^16.4.5", + "fastify": "^4.28.1", + "zod": "^3.23.8" + }, + "devDependencies": { + "@types/node": "^20.14.15", + "tsx": "^4.16.5", + "typescript": "^5.5.4", + "vitest": "^2.0.5" + }, + "engines": { + "node": ">=18.17" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.28.2.tgz", + "integrity": "sha512-XExcO+dvLKvVtNTibSTBej1NCAbaGhWn9Ww1ZPx80qsahhPFe/8jgWP0IchNe0F3HwkU7n8ejhH8bjonqht8mQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.28.2.tgz", + "integrity": "sha512-kXXoiPVVGQcnIYGOeaovwOURpniDBpSq4A03qkQ+BMQqtGG6HYap3xne9C1O1yo4TR3qxlCX5IqqmX6fFo2Lqg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.28.2.tgz", + "integrity": "sha512-5YfKeeI8qWfBZIX+u2xZC3Zlb3Os/gLS2sbEKM+I4ZOcsWmHS2WLysCcQZDAFRslDUU5Oiq44gf6PYN1vGwG5A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.28.2.tgz", + "integrity": "sha512-O387ite7SzUyCcy3JQX4P4bLtEA7bLLkx+esve5JHnyYfNTxcVpXZo9jhdB0lTKN44gztELTdU7nS8Nr16Fs1Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.28.2.tgz", + "integrity": "sha512-n4KqkOQrraxHJcgjM1RvwbigfQKIKJVpM7xp+KsxiyUSrRdIXnt73VhrPAx0fV44hgfmIVKjxMN9J1t5jySVkw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.28.2.tgz", + "integrity": "sha512-uq6suIWYP37qzGddBKPw5QEQPi6HiLGsO7UmkpfyaYNQ3D+rN6w6WfwH+nuqcGXWvawGwxOEroO4YGnFh95azw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.2.tgz", + "integrity": "sha512-n+I0BTSRIoy+d6RPKnEVwql5UwBJolytvY4mAOIEJorKlqgPII8ix6slVVrfZ5Tnj7glIZvloylbB/EJPMWEXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.28.2.tgz", + "integrity": "sha512-78XJTJkvPs0kz2w61301PJjXl4g7q3JqiYMZ/M/yVI73EHBrCRTgkhu9oqG7vPqq+a/yadEW8aD+agKlk5xrmg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.28.2.tgz", + "integrity": "sha512-XlDnu2q5yoqems+xay6wSAcg9DDD7K9RLKZEBOMZm3ckNpJBvOX20tSfby8KfrrhINDyv9V2YVZKY/SpoGJI8w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.28.2.tgz", + "integrity": "sha512-pW4AC0P3it8c7do9MVM4p51FzHzdM/TZrerurgRcHJ2WTa1VQ1CIq18xncfpBJw4ojkiZZrKW2yIBWBP92j6Ug==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.28.2.tgz", + "integrity": "sha512-CYbnj78HsIeA+DhgUKgFCfvNsTHFhMMrinUrMZpDXJXKN8T3XViTZ/+wtHeVxEWY8ewSzTFN+nRmSwO2tZaLUQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.28.2.tgz", + "integrity": "sha512-buwkd8nsph4R+ajRvw0qM5Hja/TXQow3ptzWO2EbG/cqcIkHloRrdlBtQlshyYGTNFvfkfJ5tpPLVkY4DtsPfQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.28.2.tgz", + "integrity": "sha512-ZVykbDyk7519VwiNb9Lcj9m8XM6v5V9uKPvrEMkkEedVewf+0itkhahp4HDpgERXhwLRpWFypsGbG/J8s0QjJA==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.28.2.tgz", + "integrity": "sha512-CAXl+Dtd9UUuJd8pKKdwh6MLm3MUMiqMPmhZ3tTSXPqfyQ3vDl6R5hZdZ/kYojK4ofXtdfSv1tFq8XzWx3heNQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.28.2.tgz", + "integrity": "sha512-GeXCej4IQtU1B+QlDV8W/RRvbzI3O/Stss+/bCXv4lZls5WGRtu2a+3JkA3i4qIUlMXpcHebWpF8AkJhATowuA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.28.2.tgz", + "integrity": "sha512-3H1weTYZPxt/WOhByszQZybS9w5lKzUn1FDMsgEChbHWQwHYQQRfBxgCcZvPhjHfKyJjIievvMmEUawJrdY9Dg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.28.2.tgz", + "integrity": "sha512-4xTZr1FUmSoQW4XIWmit3tzQrUTZM+N3P0XV8xROKYF50XfI7xeO90+1bZvNwxIufQ9hDQVRJH5YhgPVF8A/HQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.2.tgz", + "integrity": "sha512-sSATRjPeDBg3pdgHoQfoYBob11Kk1FGa9lui5RIHZCoCkJa9QKlvl3/vKz2usCmYYjs7ymJR/2Nnsqe+Hjt5nw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.28.2.tgz", + "integrity": "sha512-lqnzCV+mM0gIADaKihiCg6ifgfU2L3h5E33rNQBN1Y4MaVGnzryzmvvf7UHxprpQdE8hpqLolJ9Rl+SkIRDpyw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.2.tgz", + "integrity": "sha512-AL2qJILH7lNjrDmCQDvdxMfAUIv8KMNZOvrwAQ8i8//ntL9FflhOyMJ8OZSMBb8/AWXe3/5v5S20y3zCoZWKoQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.28.2.tgz", + "integrity": "sha512-QtiuPytchRyC4rwUKhexJdQKvDuZ6hWloi3igqPQNUJCS1/v9EiO3UTOXR6A3FoMo4fnAKbWJdqaIwhOzh8qEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.2.tgz", + "integrity": "sha512-WkhYDmpTjLvGlScA1rwjRUmhl4k8oXR3cIbtqWmELgU/dFeHHlEllxDvdWcNJV9rbzCexB5vz8gtNewWLgCT7Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.28.2.tgz", + "integrity": "sha512-GPMSkTOtMnv2U2F8gxe4Io6qmVs+YKyp832Etqqxr0hFngmXQ3rzwytelm3GIn7T4VviRUlf3sOgBOiTdvaf7g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.28.2.tgz", + "integrity": "sha512-PIhhEkE9uPBleRBrQEJpUn7MBnibZzbGzYWPmY3x+YoVg/95zbjB4CxPPOQ8l5tYYM4mMaCthF8/1DIfBQQyWQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.28.2.tgz", + "integrity": "sha512-YmJbfTlvU7Sdn9BB+4PRES4oB6pxgS37MAONj+hBr/cpXS1aBPKXxNnDbu+QCWPj0o9dgyxeq79g6c5P8KeuYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.28.2.tgz", + "integrity": "sha512-5ebpxr3nWMzrL/rnUI755Jkuee0bHL/Gq0WTF9lvcpv73wAp5eu8MfBUgWK9bhWvZjj7yX8etf/8tI8Ney695g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@fastify/ajv-compiler": { + "version": "3.6.0", + "resolved": "https://registry.npmmirror.com/@fastify/ajv-compiler/-/ajv-compiler-3.6.0.tgz", + "integrity": "sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.11.0", + "ajv-formats": "^2.1.1", + "fast-uri": "^2.0.0" + } + }, + "node_modules/@fastify/error": { + "version": "3.4.1", + "resolved": "https://registry.npmmirror.com/@fastify/error/-/error-3.4.1.tgz", + "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==", + "license": "MIT" + }, + "node_modules/@fastify/fast-json-stringify-compiler": { + "version": "4.3.0", + "resolved": "https://registry.npmmirror.com/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", + "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", + "license": "MIT", + "dependencies": { + "fast-json-stringify": "^5.7.0" + } + }, + "node_modules/@fastify/merge-json-schemas": { + "version": "0.1.1", + "resolved": "https://registry.npmmirror.com/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", + "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.6.0", + "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.6.0.tgz", + "integrity": "sha512-T7jf+5zgsZHwNJ4lvQ7/aezbyk0nNX+zJVWpmHA7VYsEx7a7qr5Rg5IbtJFqkgze5Y2sruq1RUY8Q837Od7iFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@napi-rs/lzma-linux-x64-gnu": { + "version": "1.5.1", + "resolved": "https://registry.npmmirror.com/@napi-rs/lzma-linux-x64-gnu/-/lzma-linux-x64-gnu-1.5.1.tgz", + "integrity": "sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^22.20 || ^24.12 || >=25" + } + }, + "node_modules/@pinojs/redact": { + "version": "0.4.0", + "resolved": "https://registry.npmmirror.com/@pinojs/redact/-/redact-0.4.0.tgz", + "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.63.1.tgz", + "integrity": "sha512-UZ8sUxPTiHWYX9QNdJedb1kDZSpS1t/VPWBWGSgqHNi9w3Cu6IXvu2mzbhiTiPvtrqgTQJ+zqiAq2iPIPilpaQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.63.1.tgz", + "integrity": "sha512-cQ4nFQABN5cDvDpbvJ7bMStCpnaVxynZrRMfUJYgxcIk9Sh54FIO1vtfkg0B69REjER77ioZ/ov+eAApx/KmLQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.63.1.tgz", + "integrity": "sha512-FQNqd1lRy/0QhDk3xeRIkSBiCpXCiDnZO3YLVdcDKN1UBiKToNftCzcXYNLshmPDUMlu2TdeS8tGcsU6f3YF1Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.63.1.tgz", + "integrity": "sha512-pvD16V939D3CloK0+qikpGaxiPrDUXTe7Y5cWOMkMSy7m1cawa8EGy/kXYi/G/cKAC4HDAbSnzCIk1WmsoOKXg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.63.1.tgz", + "integrity": "sha512-pcFGeL2345VwdTnJhA6zLbew+YgWB0qBG2+dMtXjCicf6+rm6kO6cOoh5VnTe0ZMrMRgRyuHmCJxZWrIdzYuOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.63.1.tgz", + "integrity": "sha512-mRJlqSRulVzcKq/LKA6ICSIc3K/l4fzlVn/gePn2nXIHy8seRi5z/eeRE0d/XMBxcMldiXtQTSpRj0tkkC3g8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.63.1.tgz", + "integrity": "sha512-YDUNvVM85TI3g/1OpnqKP1h4NeW/j64DfWMf+G3M809xNk1bJSnpFp4sh83NpmVE5DXnkh8ULor4LTVZKoYLHw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.63.1.tgz", + "integrity": "sha512-7Mcn71p9ZuQFAj+h+dhQXy/yeLePRS2yKRnmW1DijA9thKO5qap0GNOIQK4yQ6iP3SU0Mrb/yWo8h8vgRba8lw==", + "cpu": [ + "arm" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.63.1.tgz", + "integrity": "sha512-4YiLQTX6U4CSl0L9cluep9A9W6UmTfqBDc2/CH6wlu54pl4E7Jn3cOD8oxzvBDEGk/JMKgJ47C8g+radF7mwvg==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.63.1.tgz", + "integrity": "sha512-2ra8F7w8OquwZN9z2/fKFnli69wa8PLwaVzRMIPGb13ByMJwC28Fbp8YcVGoUhlYMTt7j5j9bNgpysrN2UM+vw==", + "cpu": [ + "arm64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.63.1.tgz", + "integrity": "sha512-Sy20ncyhjmBP0Ml+UvQbimjlk6VFgjW5uNP+qqwHB00mTE8Bl2C1TuHTlRwK2YoXeZbee5lP2XevBWVkAQAtSQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.63.1.tgz", + "integrity": "sha512-noITLp8oNjYliPnGWmLyelIHwULGqbHloQHGw1rtxbWhTuWooRpnZarZQJ1y9EUC4szuCusCc+HEpUtxpIwYvA==", + "cpu": [ + "loong64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.63.1.tgz", + "integrity": "sha512-hlxxXd+F1mWiAcaFR7Sv9ZQT6m6UfI8+Vy/kFJzztq2pDMU/0wZ9sish0iszNZvsQDo8Gc0i5yuFEOz5dDf6fA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.63.1.tgz", + "integrity": "sha512-EF7OpqQTQ/BvGqLzUi4rEHuagCV9MugAUXSHemwPW5vxZ75RR+jxO/2j95Ph2dalMpFHSVECjRoioHZgA9zOYA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.63.1.tgz", + "integrity": "sha512-wQO3JesW9PRkwlabQ27y7sPfVOOTLRG73I4F2UYHG5PXun3J9U3y+b7ezVKSYbsvSKGQ1k1cq8Qlun4C9kLt3w==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.63.1.tgz", + "integrity": "sha512-ouAGwhO6wHRXdnOVCOsB0tRFkA7nhNB2Nwax6oECXN0YiN8EYUTBAOudADOB1PI+yDL61TeNx/u7MVCzksNbkQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.63.1.tgz", + "integrity": "sha512-q2R38Sn+1J8RxhfJ+T54wSWmyKXWec+9jgDfqO2AtArEqHO5R2aeayp5H5OYLr5UYDVGsVaZPEFUooMhYCdz5A==", + "cpu": [ + "s390x" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.63.1.tgz", + "integrity": "sha512-gfI5T24WLLuFfSKw7Go/zDXjAAV0fny0swTaDv+WjK7vqcw4cRhFfdsyKL1n+ukI+ooBxn3bVQnyrn06WpI50w==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.63.1.tgz", + "integrity": "sha512-4h6XqthmB4Hspji84wvgk+ElodTsGj+dbZqHJHHtKxj4mYq0ANSEEPX9ys3moJueqsRjwpaJYH7874Itwnj2ow==", + "cpu": [ + "x64" + ], + "dev": true, + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.63.1.tgz", + "integrity": "sha512-dlfCOa87o1VAYegLQ9EKilx2JCeRofiyPGhTCmqnuXZ6bMPiycO1rq1+sKoulAp7pGLIsTIw+1x5R+zgh5LhhA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.63.1.tgz", + "integrity": "sha512-cjkLbOlfcm3QGhMM1J5zaZjsw1GggbN6rw9UTSSRrPrR1KkcXnN7Uq9rPw34xImQ9VOY9GN+6u2Zj80B9ptkcw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.63.1.tgz", + "integrity": "sha512-Li1KdUnWGE4N3e1F/B4RTB1ms+nG4WBgjByO46pkeBVX/2UBsY53xf5vK9WygVmnH3RwncIST7lkSdLSY6P9lg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.63.1.tgz", + "integrity": "sha512-t4ZYOSoLTgwhuFMrmTMLx/+i1DQVK7HYqMc6kY46EApwi8X0nIVphzdNoThU3xt6n+N5urG1/gxBdCaKDLavfg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.63.1.tgz", + "integrity": "sha512-RgroPfMmKlD1RzSDxvwgcPiy2HNQKoYV7OmwIXDsk73uKW5t6B/V8KIy27SMv/FNXFo/oSBtWc9J0X7t91ezZg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.63.1.tgz", + "integrity": "sha512-at8QVep6S3h5Y6gSbdGU06bRY5WJkf6WUduM9YtvYMbYhB1MOFfUgc6kehitQXzOtMSaT70q7f9ydPhpqu821w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmmirror.com/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitest/expect": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@vitest/expect/-/expect-2.1.9.tgz", + "integrity": "sha512-UJCIkTBenHeKT1TTlKMJWy1laZewsRIzYighyYiJKZreqtdxSos/S1t+ktRMQWu2CKqaarrkeszJx1cgC5tGZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@vitest/mocker/-/mocker-2.1.9.tgz", + "integrity": "sha512-tVL6uJgoUdi6icpxmdrn5YNo3g3Dxv+IHJBr0GXHaEdTcw3F+cPKnsXFhli6nO+f/6SDKPHEK1UN+k+TQv0Ehg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "2.1.9", + "estree-walker": "^3.0.3", + "magic-string": "^0.30.12" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "msw": "^2.4.9", + "vite": "^5.0.0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@vitest/pretty-format/-/pretty-format-2.1.9.tgz", + "integrity": "sha512-KhRIdGV2U9HOUzxfiHmY8IFHTdqtOhIzCpd8WRdJiE7D/HUcZVD0EgQCVjm+Q9gkUXWgBvMmTtZgIG48wq7sOQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@vitest/runner/-/runner-2.1.9.tgz", + "integrity": "sha512-ZXSSqTFIrzduD63btIfEyOmNcBmQvgOVsPNPe0jYtESiXkhd8u2erDLnMxmGrDCwHCCHE7hxwRDCT3pt0esT4g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "2.1.9", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@vitest/snapshot/-/snapshot-2.1.9.tgz", + "integrity": "sha512-oBO82rEjsxLNJincVhLhaxxZdEtV0EFHMK5Kmx5sJ6H9L183dHECjiefOAdnqpIgT5eZwT04PoggUnW88vOBNQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "magic-string": "^0.30.12", + "pathe": "^1.1.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@vitest/spy/-/spy-2.1.9.tgz", + "integrity": "sha512-E1B35FwzXXTs9FHNK6bDszs7mtydNi5MIfUWpceJ8Xbfb1gBMscAnwLbEu+B44ed6W3XjL9/ehLPHR1fkf1KLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyspy": "^3.0.2" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/utils": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/@vitest/utils/-/utils-2.1.9.tgz", + "integrity": "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "2.1.9", + "loupe": "^3.1.2", + "tinyrainbow": "^1.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/abstract-logging": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/abstract-logging/-/abstract-logging-2.0.1.tgz", + "integrity": "sha512-2BjRTZxTPvheOvGbBslFSYOUkr+SjPtOnrLP33f+VIWLzezQpZcqVg7ja3L4dBXmzzgwT+a029jRx5PCi3JuiA==", + "license": "MIT" + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmmirror.com/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv/node_modules/fast-uri": { + "version": "3.1.6", + "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.6.tgz", + "integrity": "sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/assertion-error": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/assertion-error/-/assertion-error-2.0.1.tgz", + "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + } + }, + "node_modules/atomic-sleep": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/atomic-sleep/-/atomic-sleep-1.0.0.tgz", + "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/avvio": { + "version": "8.4.0", + "resolved": "https://registry.npmmirror.com/avvio/-/avvio-8.4.0.tgz", + "integrity": "sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==", + "license": "MIT", + "dependencies": { + "@fastify/error": "^3.3.0", + "fastq": "^1.17.1" + } + }, + "node_modules/cac": { + "version": "6.7.14", + "resolved": "https://registry.npmmirror.com/cac/-/cac-6.7.14.tgz", + "integrity": "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/chai": { + "version": "5.3.3", + "resolved": "https://registry.npmmirror.com/chai/-/chai-5.3.3.tgz", + "integrity": "sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "assertion-error": "^2.0.1", + "check-error": "^2.1.1", + "deep-eql": "^5.0.1", + "loupe": "^3.1.0", + "pathval": "^2.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/check-error": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/check-error/-/check-error-2.1.3.tgz", + "integrity": "sha512-PAJdDJusoxnwm1VwW07VWwUN1sl7smmC3OKggvndJFadxxDRyFJBX/ggnu/KE4kQAB7a3Dp8f/YXC1FlUprWmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 16" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-eql": { + "version": "5.0.2", + "resolved": "https://registry.npmmirror.com/deep-eql/-/deep-eql-5.0.2.tgz", + "integrity": "sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.28.2", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.28.2.tgz", + "integrity": "sha512-HKVLS8dvII+xoKW9kmqxbRKrnWEXfJJr/FZhhJmiqIB0e053QNYFqOBouTMO/k5sID4MvCiUCvv8b9M4h32wIA==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@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/estree-walker": { + "version": "3.0.3", + "resolved": "https://registry.npmmirror.com/estree-walker/-/estree-walker-3.0.3.tgz", + "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "^1.0.0" + } + }, + "node_modules/expect-type": { + "version": "1.4.0", + "resolved": "https://registry.npmmirror.com/expect-type/-/expect-type-1.4.0.tgz", + "integrity": "sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/fast-content-type-parse": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", + "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==", + "license": "MIT" + }, + "node_modules/fast-decode-uri-component": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", + "integrity": "sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==", + "license": "MIT" + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "license": "MIT" + }, + "node_modules/fast-json-stringify": { + "version": "5.16.1", + "resolved": "https://registry.npmmirror.com/fast-json-stringify/-/fast-json-stringify-5.16.1.tgz", + "integrity": "sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==", + "license": "MIT", + "dependencies": { + "@fastify/merge-json-schemas": "^0.1.0", + "ajv": "^8.10.0", + "ajv-formats": "^3.0.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^2.1.0", + "json-schema-ref-resolver": "^1.0.1", + "rfdc": "^1.2.0" + } + }, + "node_modules/fast-json-stringify/node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "license": "MIT", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/fast-querystring": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/fast-querystring/-/fast-querystring-1.1.2.tgz", + "integrity": "sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==", + "license": "MIT", + "dependencies": { + "fast-decode-uri-component": "^1.0.1" + } + }, + "node_modules/fast-uri": { + "version": "2.4.5", + "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-2.4.5.tgz", + "integrity": "sha512-k9Nq2gfGrPow/wJcIQ+dbhLqSTdHx2eH+e6IAjAam0Xs9W5Uda8eXu6Ns73byuuyYvIXYnEJ9ziARtlXm9opgw==", + "license": "MIT" + }, + "node_modules/fastify": { + "version": "4.29.1", + "resolved": "https://registry.npmmirror.com/fastify/-/fastify-4.29.1.tgz", + "integrity": "sha512-m2kMNHIG92tSNWv+Z3UeTR9AWLLuo7KctC7mlFPtMEVrfjIhmQhkQnT9v15qA/BfVq3vvj134Y0jl9SBje3jXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/ajv-compiler": "^3.5.0", + "@fastify/error": "^3.4.0", + "@fastify/fast-json-stringify-compiler": "^4.3.0", + "abstract-logging": "^2.0.1", + "avvio": "^8.3.0", + "fast-content-type-parse": "^1.1.0", + "fast-json-stringify": "^5.8.0", + "find-my-way": "^8.0.0", + "light-my-request": "^5.11.0", + "pino": "^9.0.0", + "process-warning": "^3.0.0", + "proxy-addr": "^2.0.7", + "rfdc": "^1.3.0", + "secure-json-parse": "^2.7.0", + "semver": "^7.5.4", + "toad-cache": "^3.3.0" + } + }, + "node_modules/fastq": { + "version": "1.20.3", + "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/find-my-way": { + "version": "8.2.2", + "resolved": "https://registry.npmmirror.com/find-my-way/-/find-my-way-8.2.2.tgz", + "integrity": "sha512-Dobi7gcTEq8yszimcfp/R7+owiT4WncAJ7VTTgFH1jYJ5GaG1FbhjwDG820hptN0QDFvzVY3RfCzdInvGPGzjA==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-querystring": "^1.0.0", + "safe-regex2": "^3.1.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/json-schema-ref-resolver": { + "version": "1.0.1", + "resolved": "https://registry.npmmirror.com/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", + "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "license": "MIT" + }, + "node_modules/light-my-request": { + "version": "5.14.0", + "resolved": "https://registry.npmmirror.com/light-my-request/-/light-my-request-5.14.0.tgz", + "integrity": "sha512-aORPWntbpH5esaYpGOOmri0OHDOe3wC5M2MQxZ9dvMLZm6DnaAn0kJlcbU9hwsQgLzmZyReKwFwwPkR+nHu5kA==", + "license": "BSD-3-Clause", + "dependencies": { + "cookie": "^0.7.0", + "process-warning": "^3.0.0", + "set-cookie-parser": "^2.4.1" + } + }, + "node_modules/loupe": { + "version": "3.2.1", + "resolved": "https://registry.npmmirror.com/loupe/-/loupe-3.2.1.tgz", + "integrity": "sha512-CdzqowRJCeLU72bHvWqwRBBlLcMEtIvGrlvef74kMnV2AolS9Y8xUv1I0U/MNAWMhBlKIoyuEgoJ0t/bbwHbLQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmmirror.com/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.18", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.18.tgz", + "integrity": "sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/on-exit-leak-free": { + "version": "2.1.2", + "resolved": "https://registry.npmmirror.com/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", + "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/pathe": { + "version": "1.1.2", + "resolved": "https://registry.npmmirror.com/pathe/-/pathe-1.1.2.tgz", + "integrity": "sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/pathval": { + "version": "2.0.1", + "resolved": "https://registry.npmmirror.com/pathval/-/pathval-2.0.1.tgz", + "integrity": "sha512-//nshmD55c46FuFw26xV/xFAaB5HF9Xdap7HJBBnrKdAd6/GxDBaNA1870O79+9ueg61cZLSVc+OaFlfmObYVQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.16" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/pino": { + "version": "9.14.0", + "resolved": "https://registry.npmmirror.com/pino/-/pino-9.14.0.tgz", + "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", + "license": "MIT", + "dependencies": { + "@pinojs/redact": "^0.4.0", + "atomic-sleep": "^1.0.0", + "on-exit-leak-free": "^2.1.0", + "pino-abstract-transport": "^2.0.0", + "pino-std-serializers": "^7.0.0", + "process-warning": "^5.0.0", + "quick-format-unescaped": "^4.0.3", + "real-require": "^0.2.0", + "safe-stable-stringify": "^2.3.1", + "sonic-boom": "^4.0.1", + "thread-stream": "^3.0.0" + }, + "bin": { + "pino": "bin.js" + } + }, + "node_modules/pino-abstract-transport": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", + "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", + "license": "MIT", + "dependencies": { + "split2": "^4.0.0" + } + }, + "node_modules/pino-std-serializers": { + "version": "7.1.0", + "resolved": "https://registry.npmmirror.com/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", + "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", + "license": "MIT" + }, + "node_modules/pino/node_modules/process-warning": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/process-warning/-/process-warning-5.1.0.tgz", + "integrity": "sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, + "node_modules/postcss": { + "version": "8.5.26", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.26.tgz", + "integrity": "sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.17", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/process-warning": { + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/process-warning/-/process-warning-3.0.0.tgz", + "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/quick-format-unescaped": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", + "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", + "license": "MIT" + }, + "node_modules/real-require": { + "version": "0.2.0", + "resolved": "https://registry.npmmirror.com/real-require/-/real-require-0.2.0.tgz", + "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", + "license": "MIT", + "engines": { + "node": ">= 12.13.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmmirror.com/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ret": { + "version": "0.4.3", + "resolved": "https://registry.npmmirror.com/ret/-/ret-0.4.3.tgz", + "integrity": "sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmmirror.com/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, + "node_modules/rollup": { + "version": "4.63.1", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.63.1.tgz", + "integrity": "sha512-3Df9jsstwhccuEfmAMi9l8XUh/GOkVObmFTU7CCVBysEbcOZLl84jCtaAZMcPiMz2EGKsATzQcU+Xr3n/wU6cg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.9" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@napi-rs/lzma-linux-x64-gnu": "1.5.1", + "@rollup/rollup-android-arm-eabi": "4.63.1", + "@rollup/rollup-android-arm64": "4.63.1", + "@rollup/rollup-darwin-arm64": "4.63.1", + "@rollup/rollup-darwin-x64": "4.63.1", + "@rollup/rollup-freebsd-arm64": "4.63.1", + "@rollup/rollup-freebsd-x64": "4.63.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.63.1", + "@rollup/rollup-linux-arm-musleabihf": "4.63.1", + "@rollup/rollup-linux-arm64-gnu": "4.63.1", + "@rollup/rollup-linux-arm64-musl": "4.63.1", + "@rollup/rollup-linux-loong64-gnu": "4.63.1", + "@rollup/rollup-linux-loong64-musl": "4.63.1", + "@rollup/rollup-linux-ppc64-gnu": "4.63.1", + "@rollup/rollup-linux-ppc64-musl": "4.63.1", + "@rollup/rollup-linux-riscv64-gnu": "4.63.1", + "@rollup/rollup-linux-riscv64-musl": "4.63.1", + "@rollup/rollup-linux-s390x-gnu": "4.63.1", + "@rollup/rollup-linux-x64-gnu": "4.63.1", + "@rollup/rollup-linux-x64-musl": "4.63.1", + "@rollup/rollup-openbsd-x64": "4.63.1", + "@rollup/rollup-openharmony-arm64": "4.63.1", + "@rollup/rollup-win32-arm64-msvc": "4.63.1", + "@rollup/rollup-win32-ia32-msvc": "4.63.1", + "@rollup/rollup-win32-x64-gnu": "4.63.1", + "@rollup/rollup-win32-x64-msvc": "4.63.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/safe-regex2": { + "version": "3.1.0", + "resolved": "https://registry.npmmirror.com/safe-regex2/-/safe-regex2-3.1.0.tgz", + "integrity": "sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==", + "license": "MIT", + "dependencies": { + "ret": "~0.4.0" + } + }, + "node_modules/safe-stable-stringify": { + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", + "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "node_modules/secure-json-parse": { + "version": "2.7.0", + "resolved": "https://registry.npmmirror.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz", + "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "license": "BSD-3-Clause" + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmmirror.com/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/set-cookie-parser": { + "version": "2.7.2", + "resolved": "https://registry.npmmirror.com/set-cookie-parser/-/set-cookie-parser-2.7.2.tgz", + "integrity": "sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==", + "license": "MIT" + }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmmirror.com/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, + "node_modules/sonic-boom": { + "version": "4.2.1", + "resolved": "https://registry.npmmirror.com/sonic-boom/-/sonic-boom-4.2.1.tgz", + "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", + "license": "MIT", + "dependencies": { + "atomic-sleep": "^1.0.0" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/split2": { + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/split2/-/split2-4.2.0.tgz", + "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", + "license": "ISC", + "engines": { + "node": ">= 10.x" + } + }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmmirror.com/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmmirror.com/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "node_modules/thread-stream": { + "version": "3.2.0", + "resolved": "https://registry.npmmirror.com/thread-stream/-/thread-stream-3.2.0.tgz", + "integrity": "sha512-zLBvqpwr4Esa0kRjcrzGU6zL25lePWaCLMx0RQFrmteozIfeNdaMLpG5U7PeHzvlFkAWaRKA9/KVW4F60iB+qw==", + "license": "MIT", + "dependencies": { + "real-require": "^0.2.0" + } + }, + "node_modules/tinybench": { + "version": "2.9.0", + "resolved": "https://registry.npmmirror.com/tinybench/-/tinybench-2.9.0.tgz", + "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinyexec": { + "version": "0.3.2", + "resolved": "https://registry.npmmirror.com/tinyexec/-/tinyexec-0.3.2.tgz", + "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tinypool": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/tinypool/-/tinypool-1.1.1.tgz", + "integrity": "sha512-Zba82s87IFq9A9XmjiX5uZA/ARWDrB03OHlq+Vw1fSdt0I+4/Kutwy8BP4Y/y/aORMo61FQ0vIb5j44vSo5Pkg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.0.0 || >=20.0.0" + } + }, + "node_modules/tinyrainbow": { + "version": "1.2.0", + "resolved": "https://registry.npmmirror.com/tinyrainbow/-/tinyrainbow-1.2.0.tgz", + "integrity": "sha512-weEDEq7Z5eTHPDh4xjX789+fHfF+P8boiFB+0vbWzpbnbsEr/GRaohi/uMKxg8RZMXnl1ItAi/IUHWMsjDV7kQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/tinyspy": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/tinyspy/-/tinyspy-3.0.2.tgz", + "integrity": "sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/toad-cache": { + "version": "3.7.4", + "resolved": "https://registry.npmmirror.com/toad-cache/-/toad-cache-3.7.4.tgz", + "integrity": "sha512-m1TdR/rvT7kgGJZhspNtXdsdYk0fddFpJJFlG5s+UkPFo6lkLoZ3YLOaovPYjq1R75NP5JfeTlSHaOsE09peCg==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/tsx": { + "version": "4.23.13", + "resolved": "https://registry.npmmirror.com/tsx/-/tsx-4.23.13.tgz", + "integrity": "sha512-BL5MGkRln6aDYhb0xbQlEAGw743BaZYWdbWtdJOBriYJboKgUUYCadFp2/FpBBZquBC/ezNBn7wMMPx7FDZUDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmmirror.com/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmmirror.com/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-node": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/vite-node/-/vite-node-2.1.9.tgz", + "integrity": "sha512-AM9aQ/IPrW/6ENLQg3AGY4K1N2TGZdR5e4gu/MmmR2xR3Ll1+dib+nook92g4TV3PXVyeyxdWwtaCAiUL0hMxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cac": "^6.7.14", + "debug": "^4.3.7", + "es-module-lexer": "^1.5.4", + "pathe": "^1.1.2", + "vite": "^5.0.0" + }, + "bin": { + "vite-node": "vite-node.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/vite/node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/vite/node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/vitest": { + "version": "2.1.9", + "resolved": "https://registry.npmmirror.com/vitest/-/vitest-2.1.9.tgz", + "integrity": "sha512-MSmPM9REYqDGBI8439mA4mWhV5sKmDlBKWIYbA3lRb2PTHACE0mgKwA8yQ2xq9vxDTuk4iPrECBAEW2aoFXY0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "2.1.9", + "@vitest/mocker": "2.1.9", + "@vitest/pretty-format": "^2.1.9", + "@vitest/runner": "2.1.9", + "@vitest/snapshot": "2.1.9", + "@vitest/spy": "2.1.9", + "@vitest/utils": "2.1.9", + "chai": "^5.1.2", + "debug": "^4.3.7", + "expect-type": "^1.1.0", + "magic-string": "^0.30.12", + "pathe": "^1.1.2", + "std-env": "^3.8.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.1", + "tinypool": "^1.0.1", + "tinyrainbow": "^1.2.0", + "vite": "^5.0.0", + "vite-node": "2.1.9", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/node": "^18.0.0 || >=20.0.0", + "@vitest/browser": "2.1.9", + "@vitest/ui": "2.1.9", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmmirror.com/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, + "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, + "bin": { + "why-is-node-running": "cli.js" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/zod": { + "version": "3.25.76", + "resolved": "https://registry.npmmirror.com/zod/-/zod-3.25.76.tgz", + "integrity": "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + } +} diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/package.json b/submissions/mcp-hackathon/visioneer-gaspulse/source/package.json new file mode 100644 index 0000000..9878bc3 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/package.json @@ -0,0 +1,27 @@ +{ + "name": "gaspulse", + "version": "0.1.0", + "private": true, + "type": "module", + "engines": { + "node": ">=18.17" + }, + "scripts": { + "dev": "tsx watch src/server.ts", + "build": "tsc -p tsconfig.json", + "start": "node dist/server.js", + "test": "vitest run", + "lint": "tsc -p tsconfig.json --noEmit" + }, + "dependencies": { + "dotenv": "^16.4.5", + "fastify": "^4.28.1", + "zod": "^3.23.8" + }, + "devDependencies": { + "@types/node": "^20.14.15", + "tsx": "^4.16.5", + "typescript": "^5.5.4", + "vitest": "^2.0.5" + } +} diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/config.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/config.ts new file mode 100644 index 0000000..958f000 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/config.ts @@ -0,0 +1,5 @@ +export const GIT_COMMIT = process.env.GIT_COMMIT ?? "unknown"; +export const SUBMISSION_SLUG = "visioneer-gaspulse"; +export const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY ?? ""; +export const PORT = Number(process.env.PORT ?? 8080); +export const CACHE_TTL_MS = 60_000; diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/activityScore.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/activityScore.ts new file mode 100644 index 0000000..bc8d969 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/activityScore.ts @@ -0,0 +1,61 @@ +import type { ActivityScore, EtherscanTx } from "../types.js"; + +const DAY_MS = 86_400_000; +// One transaction every 3 days over the window earns full frequency credit. +const EXPECTED_TX_INTERVAL_DAYS = 3; + +/** + * Pure activity/liveliness signal — recency, frequency, and consistency of on-chain + * transactions. This is deliberately NOT a risk, fraud, or compliance score: it says nothing + * about an address's trustworthiness, only how recently and regularly it has transacted. + */ +export function computeActivityScore(transactions: EtherscanTx[], windowDays: number, now: Date): ActivityScore { + const windowStart = now.getTime() - windowDays * DAY_MS; + const inWindowMs = transactions + .filter((tx) => Number(tx.timeStamp) * 1000 >= windowStart && tx.isError === "0") + .map((tx) => Number(tx.timeStamp) * 1000) + .sort((a, b) => a - b); + + if (inWindowMs.length === 0) { + return { value: 0, recency: 0, frequency: 0, consistency: 0 }; + } + + const lastTxMs = inWindowMs[inWindowMs.length - 1]; + const daysSinceLastTx = (now.getTime() - lastTxMs) / DAY_MS; + const recency = clamp(100 * (1 - daysSinceLastTx / windowDays), 0, 100); + + const expectedTxCount = windowDays / EXPECTED_TX_INTERVAL_DAYS; + const frequency = clamp((100 * inWindowMs.length) / expectedTxCount, 0, 100); + + const consistency = computeConsistency(inWindowMs); + + const value = Math.round(0.4 * recency + 0.4 * frequency + 0.2 * consistency); + + return { + value: clamp(value, 0, 100), + recency: Math.round(recency), + frequency: Math.round(frequency), + consistency: Math.round(consistency), + }; +} + +function computeConsistency(sortedTimestampsMs: number[]): number { + if (sortedTimestampsMs.length < 3) return 0; + + const gapsDays: number[] = []; + for (let i = 1; i < sortedTimestampsMs.length; i += 1) { + gapsDays.push((sortedTimestampsMs[i] - sortedTimestampsMs[i - 1]) / DAY_MS); + } + + const mean = gapsDays.reduce((sum, g) => sum + g, 0) / gapsDays.length; + if (mean === 0) return 100; + + const variance = gapsDays.reduce((sum, g) => sum + (g - mean) ** 2, 0) / gapsDays.length; + const coefficientOfVariation = Math.sqrt(variance) / mean; + + return clamp(100 * (1 - Math.min(1, coefficientOfVariation)), 0, 100); +} + +function clamp(value: number, min: number, max: number): number { + return Math.min(max, Math.max(min, value)); +} diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/etherscan.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/etherscan.ts new file mode 100644 index 0000000..638b0d1 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/etherscan.ts @@ -0,0 +1,62 @@ +import type { EtherscanTx } from "../types.js"; + +const ETHERSCAN_BASE_URL = "https://api.etherscan.io/v2/api"; +const CHAIN_ID_ETHEREUM_MAINNET = 1; +const REQUEST_TIMEOUT_MS = 8_000; +const MAX_TRANSACTIONS = 1000; + +export class EtherscanError extends Error { + constructor( + message: string, + public readonly kind: "rate_limited" | "unavailable", + ) { + super(message); + } +} + +interface EtherscanListResponse { + status: string; + message: string; + result: EtherscanTx[] | string; +} + +export async function fetchTransactions(address: string, apiKey: string): Promise { + const url = new URL(ETHERSCAN_BASE_URL); + url.searchParams.set("chainid", String(CHAIN_ID_ETHEREUM_MAINNET)); + url.searchParams.set("module", "account"); + url.searchParams.set("action", "txlist"); + url.searchParams.set("address", address); + url.searchParams.set("startblock", "0"); + url.searchParams.set("endblock", "99999999"); + url.searchParams.set("page", "1"); + url.searchParams.set("offset", String(MAX_TRANSACTIONS)); + url.searchParams.set("sort", "desc"); + url.searchParams.set("apikey", apiKey); + + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS); + let response: Response; + try { + response = await fetch(url, { signal: controller.signal }); + } catch (error) { + throw new EtherscanError(`Etherscan request failed: ${(error as Error).message}`, "unavailable"); + } finally { + clearTimeout(timeout); + } + + if (response.status === 429) { + throw new EtherscanError("Etherscan rate limit exceeded", "rate_limited"); + } + if (!response.ok) { + throw new EtherscanError(`Etherscan responded with HTTP ${response.status}`, "unavailable"); + } + + const body = (await response.json()) as EtherscanListResponse; + if (body.status === "0") { + if (typeof body.result === "string" && /rate limit/i.test(body.result)) { + throw new EtherscanError(body.result, "rate_limited"); + } + return []; + } + return Array.isArray(body.result) ? body.result : []; +} diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/gasTrend.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/gasTrend.ts new file mode 100644 index 0000000..cfeea08 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/gasTrend.ts @@ -0,0 +1,71 @@ +import type { EtherscanTx, GasBucket, GasTrend } from "../types.js"; + +const BUCKET_DAYS = 7; +const DAY_MS = 86_400_000; +// Below this fractional change between the first and last active bucket, the trend reads as flat. +const TREND_THRESHOLD = 0.05; + +export function computeGasTrend(transactions: EtherscanTx[], windowDays: number, now: Date): GasTrend { + const windowStart = now.getTime() - windowDays * DAY_MS; + const inWindow = transactions.filter( + (tx) => Number(tx.timeStamp) * 1000 >= windowStart && tx.isError === "0", + ); + + if (inWindow.length === 0) { + return { totalGasUsed: "0", avgGasPriceGwei: 0, direction: "insufficient_data", buckets: [] }; + } + + const bucketCount = Math.max(1, Math.ceil(windowDays / BUCKET_DAYS)); + const buckets: GasBucket[] = []; + let totalGasUsed = 0n; + let totalGasPriceWei = 0n; + + for (let i = bucketCount - 1; i >= 0; i -= 1) { + const periodEndMs = now.getTime() - i * BUCKET_DAYS * DAY_MS; + const periodStartMs = periodEndMs - BUCKET_DAYS * DAY_MS; + const bucketTxs = inWindow.filter((tx) => { + const ts = Number(tx.timeStamp) * 1000; + return ts >= periodStartMs && ts < periodEndMs; + }); + + let bucketGasUsed = 0n; + let bucketGasPriceWei = 0n; + for (const tx of bucketTxs) { + bucketGasUsed += BigInt(tx.gasUsed); + bucketGasPriceWei += BigInt(tx.gasPrice); + } + totalGasUsed += bucketGasUsed; + totalGasPriceWei += bucketGasPriceWei; + + buckets.push({ + periodStart: new Date(periodStartMs).toISOString(), + periodEnd: new Date(periodEndMs).toISOString(), + txCount: bucketTxs.length, + gasUsed: bucketGasUsed.toString(), + avgGasPriceGwei: bucketTxs.length > 0 ? weiToGwei(bucketGasPriceWei / BigInt(bucketTxs.length)) : 0, + }); + } + + const avgGasPriceGwei = weiToGwei(totalGasPriceWei / BigInt(inWindow.length)); + const direction = computeDirection(buckets); + + return { totalGasUsed: totalGasUsed.toString(), avgGasPriceGwei, direction, buckets }; +} + +function computeDirection(buckets: GasBucket[]): GasTrend["direction"] { + const active = buckets.filter((b) => b.txCount > 0); + if (active.length < 2) return "insufficient_data"; + + const first = active[0].avgGasPriceGwei; + const last = active[active.length - 1].avgGasPriceGwei; + if (first === 0) return "insufficient_data"; + + const change = (last - first) / first; + if (change > TREND_THRESHOLD) return "increasing"; + if (change < -TREND_THRESHOLD) return "decreasing"; + return "flat"; +} + +function weiToGwei(wei: bigint): number { + return Math.round((Number(wei) / 1e9) * 100) / 100; +} diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/activity.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/activity.ts new file mode 100644 index 0000000..4d9a61c --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/activity.ts @@ -0,0 +1,88 @@ +import type { FastifyInstance } from "fastify"; +import { z } from "zod"; +import { ETHERSCAN_API_KEY, CACHE_TTL_MS } from "../config.js"; +import { fetchTransactions, EtherscanError } from "../lib/etherscan.js"; +import { computeGasTrend } from "../lib/gasTrend.js"; +import { computeActivityScore } from "../lib/activityScore.js"; +import type { ActivityResponse, EtherscanTx } from "../types.js"; + +const paramsSchema = z.object({ + address: z.string().regex(/^0x[0-9a-fA-F]{40}$/), +}); + +const querySchema = z.object({ + windowDays: z.coerce.number().int().min(1).max(180).default(30), +}); + +const transactionCache = new Map(); + +export async function registerActivityRoute(app: FastifyInstance): Promise { + app.get("/v1/address/:address/activity", async (request, reply) => { + const paramsResult = paramsSchema.safeParse(request.params); + if (!paramsResult.success) { + return reply.code(400).send({ + error: { + code: "invalid_address", + message: "address must be a 0x-prefixed 40-hex-character Ethereum address", + }, + }); + } + + const queryResult = querySchema.safeParse(request.query); + if (!queryResult.success) { + return reply.code(400).send({ + error: { code: "invalid_window", message: "windowDays must be an integer between 1 and 180" }, + }); + } + + const address = paramsResult.data.address.toLowerCase(); + const { windowDays } = queryResult.data; + + let transactions: EtherscanTx[]; + try { + transactions = await getTransactions(address); + } catch (error) { + if (error instanceof EtherscanError && error.kind === "rate_limited") { + return reply.code(429).send({ + error: { code: "upstream_rate_limited", message: "Etherscan rate limit exceeded, retry shortly" }, + }); + } + request.log.error(error); + return reply.code(502).send({ + error: { code: "upstream_unavailable", message: "the upstream data source is unavailable" }, + }); + } + + const now = new Date(); + const windowStartMs = now.getTime() - windowDays * 86_400_000; + const inWindow = transactions.filter((tx) => Number(tx.timeStamp) * 1000 >= windowStartMs); + const gasTrend = computeGasTrend(transactions, windowDays, now); + const activityScore = computeActivityScore(transactions, windowDays, now); + + const allTimestampsMs = transactions.map((tx) => Number(tx.timeStamp) * 1000); + const response: ActivityResponse = { + address, + network: "ethereum-mainnet", + windowDays, + asOf: now.toISOString(), + txCount: inWindow.length, + firstSeen: allTimestampsMs.length > 0 ? new Date(Math.min(...allTimestampsMs)).toISOString() : null, + lastSeen: allTimestampsMs.length > 0 ? new Date(Math.max(...allTimestampsMs)).toISOString() : null, + gasTrend, + activityScore, + dataSource: "etherscan", + }; + + return response; + }); +} + +async function getTransactions(address: string): Promise { + const cached = transactionCache.get(address); + if (cached && cached.expiresAt > Date.now()) { + return cached.transactions; + } + const transactions = await fetchTransactions(address, ETHERSCAN_API_KEY); + transactionCache.set(address, { expiresAt: Date.now() + CACHE_TTL_MS, transactions }); + return transactions; +} diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/health.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/health.ts new file mode 100644 index 0000000..0280a31 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/health.ts @@ -0,0 +1,6 @@ +import type { FastifyInstance } from "fastify"; +import { GIT_COMMIT } from "../config.js"; + +export async function registerHealthRoute(app: FastifyInstance): Promise { + app.get("/health", async () => ({ status: "ok", commit: GIT_COMMIT })); +} diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/verification.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/verification.ts new file mode 100644 index 0000000..0f8bf5e --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/verification.ts @@ -0,0 +1,10 @@ +import type { FastifyInstance } from "fastify"; +import { GIT_COMMIT, SUBMISSION_SLUG } from "../config.js"; + +export async function registerVerificationRoute(app: FastifyInstance): Promise { + app.get("/.well-known/xagent-verification.json", async () => ({ + schemaVersion: 1, + slug: SUBMISSION_SLUG, + commit: GIT_COMMIT, + })); +} diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/server.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/server.ts new file mode 100644 index 0000000..69eb8b6 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/server.ts @@ -0,0 +1,34 @@ +import "dotenv/config"; +import Fastify from "fastify"; +import { PORT } from "./config.js"; +import { registerHealthRoute } from "./routes/health.js"; +import { registerVerificationRoute } from "./routes/verification.js"; +import { registerActivityRoute } from "./routes/activity.js"; + +export async function buildServer() { + const app = Fastify({ logger: true }); + + await registerHealthRoute(app); + await registerVerificationRoute(app); + await registerActivityRoute(app); + + app.setNotFoundHandler((_request, reply) => { + reply.code(404).send({ error: { code: "not_found", message: "no route matches this path" } }); + }); + + return app; +} + +async function main() { + const app = await buildServer(); + try { + await app.listen({ port: PORT, host: "0.0.0.0" }); + } catch (error) { + app.log.error(error); + process.exit(1); + } +} + +if (process.env.VITEST !== "true") { + void main(); +} diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/types.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/types.ts new file mode 100644 index 0000000..c6f9fb7 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/types.ts @@ -0,0 +1,50 @@ +export interface GasBucket { + periodStart: string; + periodEnd: string; + txCount: number; + gasUsed: string; + avgGasPriceGwei: number; +} + +export interface GasTrend { + totalGasUsed: string; + avgGasPriceGwei: number; + direction: "increasing" | "decreasing" | "flat" | "insufficient_data"; + buckets: GasBucket[]; +} + +export interface ActivityScore { + value: number; + recency: number; + frequency: number; + consistency: number; +} + +export interface ActivityResponse { + address: string; + network: "ethereum-mainnet"; + windowDays: number; + asOf: string; + txCount: number; + firstSeen: string | null; + lastSeen: string | null; + gasTrend: GasTrend; + activityScore: ActivityScore; + dataSource: "etherscan"; +} + +export interface EtherscanTx { + hash: string; + timeStamp: string; + gas: string; + gasPrice: string; + gasUsed: string; + isError: string; +} + +export interface ApiError { + error: { + code: string; + message: string; + }; +} diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/test/activity.route.test.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/test/activity.route.test.ts new file mode 100644 index 0000000..51abafa --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/test/activity.route.test.ts @@ -0,0 +1,93 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import type { FastifyInstance } from "fastify"; +import { buildServer } from "../src/server.js"; + +// Distinct addresses per test: the route caches Etherscan responses per address for 60s, +// so reusing one address across tests would return stale cached data instead of the mock. +const ADDR_MALFORMED_WINDOW = `0x${"5".repeat(40)}`; +const ADDR_EMPTY = `0x${"1".repeat(40)}`; +const ADDR_ACTIVE = `0x${"2".repeat(40)}`; +const ADDR_UNAVAILABLE = `0x${"3".repeat(40)}`; +const ADDR_RATE_LIMITED = `0x${"4".repeat(40)}`; + +function etherscanResponse(result: unknown) { + return new Response(JSON.stringify({ status: "1", message: "OK", result }), { + status: 200, + headers: { "content-type": "application/json" }, + }); +} + +describe("GET /v1/address/:address/activity", () => { + let app: FastifyInstance; + + beforeEach(async () => { + app = await buildServer(); + }); + + afterEach(async () => { + await app.close(); + vi.restoreAllMocks(); + }); + + it("rejects a malformed address with 400", async () => { + const response = await app.inject({ method: "GET", url: "/v1/address/not-an-address/activity" }); + expect(response.statusCode).toBe(400); + expect(response.json().error.code).toBe("invalid_address"); + }); + + it("rejects an out-of-range windowDays with 400", async () => { + const response = await app.inject({ + method: "GET", + url: `/v1/address/${ADDR_MALFORMED_WINDOW}/activity?windowDays=999`, + }); + expect(response.statusCode).toBe(400); + expect(response.json().error.code).toBe("invalid_window"); + }); + + it("returns a zeroed response for an address with no transactions", async () => { + vi.stubGlobal("fetch", vi.fn().mockResolvedValue(etherscanResponse([]))); + + const response = await app.inject({ method: "GET", url: `/v1/address/${ADDR_EMPTY}/activity` }); + expect(response.statusCode).toBe(200); + const body = response.json(); + expect(body.txCount).toBe(0); + expect(body.activityScore).toEqual({ value: 0, recency: 0, frequency: 0, consistency: 0 }); + expect(body.gasTrend.direction).toBe("insufficient_data"); + }); + + it("computes activity from returned transactions", async () => { + const now = Math.floor(Date.now() / 1000); + const sampleTx = { + hash: "0xabc", + timeStamp: String(now - 86_400), + gas: "21000", + gasPrice: "10000000000", + gasUsed: "21000", + isError: "0", + }; + vi.stubGlobal("fetch", vi.fn().mockResolvedValue(etherscanResponse([sampleTx]))); + + const response = await app.inject({ method: "GET", url: `/v1/address/${ADDR_ACTIVE}/activity` }); + expect(response.statusCode).toBe(200); + const body = response.json(); + expect(body.txCount).toBe(1); + expect(body.dataSource).toBe("etherscan"); + expect(body.address).toBe(ADDR_ACTIVE.toLowerCase()); + }); + + it("returns 502 when the upstream is unavailable", async () => { + vi.stubGlobal("fetch", vi.fn().mockResolvedValue(new Response("boom", { status: 500 }))); + + const response = await app.inject({ method: "GET", url: `/v1/address/${ADDR_UNAVAILABLE}/activity` }); + expect(response.statusCode).toBe(502); + expect(response.json().error.code).toBe("upstream_unavailable"); + }); + + it("returns 429 when the upstream rate-limits", async () => { + vi.stubGlobal("fetch", vi.fn().mockResolvedValue(new Response("too many", { status: 429 }))); + + const response = await app.inject({ method: "GET", url: `/v1/address/${ADDR_RATE_LIMITED}/activity` }); + expect(response.statusCode).toBe(429); + expect(response.json().error.code).toBe("upstream_rate_limited"); + }); +}); diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/test/activityScore.test.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/test/activityScore.test.ts new file mode 100644 index 0000000..bea0da0 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/test/activityScore.test.ts @@ -0,0 +1,59 @@ +import { describe, expect, it } from "vitest"; +import { computeActivityScore } from "../src/lib/activityScore.js"; +import type { EtherscanTx } from "../src/types.js"; + +const NOW = new Date("2026-09-02T00:00:00.000Z"); +const DAY_S = 86_400; + +function tx(daysAgo: number): EtherscanTx { + return { + hash: `0x${daysAgo}`, + timeStamp: String(Math.floor(NOW.getTime() / 1000) - daysAgo * DAY_S), + gas: "21000", + gasPrice: "10000000000", + gasUsed: "21000", + isError: "0", + }; +} + +describe("computeActivityScore", () => { + it("returns all zeros for an address with no in-window activity", () => { + const result = computeActivityScore([], 30, NOW); + expect(result).toEqual({ value: 0, recency: 0, frequency: 0, consistency: 0 }); + }); + + it("ignores failed transactions", () => { + const failed = { ...tx(1), isError: "1" }; + const result = computeActivityScore([failed], 30, NOW); + expect(result.value).toBe(0); + }); + + it("scores a regularly active address highly", () => { + const transactions = Array.from({ length: 10 }, (_, i) => tx(i * 3)); + const result = computeActivityScore(transactions, 30, NOW); + expect(result.recency).toBeGreaterThanOrEqual(90); + expect(result.frequency).toBeGreaterThanOrEqual(90); + expect(result.consistency).toBeGreaterThanOrEqual(90); + expect(result.value).toBeGreaterThanOrEqual(90); + }); + + it("scores low recency for an address whose last transaction was long ago in the window", () => { + const result = computeActivityScore([tx(29)], 30, NOW); + expect(result.recency).toBeLessThanOrEqual(10); + }); + + it("caps frequency at 100 even with far more than the expected transaction count", () => { + const transactions = Array.from({ length: 40 }, (_, i) => tx(i)); + const result = computeActivityScore(transactions, 30, NOW); + expect(result.frequency).toBe(100); + }); + + it("always returns values clamped between 0 and 100", () => { + const transactions = [tx(0), tx(15), tx(29)]; + const result = computeActivityScore(transactions, 30, NOW); + for (const value of Object.values(result)) { + expect(value).toBeGreaterThanOrEqual(0); + expect(value).toBeLessThanOrEqual(100); + } + }); +}); diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/test/gasTrend.test.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/test/gasTrend.test.ts new file mode 100644 index 0000000..6a708ce --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/test/gasTrend.test.ts @@ -0,0 +1,63 @@ +import { describe, expect, it } from "vitest"; +import { computeGasTrend } from "../src/lib/gasTrend.js"; +import type { EtherscanTx } from "../src/types.js"; + +const NOW = new Date("2026-09-02T00:00:00.000Z"); +const DAY_S = 86_400; + +function tx(daysAgo: number, gasUsed: string, gasPriceGwei: number): EtherscanTx { + return { + hash: `0x${daysAgo}`, + timeStamp: String(Math.floor(NOW.getTime() / 1000) - daysAgo * DAY_S), + gas: gasUsed, + gasPrice: String(gasPriceGwei * 1e9), + gasUsed, + isError: "0", + }; +} + +describe("computeGasTrend", () => { + it("returns insufficient_data for no transactions", () => { + const result = computeGasTrend([], 30, NOW); + expect(result).toEqual({ + totalGasUsed: "0", + avgGasPriceGwei: 0, + direction: "insufficient_data", + buckets: [], + }); + }); + + it("excludes failed transactions and out-of-window transactions", () => { + const transactions = [ + tx(2, "21000", 10), + { ...tx(2, "21000", 10), isError: "1" }, + tx(60, "21000", 10), + ]; + const result = computeGasTrend(transactions, 30, NOW); + expect(result.totalGasUsed).toBe("21000"); + }); + + it("detects an increasing gas price trend", () => { + const transactions = [tx(27, "21000", 10), tx(1, "21000", 20)]; + const result = computeGasTrend(transactions, 30, NOW); + expect(result.direction).toBe("increasing"); + }); + + it("detects a decreasing gas price trend", () => { + const transactions = [tx(27, "21000", 20), tx(1, "21000", 10)]; + const result = computeGasTrend(transactions, 30, NOW); + expect(result.direction).toBe("decreasing"); + }); + + it("reports flat when gas price barely changes", () => { + const transactions = [tx(27, "21000", 10), tx(1, "21000", 10.2)]; + const result = computeGasTrend(transactions, 30, NOW); + expect(result.direction).toBe("flat"); + }); + + it("reports insufficient_data with only one active bucket", () => { + const transactions = [tx(1, "21000", 10), tx(2, "21000", 12)]; + const result = computeGasTrend(transactions, 30, NOW); + expect(result.direction).toBe("insufficient_data"); + }); +}); diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/tsconfig.json b/submissions/mcp-hackathon/visioneer-gaspulse/source/tsconfig.json new file mode 100644 index 0000000..c35bd02 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/tsconfig.json @@ -0,0 +1,15 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "outDir": "dist", + "rootDir": "src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true + }, + "include": ["src"] +} diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/submission.json b/submissions/mcp-hackathon/visioneer-gaspulse/submission.json new file mode 100644 index 0000000..7efe741 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/submission.json @@ -0,0 +1,10 @@ +{ + "schemaVersion": 1, + "name": "GasPulse", + "slug": "visioneer-gaspulse", + "sourceRepository": "https://github.com/muhammad-wei/gaspulse", + "reviewCommit": "0b5562319a3e1e96adeef5e1231bd4cbd2dd3030", + "apiBaseUrl": "https://api.gaspulse.win/v1", + "healthCheckUrl": "https://api.gaspulse.win/health", + "deploymentProofUrl": "https://api.gaspulse.win/.well-known/xagent-verification.json" +} diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/verification/README.md b/submissions/mcp-hackathon/visioneer-gaspulse/verification/README.md new file mode 100644 index 0000000..cee6f9c --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/verification/README.md @@ -0,0 +1,82 @@ +# Verification evidence + +## Prerequisites + +- Review commit: `0b5562319a3e1e96adeef5e1231bd4cbd2dd3030` +- API base URL: `https://api.gaspulse.win/v1` +- Authentication: none required — all endpoints are public. + +## 1. Health check + +```bash +curl --fail --silent --show-error https://api.gaspulse.win/health +``` + +Expected response: + +```json +{"status":"ok","commit":"0b5562319a3e1e96adeef5e1231bd4cbd2dd3030"} +``` + +## 2. Deployment proof + +```bash +curl --fail --silent --show-error https://api.gaspulse.win/.well-known/xagent-verification.json +``` + +Expected response: + +```json +{"schemaVersion":1,"slug":"visioneer-gaspulse","commit":"0b5562319a3e1e96adeef5e1231bd4cbd2dd3030"} +``` + +## 3. Capability call + +```bash +curl --fail --silent --show-error \ + "https://api.gaspulse.win/v1/address/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/activity?windowDays=30" +``` + +Expected success response shape (HTTP 200) — this reflects live on-chain data at call time, so +the exact numbers (`txCount`, `gasTrend`, `activityScore`) will differ on reproduction; the shape +and field semantics are what to verify, e.g.: + +```json +{ + "address": "0xd8da6bf26964af9d7eed9e03e53415d37aa96045", + "network": "ethereum-mainnet", + "windowDays": 30, + "asOf": "2026-09-03T05:34:13.053Z", + "txCount": 23, + "firstSeen": "2026-05-01T04:32:35.000Z", + "lastSeen": "2026-09-01T11:12:59.000Z", + "gasTrend": { + "totalGasUsed": "554287", + "avgGasPriceGwei": 3.79, + "direction": "increasing", + "buckets": [ + { "periodStart": "...", "periodEnd": "...", "txCount": 0, "gasUsed": "0", "avgGasPriceGwei": 0 }, + { "periodStart": "...", "periodEnd": "...", "txCount": 2, "gasUsed": "42124", "avgGasPriceGwei": 0.11 }, + { "periodStart": "...", "periodEnd": "...", "txCount": 6, "gasUsed": "126372", "avgGasPriceGwei": 0.28 }, + { "periodStart": "...", "periodEnd": "...", "txCount": 12, "gasUsed": "330931", "avgGasPriceGwei": 6.78 }, + { "periodStart": "...", "periodEnd": "...", "txCount": 2, "gasUsed": "54860", "avgGasPriceGwei": 0.14 } + ] + }, + "activityScore": { "value": 78, "recency": 94, "frequency": 100, "consistency": 0 }, + "dataSource": "etherscan" +} +``` + +Safe failure response — a malformed address (HTTP 400): + +```bash +curl --silent "https://api.gaspulse.win/v1/address/not-an-address/activity" +``` + +```json +{"error":{"code":"invalid_address","message":"address must be a 0x-prefixed 40-hex-character Ethereum address"}} +``` + +An address with no transactions in the window returns `HTTP 200` with every numeric field +zeroed and `gasTrend.direction: "insufficient_data"` — never a `404` — so callers get a +deterministic response shape regardless of activity level. From 9a81900580c3d7737a3c2baaa74415ad1e9261c0 Mon Sep 17 00:00:00 2001 From: muhammad-wei Date: Thu, 3 Sep 2026 14:34:34 +0800 Subject: [PATCH 2/3] Update submission to reviewCommit 44ec16e (fastify security fix) gaspulse's fastify dependency was upgraded from 4.29.1 to 5.12.1 to clear four high-severity advisories (npm audit --omit=dev now reports 0 vulnerabilities). Redeployed and re-verified live before updating this PR: /health and /.well-known/xagent-verification.json both confirmed returning the new commit prior to this push, per the redeploy protocol. Offline and --online validate:submission both pass against the new commit. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_012SF7wRZMuBk8ihrx4xrgYe --- .../visioneer-gaspulse/RIGHTS.md | 2 +- .../visioneer-gaspulse/SUBMISSION.md | 8 +- .../source/package-lock.json | 435 +++++++++++------- .../visioneer-gaspulse/source/package.json | 2 +- .../visioneer-gaspulse/submission.json | 2 +- .../visioneer-gaspulse/verification/README.md | 6 +- 6 files changed, 290 insertions(+), 165 deletions(-) diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/RIGHTS.md b/submissions/mcp-hackathon/visioneer-gaspulse/RIGHTS.md index c08eef9..49ee2ba 100644 --- a/submissions/mcp-hackathon/visioneer-gaspulse/RIGHTS.md +++ b/submissions/mcp-hackathon/visioneer-gaspulse/RIGHTS.md @@ -9,7 +9,7 @@ The submitter confirms that they own, or have sufficient authorization for, the Subject to the official program terms, the submitter authorizes X-Agent to retain, reproduce, audit, test, archive, and publish the submitted program artifact for judging, fraud prevention, dispute handling, ecosystem submission, and post-award accountability. Closing the pull request, deleting a fork, or deleting an external repository does not revoke the official archive rights attached to an accepted and rewarded entry. -Third-party components and their licenses: `fastify@4.29.1 (MIT), zod@3.25.76 (MIT), dotenv@16.6.1 (BSD-2-Clause), typescript@5.9.3 (Apache-2.0), tsx@4.23.13 (MIT), vitest@2.1.9 (MIT). Runtime data source: Etherscan API (api.etherscan.io), used under Etherscan's public API terms — no Etherscan code or data is redistributed, only queried live per request.` +Third-party components and their licenses: `fastify@5.12.1 (MIT), zod@3.25.76 (MIT), dotenv@16.6.1 (BSD-2-Clause), typescript@5.9.3 (Apache-2.0), tsx@4.23.13 (MIT), vitest@2.1.9 (MIT). Runtime data source: Etherscan API (api.etherscan.io), used under Etherscan's public API terms — no Etherscan code or data is redistributed, only queried live per request.` Exceptions or restrictions: `none` diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/SUBMISSION.md b/submissions/mcp-hackathon/visioneer-gaspulse/SUBMISSION.md index 644a407..85ac273 100644 --- a/submissions/mcp-hackathon/visioneer-gaspulse/SUBMISSION.md +++ b/submissions/mcp-hackathon/visioneer-gaspulse/SUBMISSION.md @@ -32,7 +32,7 @@ ## Source and reproducibility - **Source repository:** https://github.com/muhammad-wei/gaspulse -- **Review commit:** `0b5562319a3e1e96adeef5e1231bd4cbd2dd3030` +- **Review commit:** `44ec16e5187d8a1944d6c058757bf2a06384b3d5` - **Source submitted in this PR:** `source/` - **Run tests:** `npm ci && npm test` - **Run locally:** `npm ci && cp .env.example .env` (fill in `ETHERSCAN_API_KEY`) `&& npm run dev` @@ -48,12 +48,12 @@ The API must expose: ```json // GET https://api.gaspulse.win/health -{"status":"ok","commit":"0b5562319a3e1e96adeef5e1231bd4cbd2dd3030"} +{"status":"ok","commit":"44ec16e5187d8a1944d6c058757bf2a06384b3d5"} ``` ```json // GET /.well-known/xagent-verification.json on the same API origin -{"schemaVersion":1,"slug":"visioneer-gaspulse","commit":"0b5562319a3e1e96adeef5e1231bd4cbd2dd3030"} +{"schemaVersion":1,"slug":"visioneer-gaspulse","commit":"44ec16e5187d8a1944d6c058757bf2a06384b3d5"} ``` ## Verification @@ -61,7 +61,7 @@ The API must expose: The reproducible call instructions and redacted example responses are in `verification/README.md`. - **Health-check result:** `GET https://api.gaspulse.win/health` → - `{"status":"ok","commit":"0b5562319a3e1e96adeef5e1231bd4cbd2dd3030"}` + `{"status":"ok","commit":"44ec16e5187d8a1944d6c058757bf2a06384b3d5"}` - **Capability call:** `GET https://api.gaspulse.win/v1/address/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/activity?windowDays=30` → `200` with gas trend and activity score for that address. - **Expected error behavior:** malformed address → `400 invalid_address`; out-of-range diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/package-lock.json b/submissions/mcp-hackathon/visioneer-gaspulse/source/package-lock.json index 79da9ae..cbafb15 100644 --- a/submissions/mcp-hackathon/visioneer-gaspulse/source/package-lock.json +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "dependencies": { "dotenv": "^16.4.5", - "fastify": "^4.28.1", + "fastify": "^5.12.1", "zod": "^3.23.8" }, "devDependencies": { @@ -465,38 +465,114 @@ } }, "node_modules/@fastify/ajv-compiler": { - "version": "3.6.0", - "resolved": "https://registry.npmmirror.com/@fastify/ajv-compiler/-/ajv-compiler-3.6.0.tgz", - "integrity": "sha512-LwdXQJjmMD+GwLOkP7TVC68qa+pSSogeWWmznRJ/coyTcfe9qA05AHFSe1eZFwK6q+xVRpChnvFUkf1iYaSZsQ==", + "version": "4.0.6", + "resolved": "https://registry.npmmirror.com/@fastify/ajv-compiler/-/ajv-compiler-4.0.6.tgz", + "integrity": "sha512-NtuzM0SfaMJbGlnjr9LWQUN5LzgSrbB8tf/wRZNas+4E1O/Nmzl53e7ruT61HDZyRCJGC6FxIogmNZO1c5ETBA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "ajv": "^8.11.0", - "ajv-formats": "^2.1.1", - "fast-uri": "^2.0.0" + "ajv": "^8.12.0", + "ajv-formats": "^3.0.1", + "fast-uri": "^4.0.0" } }, "node_modules/@fastify/error": { - "version": "3.4.1", - "resolved": "https://registry.npmmirror.com/@fastify/error/-/error-3.4.1.tgz", - "integrity": "sha512-wWSvph+29GR783IhmvdwWnN4bUxTD01Vm5Xad4i7i1VuAOItLvbPAb69sb0IQ2N57yprvhNIwAP5B6xfKTmjmQ==", + "version": "4.2.0", + "resolved": "https://registry.npmmirror.com/@fastify/error/-/error-4.2.0.tgz", + "integrity": "sha512-RSo3sVDXfHskiBZKBPRgnQTtIqpi/7zhJOEmAxCiBcM7d0uwdGdxLlsCaLzGs8v8NnxIRlfG0N51p5yFaOentQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT" }, "node_modules/@fastify/fast-json-stringify-compiler": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-4.3.0.tgz", - "integrity": "sha512-aZAXGYo6m22Fk1zZzEUKBvut/CIIQe/BapEORnxiD5Qr0kPHqqI69NtEMCme74h+at72sPhbkb4ZrLd1W3KRLA==", + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/@fastify/fast-json-stringify-compiler/-/fast-json-stringify-compiler-5.1.0.tgz", + "integrity": "sha512-PxcYtKLbQ8Z+yApiqjK8FwxIwvEj38k2OiLc17u8dkJSlmfi2wHHPaSnaoqBPQqtvF8YVsDgDpP2snDCfFrpfw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "fast-json-stringify": "^5.7.0" + "fast-json-stringify": "^7.0.0" } }, + "node_modules/@fastify/forwarded": { + "version": "3.0.2", + "resolved": "https://registry.npmmirror.com/@fastify/forwarded/-/forwarded-3.0.2.tgz", + "integrity": "sha512-NE8HgKLgYejV9lDpqkEFaDKMLYelJBVfHekhB0UKvX0ghagXRJqg68feg8er1NPXxG4N9i6vPxzt8E+3wHfcmA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/@fastify/merge-json-schemas": { - "version": "0.1.1", - "resolved": "https://registry.npmmirror.com/@fastify/merge-json-schemas/-/merge-json-schemas-0.1.1.tgz", - "integrity": "sha512-fERDVz7topgNjtXsJTTW1JKLy0rhuLRcquYqNR9rF7OcVpCa2OVW49ZPDIhaRRCaUuvVxI+N416xUoF76HNSXA==", + "version": "0.2.1", + "resolved": "https://registry.npmmirror.com/@fastify/merge-json-schemas/-/merge-json-schemas-0.2.1.tgz", + "integrity": "sha512-OA3KGBCy6KtIvLf8DINC5880o5iBlDX4SxzLQS8HorJAbqluzLRn80UXU0bxZn7UOFhFgpRJDasfwn9nG4FG4A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.3" + "dequal": "^2.0.3" + } + }, + "node_modules/@fastify/proxy-addr": { + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/@fastify/proxy-addr/-/proxy-addr-5.1.0.tgz", + "integrity": "sha512-INS+6gh91cLUjB+PVHfu1UqcB76Sqtpyp7bnL+FYojhjygvOPA9ctiD/JDKsyD9Xgu4hUhCSJBPig/w7duNajw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT", + "dependencies": { + "@fastify/forwarded": "^3.0.0", + "ipaddr.js": "^2.1.0" } }, "node_modules/@jridgewell/sourcemap-codec": { @@ -1074,9 +1150,9 @@ } }, "node_modules/ajv-formats": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-2.1.1.tgz", - "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "version": "3.0.1", + "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", "dependencies": { "ajv": "^8.0.0" @@ -1091,9 +1167,9 @@ } }, "node_modules/ajv/node_modules/fast-uri": { - "version": "3.1.6", - "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.6.tgz", - "integrity": "sha512-7Ical1vFEMr0onbVzEDIreM22I4khW+fzyQPwvAFWBp1iwdshSZRsL4jjRvPG9JP1uiqMHRto+YU6R2/CzDz5Q==", + "version": "3.1.7", + "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-3.1.7.tgz", + "integrity": "sha512-dOvZVzjdZdz7phd9v6jCbwxrBW3fK6n8Rc0CtdmM4bumzMnxywBYhuph6J819RRw/ku+rLbelwfMunktuzVVHg==", "funding": [ { "type": "github", @@ -1126,12 +1202,22 @@ } }, "node_modules/avvio": { - "version": "8.4.0", - "resolved": "https://registry.npmmirror.com/avvio/-/avvio-8.4.0.tgz", - "integrity": "sha512-CDSwaxINFy59iNwhYnkvALBwZiTydGkOecZyPkqBpABYR1KqGEsET0VOOYDwtleZSUIdeY36DC2bSZ24CO1igA==", + "version": "9.3.0", + "resolved": "https://registry.npmmirror.com/avvio/-/avvio-9.3.0.tgz", + "integrity": "sha512-g2tQ7LE7oOSqDfwEm3M+ZCMTJc7KiZCdJ4UwyZJb5ckTKyYu50OYmvv0mCFXPuYXoM4zkSt8zM9XQ9KCvxA74A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "@fastify/error": "^3.3.0", + "@fastify/error": "^4.0.0", "fastq": "^1.17.1" } }, @@ -1173,12 +1259,16 @@ } }, "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmmirror.com/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/debug": { @@ -1209,6 +1299,15 @@ "node": ">=6" } }, + "node_modules/dequal": { + "version": "2.0.3", + "resolved": "https://registry.npmmirror.com/dequal/-/dequal-2.0.3.tgz", + "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/dotenv": { "version": "16.6.1", "resolved": "https://registry.npmmirror.com/dotenv/-/dotenv-16.6.1.tgz", @@ -1290,12 +1389,6 @@ "node": ">=12.0.0" } }, - "node_modules/fast-content-type-parse": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/fast-content-type-parse/-/fast-content-type-parse-1.1.0.tgz", - "integrity": "sha512-fBHHqSTFLVnR61C+gltJuE5GkVQMV0S2nqUO8TJ+5Z3qAKG8vAx4FKai1s5jq/inV1+sREynIWSuQ6HgoSXpDQ==", - "license": "MIT" - }, "node_modules/fast-decode-uri-component": { "version": "1.0.1", "resolved": "https://registry.npmmirror.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz", @@ -1309,37 +1402,29 @@ "license": "MIT" }, "node_modules/fast-json-stringify": { - "version": "5.16.1", - "resolved": "https://registry.npmmirror.com/fast-json-stringify/-/fast-json-stringify-5.16.1.tgz", - "integrity": "sha512-KAdnLvy1yu/XrRtP+LJnxbBGrhN+xXu+gt3EUvZhYGKCr3lFHq/7UFJHHFgmJKoqlh6B40bZLEv7w46B0mqn1g==", + "version": "7.0.1", + "resolved": "https://registry.npmmirror.com/fast-json-stringify/-/fast-json-stringify-7.0.1.tgz", + "integrity": "sha512-eRSayARSbbwlBjpP4vnTTIRD5QPcIrmihPxDeN1DtKnHPg66UuJLx+8hlK1kaFdjvzyQ/dzALoi4vwAQ+T+iZA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "@fastify/merge-json-schemas": "^0.1.0", - "ajv": "^8.10.0", + "@fastify/merge-json-schemas": "^0.2.0", + "ajv": "^8.12.0", "ajv-formats": "^3.0.1", - "fast-deep-equal": "^3.1.3", - "fast-uri": "^2.1.0", - "json-schema-ref-resolver": "^1.0.1", + "fast-uri": "^4.0.0", + "json-schema-ref-resolver": "^3.0.0", "rfdc": "^1.2.0" } }, - "node_modules/fast-json-stringify/node_modules/ajv-formats": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/ajv-formats/-/ajv-formats-3.0.1.tgz", - "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", - "license": "MIT", - "dependencies": { - "ajv": "^8.0.0" - }, - "peerDependencies": { - "ajv": "^8.0.0" - }, - "peerDependenciesMeta": { - "ajv": { - "optional": true - } - } - }, "node_modules/fast-querystring": { "version": "1.1.2", "resolved": "https://registry.npmmirror.com/fast-querystring/-/fast-querystring-1.1.2.tgz", @@ -1350,15 +1435,25 @@ } }, "node_modules/fast-uri": { - "version": "2.4.5", - "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-2.4.5.tgz", - "integrity": "sha512-k9Nq2gfGrPow/wJcIQ+dbhLqSTdHx2eH+e6IAjAam0Xs9W5Uda8eXu6Ns73byuuyYvIXYnEJ9ziARtlXm9opgw==", - "license": "MIT" + "version": "4.1.4", + "resolved": "https://registry.npmmirror.com/fast-uri/-/fast-uri-4.1.4.tgz", + "integrity": "sha512-dODXrIxlS9JSdgAnhIUKOosKV1oMtU2VtVw87QRaHzyl5jxO290Ii5tEZfCfzfWNHi3jKWwBSdQj0qIyshdZdQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" }, "node_modules/fastify": { - "version": "4.29.1", - "resolved": "https://registry.npmmirror.com/fastify/-/fastify-4.29.1.tgz", - "integrity": "sha512-m2kMNHIG92tSNWv+Z3UeTR9AWLLuo7KctC7mlFPtMEVrfjIhmQhkQnT9v15qA/BfVq3vvj134Y0jl9SBje3jXQ==", + "version": "5.12.1", + "resolved": "https://registry.npmmirror.com/fastify/-/fastify-5.12.1.tgz", + "integrity": "sha512-FWi+tQvwxR/PeRX7Z2mhfEF5ozJ3jn9asiiclzKXNSzJRHAYcU924aIOKAdHFJ+YIKieh3cqr1IwCOvTr41B3Q==", "funding": [ { "type": "github", @@ -1371,22 +1466,21 @@ ], "license": "MIT", "dependencies": { - "@fastify/ajv-compiler": "^3.5.0", - "@fastify/error": "^3.4.0", - "@fastify/fast-json-stringify-compiler": "^4.3.0", + "@fastify/ajv-compiler": "^4.0.5", + "@fastify/error": "^4.0.0", + "@fastify/fast-json-stringify-compiler": "^5.0.0", + "@fastify/proxy-addr": "^5.0.0", "abstract-logging": "^2.0.1", - "avvio": "^8.3.0", - "fast-content-type-parse": "^1.1.0", - "fast-json-stringify": "^5.8.0", - "find-my-way": "^8.0.0", - "light-my-request": "^5.11.0", - "pino": "^9.0.0", - "process-warning": "^3.0.0", - "proxy-addr": "^2.0.7", - "rfdc": "^1.3.0", - "secure-json-parse": "^2.7.0", - "semver": "^7.5.4", - "toad-cache": "^3.3.0" + "avvio": "^9.0.0", + "fast-json-stringify": "^7.0.0", + "find-my-way": "^9.6.0", + "light-my-request": "^6.0.0", + "pino": "^9.14.0 || ^10.1.0", + "process-warning": "^5.1.0", + "rfdc": "^1.3.1", + "secure-json-parse": "^4.0.0", + "semver": "^7.6.0", + "toad-cache": "^3.7.0" } }, "node_modules/fastq": { @@ -1399,26 +1493,17 @@ } }, "node_modules/find-my-way": { - "version": "8.2.2", - "resolved": "https://registry.npmmirror.com/find-my-way/-/find-my-way-8.2.2.tgz", - "integrity": "sha512-Dobi7gcTEq8yszimcfp/R7+owiT4WncAJ7VTTgFH1jYJ5GaG1FbhjwDG820hptN0QDFvzVY3RfCzdInvGPGzjA==", + "version": "9.9.0", + "resolved": "https://registry.npmmirror.com/find-my-way/-/find-my-way-9.9.0.tgz", + "integrity": "sha512-sJsgZ1sQH2UDuowPuMKg8az7Qc8F0jnj+SKkFWU/+T0xcFlgV5skgXOGUqmQzOdmW6ALA7AhJINWx3qFBkbLHA==", "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-querystring": "^1.0.0", - "safe-regex2": "^3.1.0" + "safe-regex2": "^5.0.0" }, "engines": { - "node": ">=14" - } - }, - "node_modules/forwarded": { - "version": "0.2.0", - "resolved": "https://registry.npmmirror.com/forwarded/-/forwarded-0.2.0.tgz", - "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", - "license": "MIT", - "engines": { - "node": ">= 0.6" + "node": ">=20" } }, "node_modules/fsevents": { @@ -1437,21 +1522,31 @@ } }, "node_modules/ipaddr.js": { - "version": "1.9.1", - "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz", - "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "version": "2.5.0", + "resolved": "https://registry.npmmirror.com/ipaddr.js/-/ipaddr.js-2.5.0.tgz", + "integrity": "sha512-aq+t5NAc+cS6rZQQVWC2x98CPqGtKKTMDd4Gaodv0wShnItdKg/51djkGJ1hqH+Oy0ivDftCbSLCQob8zso01w==", "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">= 10" } }, "node_modules/json-schema-ref-resolver": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/json-schema-ref-resolver/-/json-schema-ref-resolver-1.0.1.tgz", - "integrity": "sha512-EJAj1pgHc1hxF6vo2Z3s69fMjO1INq6eGHXZ8Z6wCQeldCuwxGK9Sxf4/cScGn3FZubCVUehfWtcDM/PLteCQw==", + "version": "3.0.0", + "resolved": "https://registry.npmmirror.com/json-schema-ref-resolver/-/json-schema-ref-resolver-3.0.0.tgz", + "integrity": "sha512-hOrZIVL5jyYFjzk7+y7n5JDzGlU8rfWDuYyHwGa2WA8/pcmMHezp2xsVwxrebD/Q9t8Nc5DboieySDpCp4WG4A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.3" + "dequal": "^2.0.3" } }, "node_modules/json-schema-traverse": { @@ -1461,16 +1556,42 @@ "license": "MIT" }, "node_modules/light-my-request": { - "version": "5.14.0", - "resolved": "https://registry.npmmirror.com/light-my-request/-/light-my-request-5.14.0.tgz", - "integrity": "sha512-aORPWntbpH5esaYpGOOmri0OHDOe3wC5M2MQxZ9dvMLZm6DnaAn0kJlcbU9hwsQgLzmZyReKwFwwPkR+nHu5kA==", + "version": "6.6.0", + "resolved": "https://registry.npmmirror.com/light-my-request/-/light-my-request-6.6.0.tgz", + "integrity": "sha512-CHYbu8RtboSIoVsHZ6Ye4cj4Aw/yg2oAFimlF7mNvfDV192LR7nDiKtSIfCuLT7KokPSTn/9kfVLm5OGN0A28A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "BSD-3-Clause", "dependencies": { - "cookie": "^0.7.0", - "process-warning": "^3.0.0", - "set-cookie-parser": "^2.4.1" + "cookie": "^1.0.1", + "process-warning": "^4.0.0", + "set-cookie-parser": "^2.6.0" } }, + "node_modules/light-my-request/node_modules/process-warning": { + "version": "4.0.1", + "resolved": "https://registry.npmmirror.com/process-warning/-/process-warning-4.0.1.tgz", + "integrity": "sha512-3c2LzQ3rY9d0hc1emcsHhfT9Jwz0cChib/QN89oME2R451w5fy3f0afAhERFZAwrbDU43wk12d0ORBpDVME50Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/loupe": { "version": "3.2.1", "resolved": "https://registry.npmmirror.com/loupe/-/loupe-3.2.1.tgz", @@ -1584,22 +1705,6 @@ "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", "license": "MIT" }, - "node_modules/pino/node_modules/process-warning": { - "version": "5.1.0", - "resolved": "https://registry.npmmirror.com/process-warning/-/process-warning-5.1.0.tgz", - "integrity": "sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, "node_modules/postcss": { "version": "8.5.26", "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.26.tgz", @@ -1630,24 +1735,21 @@ } }, "node_modules/process-warning": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/process-warning/-/process-warning-3.0.0.tgz", - "integrity": "sha512-mqn0kFRl0EoqhnL0GQ0veqFHyIN1yig9RHh/InzORTUiZHFRAur+aMtRkELNwGs9aNwKS6tg/An4NYBPGwvtzQ==", + "version": "5.1.0", + "resolved": "https://registry.npmmirror.com/process-warning/-/process-warning-5.1.0.tgz", + "integrity": "sha512-jQSaVHsPgtyw60e1rQ/A+/ArPEj/S8pS/vFnyGa/gYFXrKk/6RuDkoqVDQ5NI5MmS01698ltlAk0NoDBNLujRw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT" }, - "node_modules/proxy-addr": { - "version": "2.0.7", - "resolved": "https://registry.npmmirror.com/proxy-addr/-/proxy-addr-2.0.7.tgz", - "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", - "license": "MIT", - "dependencies": { - "forwarded": "0.2.0", - "ipaddr.js": "1.9.1" - }, - "engines": { - "node": ">= 0.10" - } - }, "node_modules/quick-format-unescaped": { "version": "4.0.4", "resolved": "https://registry.npmmirror.com/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", @@ -1673,9 +1775,9 @@ } }, "node_modules/ret": { - "version": "0.4.3", - "resolved": "https://registry.npmmirror.com/ret/-/ret-0.4.3.tgz", - "integrity": "sha512-0f4Memo5QP7WQyUEAYUO3esD/XjOc3Zjjg5CPsAq1p8sIu0XPeMbHJemKA0BO7tV0X7+A0FoEpbmHXWxPyD3wQ==", + "version": "0.5.0", + "resolved": "https://registry.npmmirror.com/ret/-/ret-0.5.0.tgz", + "integrity": "sha512-I1XxrZSQ+oErkRR4jYbAyEEu2I0avBvvMM5JN+6EBprOGRCs63ENqZ3vjavq8fBw2+62G5LF5XelKwuJpcvcxw==", "license": "MIT", "engines": { "node": ">=10" @@ -1744,12 +1846,25 @@ } }, "node_modules/safe-regex2": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/safe-regex2/-/safe-regex2-3.1.0.tgz", - "integrity": "sha512-RAAZAGbap2kBfbVhvmnTFv73NWLMvDGOITFYTZBAaY8eR+Ir4ef7Up/e7amo+y1+AH+3PtLkrt9mvcTsG9LXug==", + "version": "5.1.1", + "resolved": "https://registry.npmmirror.com/safe-regex2/-/safe-regex2-5.1.1.tgz", + "integrity": "sha512-mOSBvHGDZMuIEZMdOz/aCEYDCv0E7nfcNsIhUF+/P+xC7Hyf3FkvymqgPbg9D1EdSGu+uKbJgy09K/RKKc7kJA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "MIT", "dependencies": { - "ret": "~0.4.0" + "ret": "~0.5.0" + }, + "bin": { + "safe-regex2": "bin/safe-regex2.js" } }, "node_modules/safe-stable-stringify": { @@ -1762,9 +1877,19 @@ } }, "node_modules/secure-json-parse": { - "version": "2.7.0", - "resolved": "https://registry.npmmirror.com/secure-json-parse/-/secure-json-parse-2.7.0.tgz", - "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", + "version": "4.1.0", + "resolved": "https://registry.npmmirror.com/secure-json-parse/-/secure-json-parse-4.1.0.tgz", + "integrity": "sha512-l4KnYfEyqYJxDwlNVyRfO2E4NTHfMKAWdUuA8J0yve2Dz/E/PdBepY03RvyJpssIpRFwJoCD55wA+mEDs6ByWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], "license": "BSD-3-Clause" }, "node_modules/semver": { diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/package.json b/submissions/mcp-hackathon/visioneer-gaspulse/source/package.json index 9878bc3..12d75ef 100644 --- a/submissions/mcp-hackathon/visioneer-gaspulse/source/package.json +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/package.json @@ -15,7 +15,7 @@ }, "dependencies": { "dotenv": "^16.4.5", - "fastify": "^4.28.1", + "fastify": "^5.12.1", "zod": "^3.23.8" }, "devDependencies": { diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/submission.json b/submissions/mcp-hackathon/visioneer-gaspulse/submission.json index 7efe741..d28e622 100644 --- a/submissions/mcp-hackathon/visioneer-gaspulse/submission.json +++ b/submissions/mcp-hackathon/visioneer-gaspulse/submission.json @@ -3,7 +3,7 @@ "name": "GasPulse", "slug": "visioneer-gaspulse", "sourceRepository": "https://github.com/muhammad-wei/gaspulse", - "reviewCommit": "0b5562319a3e1e96adeef5e1231bd4cbd2dd3030", + "reviewCommit": "44ec16e5187d8a1944d6c058757bf2a06384b3d5", "apiBaseUrl": "https://api.gaspulse.win/v1", "healthCheckUrl": "https://api.gaspulse.win/health", "deploymentProofUrl": "https://api.gaspulse.win/.well-known/xagent-verification.json" diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/verification/README.md b/submissions/mcp-hackathon/visioneer-gaspulse/verification/README.md index cee6f9c..2d3eac7 100644 --- a/submissions/mcp-hackathon/visioneer-gaspulse/verification/README.md +++ b/submissions/mcp-hackathon/visioneer-gaspulse/verification/README.md @@ -2,7 +2,7 @@ ## Prerequisites -- Review commit: `0b5562319a3e1e96adeef5e1231bd4cbd2dd3030` +- Review commit: `44ec16e5187d8a1944d6c058757bf2a06384b3d5` - API base URL: `https://api.gaspulse.win/v1` - Authentication: none required — all endpoints are public. @@ -15,7 +15,7 @@ curl --fail --silent --show-error https://api.gaspulse.win/health Expected response: ```json -{"status":"ok","commit":"0b5562319a3e1e96adeef5e1231bd4cbd2dd3030"} +{"status":"ok","commit":"44ec16e5187d8a1944d6c058757bf2a06384b3d5"} ``` ## 2. Deployment proof @@ -27,7 +27,7 @@ curl --fail --silent --show-error https://api.gaspulse.win/.well-known/xagent-ve Expected response: ```json -{"schemaVersion":1,"slug":"visioneer-gaspulse","commit":"0b5562319a3e1e96adeef5e1231bd4cbd2dd3030"} +{"schemaVersion":1,"slug":"visioneer-gaspulse","commit":"44ec16e5187d8a1944d6c058757bf2a06384b3d5"} ``` ## 3. Capability call From e0b877615cc841bb6e58b6ea32affb49285a471a Mon Sep 17 00:00:00 2001 From: muhammad-wei Date: Fri, 4 Sep 2026 12:01:21 +0800 Subject: [PATCH 3/3] Update GasPulse submission for the new free /v1/gas/current endpoint Re-vendors source/ from commit f1edc68 (adds the free gas-price endpoint alongside the existing paid activity endpoint), bumps reviewCommit across submission.json/SUBMISSION.md/verification/README.md, and documents the new endpoint with a verified live example. Redeploy protocol followed: pushed to gaspulse, deployed, verified both live proof endpoints match, then updated this submission. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_012SF7wRZMuBk8ihrx4xrgYe --- .../visioneer-gaspulse/SUBMISSION.md | 12 +-- .../visioneer-gaspulse/source/README.md | 21 +++++ .../visioneer-gaspulse/source/src/config.ts | 1 + .../source/src/lib/etherscan.ts | 71 ++++++++++++++--- .../source/src/routes/gasCurrent.ts | 47 +++++++++++ .../visioneer-gaspulse/source/src/server.ts | 2 + .../visioneer-gaspulse/source/src/types.ts | 9 +++ .../source/test/gasCurrent.route.test.ts | 78 +++++++++++++++++++ .../visioneer-gaspulse/submission.json | 2 +- .../visioneer-gaspulse/verification/README.md | 26 ++++++- 10 files changed, 249 insertions(+), 20 deletions(-) create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/gasCurrent.ts create mode 100644 submissions/mcp-hackathon/visioneer-gaspulse/source/test/gasCurrent.route.test.ts diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/SUBMISSION.md b/submissions/mcp-hackathon/visioneer-gaspulse/SUBMISSION.md index 85ac273..75d5855 100644 --- a/submissions/mcp-hackathon/visioneer-gaspulse/SUBMISSION.md +++ b/submissions/mcp-hackathon/visioneer-gaspulse/SUBMISSION.md @@ -27,12 +27,14 @@ as `429`/`502` (see error taxonomy below and in `source/README.md`). - **API contract:** `GET /v1/address/{address}/activity?windowDays=30` — full request/response shapes, field meanings, and the activity-score methodology are documented in - `source/README.md` (reproduced with a live example in `verification/README.md`). + `source/README.md` (reproduced with a live example in `verification/README.md`). A second, + free endpoint, `GET /v1/gas/current`, returns the current network-wide gas price (no address, + no parameters) — also documented in `source/README.md`. ## Source and reproducibility - **Source repository:** https://github.com/muhammad-wei/gaspulse -- **Review commit:** `44ec16e5187d8a1944d6c058757bf2a06384b3d5` +- **Review commit:** `f1edc68641d5bee0f6c8a72f11abc75bfa837c75` - **Source submitted in this PR:** `source/` - **Run tests:** `npm ci && npm test` - **Run locally:** `npm ci && cp .env.example .env` (fill in `ETHERSCAN_API_KEY`) `&& npm run dev` @@ -48,12 +50,12 @@ The API must expose: ```json // GET https://api.gaspulse.win/health -{"status":"ok","commit":"44ec16e5187d8a1944d6c058757bf2a06384b3d5"} +{"status":"ok","commit":"f1edc68641d5bee0f6c8a72f11abc75bfa837c75"} ``` ```json // GET /.well-known/xagent-verification.json on the same API origin -{"schemaVersion":1,"slug":"visioneer-gaspulse","commit":"44ec16e5187d8a1944d6c058757bf2a06384b3d5"} +{"schemaVersion":1,"slug":"visioneer-gaspulse","commit":"f1edc68641d5bee0f6c8a72f11abc75bfa837c75"} ``` ## Verification @@ -61,7 +63,7 @@ The API must expose: The reproducible call instructions and redacted example responses are in `verification/README.md`. - **Health-check result:** `GET https://api.gaspulse.win/health` → - `{"status":"ok","commit":"44ec16e5187d8a1944d6c058757bf2a06384b3d5"}` + `{"status":"ok","commit":"f1edc68641d5bee0f6c8a72f11abc75bfa837c75"}` - **Capability call:** `GET https://api.gaspulse.win/v1/address/0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045/activity?windowDays=30` → `200` with gas trend and activity score for that address. - **Expected error behavior:** malformed address → `400 invalid_address`; out-of-range diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/README.md b/submissions/mcp-hackathon/visioneer-gaspulse/source/README.md index 5b8dc4c..8c111f1 100644 --- a/submissions/mcp-hackathon/visioneer-gaspulse/source/README.md +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/README.md @@ -71,6 +71,27 @@ raw transaction list per address for 60 seconds in memory to absorb repeated age 1,000 transactions Etherscan returns for the address; extremely long-lived, high-volume addresses may have earlier history that isn't reflected. Ethereum mainnet only. +### `GET /v1/gas/current` — free + +Current Ethereum mainnet gas price, no parameters, no authentication. Distinct from the +per-address activity endpoint above: this is a network-wide snapshot, not tied to any address. + +```json +{ + "network": "ethereum-mainnet", + "asOf": "2026-09-04T03:58:43.479Z", + "safeGwei": 12.1, + "standardGwei": 14.3, + "fastGwei": 18.7, + "dataSource": "etherscan" +} +``` + +**Errors**: `429 upstream_rate_limited`, `502 upstream_unavailable` — same taxonomy as above. + +**Caching**: the current gas price is cached in memory for 15 seconds (shorter than the +per-address cache, since the value is meant to be "current" and changes roughly every block). + ## Setup Requirements: Node.js `>=18.17`, a free Etherscan API key (https://etherscan.io/apis). diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/config.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/config.ts index 958f000..c0d5520 100644 --- a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/config.ts +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/config.ts @@ -3,3 +3,4 @@ export const SUBMISSION_SLUG = "visioneer-gaspulse"; export const ETHERSCAN_API_KEY = process.env.ETHERSCAN_API_KEY ?? ""; export const PORT = Number(process.env.PORT ?? 8080); export const CACHE_TTL_MS = 60_000; +export const GAS_ORACLE_CACHE_TTL_MS = 15_000; diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/etherscan.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/etherscan.ts index 638b0d1..d8ffdbb 100644 --- a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/etherscan.ts +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/lib/etherscan.ts @@ -20,18 +20,30 @@ interface EtherscanListResponse { result: EtherscanTx[] | string; } -export async function fetchTransactions(address: string, apiKey: string): Promise { +interface EtherscanGasOracleResult { + SafeGasPrice: string; + ProposeGasPrice: string; + FastGasPrice: string; +} + +interface EtherscanGasOracleResponse { + status: string; + message: string; + result: EtherscanGasOracleResult | string; +} + +export interface GasOraclePrices { + safeGwei: number; + standardGwei: number; + fastGwei: number; +} + +async function etherscanGet(params: Record): Promise { const url = new URL(ETHERSCAN_BASE_URL); url.searchParams.set("chainid", String(CHAIN_ID_ETHEREUM_MAINNET)); - url.searchParams.set("module", "account"); - url.searchParams.set("action", "txlist"); - url.searchParams.set("address", address); - url.searchParams.set("startblock", "0"); - url.searchParams.set("endblock", "99999999"); - url.searchParams.set("page", "1"); - url.searchParams.set("offset", String(MAX_TRANSACTIONS)); - url.searchParams.set("sort", "desc"); - url.searchParams.set("apikey", apiKey); + for (const [key, value] of Object.entries(params)) { + url.searchParams.set(key, value); + } const controller = new AbortController(); const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS); @@ -51,7 +63,44 @@ export async function fetchTransactions(address: string, apiKey: string): Promis throw new EtherscanError(`Etherscan responded with HTTP ${response.status}`, "unavailable"); } - const body = (await response.json()) as EtherscanListResponse; + return (await response.json()) as T; +} + +export async function fetchGasOracle(apiKey: string): Promise { + const body = await etherscanGet({ + module: "gastracker", + action: "gasoracle", + apikey: apiKey, + }); + + if (body.status === "0" || typeof body.result === "string") { + const message = typeof body.result === "string" ? body.result : body.message; + if (/rate limit/i.test(message)) { + throw new EtherscanError(message, "rate_limited"); + } + throw new EtherscanError(message, "unavailable"); + } + + return { + safeGwei: Number(body.result.SafeGasPrice), + standardGwei: Number(body.result.ProposeGasPrice), + fastGwei: Number(body.result.FastGasPrice), + }; +} + +export async function fetchTransactions(address: string, apiKey: string): Promise { + const body = await etherscanGet({ + module: "account", + action: "txlist", + address, + startblock: "0", + endblock: "99999999", + page: "1", + offset: String(MAX_TRANSACTIONS), + sort: "desc", + apikey: apiKey, + }); + if (body.status === "0") { if (typeof body.result === "string" && /rate limit/i.test(body.result)) { throw new EtherscanError(body.result, "rate_limited"); diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/gasCurrent.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/gasCurrent.ts new file mode 100644 index 0000000..70c441f --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/routes/gasCurrent.ts @@ -0,0 +1,47 @@ +import type { FastifyInstance } from "fastify"; +import { ETHERSCAN_API_KEY, GAS_ORACLE_CACHE_TTL_MS } from "../config.js"; +import { fetchGasOracle, EtherscanError, type GasOraclePrices } from "../lib/etherscan.js"; +import type { GasOracleResponse } from "../types.js"; + +let cache: { expiresAt: number; prices: GasOraclePrices } | null = null; + +/** Test-only: clears the module-level cache so each test hits the mocked fetch. */ +export function resetGasOracleCache(): void { + cache = null; +} + +export async function registerGasCurrentRoute(app: FastifyInstance): Promise { + app.get("/v1/gas/current", async (request, reply) => { + let prices: GasOraclePrices; + try { + prices = await getGasOracle(); + } catch (error) { + if (error instanceof EtherscanError && error.kind === "rate_limited") { + return reply.code(429).send({ + error: { code: "upstream_rate_limited", message: "Etherscan rate limit exceeded, retry shortly" }, + }); + } + request.log.error(error); + return reply.code(502).send({ + error: { code: "upstream_unavailable", message: "the upstream data source is unavailable" }, + }); + } + + const response: GasOracleResponse = { + network: "ethereum-mainnet", + asOf: new Date().toISOString(), + ...prices, + dataSource: "etherscan", + }; + return response; + }); +} + +async function getGasOracle(): Promise { + if (cache && cache.expiresAt > Date.now()) { + return cache.prices; + } + const prices = await fetchGasOracle(ETHERSCAN_API_KEY); + cache = { expiresAt: Date.now() + GAS_ORACLE_CACHE_TTL_MS, prices }; + return prices; +} diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/server.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/server.ts index 69eb8b6..22bc468 100644 --- a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/server.ts +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/server.ts @@ -4,6 +4,7 @@ import { PORT } from "./config.js"; import { registerHealthRoute } from "./routes/health.js"; import { registerVerificationRoute } from "./routes/verification.js"; import { registerActivityRoute } from "./routes/activity.js"; +import { registerGasCurrentRoute } from "./routes/gasCurrent.js"; export async function buildServer() { const app = Fastify({ logger: true }); @@ -11,6 +12,7 @@ export async function buildServer() { await registerHealthRoute(app); await registerVerificationRoute(app); await registerActivityRoute(app); + await registerGasCurrentRoute(app); app.setNotFoundHandler((_request, reply) => { reply.code(404).send({ error: { code: "not_found", message: "no route matches this path" } }); diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/types.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/types.ts index c6f9fb7..2b4aebc 100644 --- a/submissions/mcp-hackathon/visioneer-gaspulse/source/src/types.ts +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/src/types.ts @@ -33,6 +33,15 @@ export interface ActivityResponse { dataSource: "etherscan"; } +export interface GasOracleResponse { + network: "ethereum-mainnet"; + asOf: string; + safeGwei: number; + standardGwei: number; + fastGwei: number; + dataSource: "etherscan"; +} + export interface EtherscanTx { hash: string; timeStamp: string; diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/source/test/gasCurrent.route.test.ts b/submissions/mcp-hackathon/visioneer-gaspulse/source/test/gasCurrent.route.test.ts new file mode 100644 index 0000000..b678920 --- /dev/null +++ b/submissions/mcp-hackathon/visioneer-gaspulse/source/test/gasCurrent.route.test.ts @@ -0,0 +1,78 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; +import type { FastifyInstance } from "fastify"; +import { buildServer } from "../src/server.js"; +import { resetGasOracleCache } from "../src/routes/gasCurrent.js"; + +function etherscanResponse(result: unknown) { + return new Response(JSON.stringify({ status: "1", message: "OK", result }), { + status: 200, + headers: { "content-type": "application/json" }, + }); +} + +describe("GET /v1/gas/current", () => { + let app: FastifyInstance; + + beforeEach(async () => { + resetGasOracleCache(); + app = await buildServer(); + }); + + afterEach(async () => { + await app.close(); + vi.restoreAllMocks(); + }); + + it("returns parsed gas price tiers", async () => { + vi.stubGlobal( + "fetch", + vi.fn().mockResolvedValue( + etherscanResponse({ + LastBlock: "25901325", + SafeGasPrice: "12.1", + ProposeGasPrice: "14.3", + FastGasPrice: "18.7", + }), + ), + ); + + const response = await app.inject({ method: "GET", url: "/v1/gas/current" }); + expect(response.statusCode).toBe(200); + const body = response.json(); + expect(body).toMatchObject({ + network: "ethereum-mainnet", + safeGwei: 12.1, + standardGwei: 14.3, + fastGwei: 18.7, + dataSource: "etherscan", + }); + expect(typeof body.asOf).toBe("string"); + }); + + it("returns 502 when the upstream is unavailable", async () => { + vi.stubGlobal("fetch", vi.fn().mockResolvedValue(new Response("boom", { status: 500 }))); + + const response = await app.inject({ method: "GET", url: "/v1/gas/current" }); + expect(response.statusCode).toBe(502); + expect(response.json().error.code).toBe("upstream_unavailable"); + }); + + it("returns 429 when the upstream rate-limits", async () => { + vi.stubGlobal("fetch", vi.fn().mockResolvedValue(new Response("too many", { status: 429 }))); + + const response = await app.inject({ method: "GET", url: "/v1/gas/current" }); + expect(response.statusCode).toBe(429); + expect(response.json().error.code).toBe("upstream_rate_limited"); + }); + + it("serves the cached value on a second call without a new upstream request", async () => { + const fetchMock = vi.fn().mockResolvedValue( + etherscanResponse({ LastBlock: "1", SafeGasPrice: "1", ProposeGasPrice: "2", FastGasPrice: "3" }), + ); + vi.stubGlobal("fetch", fetchMock); + + await app.inject({ method: "GET", url: "/v1/gas/current" }); + await app.inject({ method: "GET", url: "/v1/gas/current" }); + expect(fetchMock).toHaveBeenCalledTimes(1); + }); +}); diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/submission.json b/submissions/mcp-hackathon/visioneer-gaspulse/submission.json index d28e622..f560fd6 100644 --- a/submissions/mcp-hackathon/visioneer-gaspulse/submission.json +++ b/submissions/mcp-hackathon/visioneer-gaspulse/submission.json @@ -3,7 +3,7 @@ "name": "GasPulse", "slug": "visioneer-gaspulse", "sourceRepository": "https://github.com/muhammad-wei/gaspulse", - "reviewCommit": "44ec16e5187d8a1944d6c058757bf2a06384b3d5", + "reviewCommit": "f1edc68641d5bee0f6c8a72f11abc75bfa837c75", "apiBaseUrl": "https://api.gaspulse.win/v1", "healthCheckUrl": "https://api.gaspulse.win/health", "deploymentProofUrl": "https://api.gaspulse.win/.well-known/xagent-verification.json" diff --git a/submissions/mcp-hackathon/visioneer-gaspulse/verification/README.md b/submissions/mcp-hackathon/visioneer-gaspulse/verification/README.md index 2d3eac7..5001c44 100644 --- a/submissions/mcp-hackathon/visioneer-gaspulse/verification/README.md +++ b/submissions/mcp-hackathon/visioneer-gaspulse/verification/README.md @@ -2,7 +2,7 @@ ## Prerequisites -- Review commit: `44ec16e5187d8a1944d6c058757bf2a06384b3d5` +- Review commit: `f1edc68641d5bee0f6c8a72f11abc75bfa837c75` - API base URL: `https://api.gaspulse.win/v1` - Authentication: none required — all endpoints are public. @@ -15,7 +15,7 @@ curl --fail --silent --show-error https://api.gaspulse.win/health Expected response: ```json -{"status":"ok","commit":"44ec16e5187d8a1944d6c058757bf2a06384b3d5"} +{"status":"ok","commit":"f1edc68641d5bee0f6c8a72f11abc75bfa837c75"} ``` ## 2. Deployment proof @@ -27,7 +27,7 @@ curl --fail --silent --show-error https://api.gaspulse.win/.well-known/xagent-ve Expected response: ```json -{"schemaVersion":1,"slug":"visioneer-gaspulse","commit":"44ec16e5187d8a1944d6c058757bf2a06384b3d5"} +{"schemaVersion":1,"slug":"visioneer-gaspulse","commit":"f1edc68641d5bee0f6c8a72f11abc75bfa837c75"} ``` ## 3. Capability call @@ -80,3 +80,23 @@ curl --silent "https://api.gaspulse.win/v1/address/not-an-address/activity" An address with no transactions in the window returns `HTTP 200` with every numeric field zeroed and `gasTrend.direction: "insufficient_data"` — never a `404` — so callers get a deterministic response shape regardless of activity level. + +## 4. Free capability call — current network gas price + +```bash +curl --fail --silent --show-error https://api.gaspulse.win/v1/gas/current +``` + +No parameters, no authentication. Expected response shape (values reflect live network +conditions at call time): + +```json +{ + "network": "ethereum-mainnet", + "asOf": "2026-09-04T03:59:55.975Z", + "safeGwei": 0.061819813, + "standardGwei": 0.062017679, + "fastGwei": 0.072819813, + "dataSource": "etherscan" +} +```