diff --git a/.github/workflows/update-prices.yml b/.github/workflows/update-prices.yml new file mode 100644 index 0000000..f3db52d --- /dev/null +++ b/.github/workflows/update-prices.yml @@ -0,0 +1,45 @@ +name: Update Price History + +on: + schedule: + # Run at 00:00 UTC so new closed daily candles are picked up at UTC day boundaries. + - cron: "0 0 * * *" + push: + branches: [main, feat/fiat-price-overlay] + pull_request: + branches: [main] + workflow_dispatch: + +jobs: + update-prices: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + persist-credentials: true + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: "22" + cache: "npm" + + - name: Install dependencies + run: npm install + + - name: Fetch new prices + run: npm run fetch-prices + + - name: Commit and push changes + run: | + git config --local user.email "action@github.com" + git config --local user.name "github-actions[bot]" + git status + if ! git diff --quiet src/data/algo-price-history.json; then + echo "Changes detected, committing..." + git add src/data/algo-price-history.json + git commit -m "chore: update algo price history [skip ci]" + git push origin HEAD:${{ github.ref_name }} + else + echo "No new price data found." + fi diff --git a/package-lock.json b/package-lock.json index 30c7ade..c91bd52 100644 --- a/package-lock.json +++ b/package-lock.json @@ -73,7 +73,8 @@ "typescript": "~5.9.3", "typescript-eslint": "^8.47.0", "vite": "^7.2.2", - "vitest": "^4.0.10" + "vitest": "^4.0.10", + "wrangler": "^3.109.2" }, "engines": { "node": ">=22.0.0", @@ -636,6 +637,144 @@ "sisteransi": "^1.0.5" } }, + "node_modules/@cloudflare/kv-asset-handler": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/@cloudflare/kv-asset-handler/-/kv-asset-handler-0.3.4.tgz", + "integrity": "sha512-YLPHc8yASwjNkmcDMQMY35yiWjoKAKnhUbPRszBRS0YgH+IXtsMp61j+yTcnCE3oO2DgP0U3iejLC8FTtKDC8Q==", + "dev": true, + "license": "MIT OR Apache-2.0", + "dependencies": { + "mime": "^3.0.0" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/@cloudflare/unenv-preset": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@cloudflare/unenv-preset/-/unenv-preset-2.0.2.tgz", + "integrity": "sha512-nyzYnlZjjV5xT3LizahG1Iu6mnrCaxglJ04rZLpDwlDVDZ7v46lNsfxhV3A/xtfgQuSHmLnc6SVI+KwBpc3Lwg==", + "dev": true, + "license": "MIT OR Apache-2.0", + "peerDependencies": { + "unenv": "2.0.0-rc.14", + "workerd": "^1.20250124.0" + }, + "peerDependenciesMeta": { + "workerd": { + "optional": true + } + } + }, + "node_modules/@cloudflare/workerd-darwin-64": { + "version": "1.20250718.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20250718.0.tgz", + "integrity": "sha512-FHf4t7zbVN8yyXgQ/r/GqLPaYZSGUVzeR7RnL28Mwj2djyw2ZergvytVc7fdGcczl6PQh+VKGfZCfUqpJlbi9g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-darwin-arm64": { + "version": "1.20250718.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-arm64/-/workerd-darwin-arm64-1.20250718.0.tgz", + "integrity": "sha512-fUiyUJYyqqp4NqJ0YgGtp4WJh/II/YZsUnEb6vVy5Oeas8lUOxnN+ZOJ8N/6/5LQCVAtYCChRiIrBbfhTn5Z8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-linux-64": { + "version": "1.20250718.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20250718.0.tgz", + "integrity": "sha512-5+eb3rtJMiEwp08Kryqzzu8d1rUcK+gdE442auo5eniMpT170Dz0QxBrqkg2Z48SFUPYbj+6uknuA5tzdRSUSg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-linux-arm64": { + "version": "1.20250718.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20250718.0.tgz", + "integrity": "sha512-Aa2M/DVBEBQDdATMbn217zCSFKE+ud/teS+fFS+OQqKABLn0azO2qq6ANAHYOIE6Q3Sq4CxDIQr8lGdaJHwUog==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-windows-64": { + "version": "1.20250718.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20250718.0.tgz", + "integrity": "sha512-dY16RXKffmugnc67LTbyjdDHZn5NoTF1yHEf2fN4+OaOnoGSp3N1x77QubTDwqZ9zECWxgQfDLjddcH8dWeFhg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, "node_modules/@csstools/color-helpers": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", @@ -777,6 +916,40 @@ "integrity": "sha512-P5LUNhtbj6YfI3iJjw5EL9eUAG6OitD0W3fWQcpQjDRc/QIsL0tRNuO1PcDvPccWL1fSTXXdE1ds+l95DV/OFA==", "license": "MIT" }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild-plugins/node-globals-polyfill": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-globals-polyfill/-/node-globals-polyfill-0.2.3.tgz", + "integrity": "sha512-r3MIryXDeXDOZh7ih1l/yE9ZLORCd5e8vWg02azWRGj5SPTuoh69A2AIyn0Z31V/kHBfZ4HgWJ+OK3GTTwLmnw==", + "dev": true, + "license": "ISC", + "peerDependencies": { + "esbuild": "*" + } + }, + "node_modules/@esbuild-plugins/node-modules-polyfill": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/@esbuild-plugins/node-modules-polyfill/-/node-modules-polyfill-0.2.2.tgz", + "integrity": "sha512-LXV7QsWJxRuMYvKbiznh+U1ilIop3g2TeKRzUxOG5X3YITc8JyyTa90BmLwqqv0YnX4v32CSlG+vsziZp9dMvA==", + "dev": true, + "license": "ISC", + "dependencies": { + "escape-string-regexp": "^4.0.0", + "rollup-plugin-node-polyfills": "^0.2.1" + }, + "peerDependencies": { + "esbuild": "*" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.11", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.11.tgz", @@ -1350,6 +1523,16 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, "node_modules/@floating-ui/core": { "version": "1.7.3", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.3.tgz", @@ -1440,97 +1623,477 @@ "url": "https://github.com/sponsors/nzakas" } }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.33.5.tgz", + "integrity": "sha512-UT4p+iz/2H4twwAoLCqfA9UH5pI6DggwKEGuaPy7nCVQ8ZsiY5PIcrRvD1DzuY3qYL07NtIQcWnBSY/heikIFQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.0.4" } }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "license": "MIT", + "node_modules/@img/sharp-darwin-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.33.5.tgz", + "integrity": "sha512-fyHac4jIc1ANYGRDxtiqelIbdWkIuQaI84Mv45KvGRRxSAa7o7d1ZKAOBaYbnepLC1WqxfpimdeWfvqqSGwR2Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], "engines": { - "node": ">=6.0.0" + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.0.4" } }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.0.4.tgz", + "integrity": "sha512-XblONe153h0O2zuFfTAbQYAX2JhYmDHeWikp1LM9Hul9gVPjFY427k6dFEcOL72O01QxQsWi761svJ/ev9xEDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.0.4.tgz", + "integrity": "sha512-xnGR8YuZYfJGmWPvmlunFaWJsb9T/AO2ykoP3Fz/0X5XV2aoYBPkX6xqCQvUTKKiLddarLaxpzNe+b1hjeWHAQ==", + "cpu": [ + "x64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.0.5.tgz", + "integrity": "sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==", + "cpu": [ + "arm" + ], "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.0.4.tgz", + "integrity": "sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==", + "cpu": [ + "arm64" + ], "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" } }, - "node_modules/@pivanov/utils": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@pivanov/utils/-/utils-0.0.2.tgz", - "integrity": "sha512-q9CN0bFWxWgMY5hVVYyBgez1jGiLBa6I+LkG37ycylPhFvEGOOeaADGtUSu46CaZasPnlY8fCdVJZmrgKb1EPA==", - "license": "MIT", - "peerDependencies": { - "react": ">=18", - "react-dom": ">=18" + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.0.4.tgz", + "integrity": "sha512-u7Wz6ntiSSgGSGcjZ55im6uvTrOxSIS8/dgoVMoiGE9I6JAfU50yH5BoDlYA1tcuGS7g/QNtetJnxA6QEsCVTA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.0.4.tgz", + "integrity": "sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.0.4.tgz", + "integrity": "sha512-9Ti+BbTYDcsbp4wfYib8Ctm1ilkugkA/uscUn6UXK1ldpC1JjiXbLfFZtRlBhjPZ5o1NCLiDbg8fhUPKStHoTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.0.4.tgz", + "integrity": "sha512-viYN1KX9m+/hGkJtvYYp+CCLgnJXwiQB39damAO7WMdKWlIhmYTfHjwSbQeUK/20vY154mwezd9HflVFM1wVSw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.33.5.tgz", + "integrity": "sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.0.5" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.33.5.tgz", + "integrity": "sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.33.5.tgz", + "integrity": "sha512-y/5PCd+mP4CA/sPDKl2961b+C9d+vPAveS33s6Z3zfASk2j5upL6fXVPZi7ztePZ5CuH+1kW8JtvxgbuXHRa4Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.0.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.33.5.tgz", + "integrity": "sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.33.5.tgz", + "integrity": "sha512-XrHMZwGQGvJg2V/oRSUfSAfjfPxO+4DkiRh6p2AFjLQztWUuY/o8Mq0eMQVIY7HJ1CDQUJlxGGZRw1a5bqmd1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.33.5.tgz", + "integrity": "sha512-WT+d/cgqKkkKySYmqoZ8y3pxx7lx9vVejxW/W4DOFMYVSkErR+w7mf2u8m/y4+xHe7yY9DAXQMWQhpnMuFfScw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.0.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.33.5.tgz", + "integrity": "sha512-ykUW4LVGaMcU9lu9thv85CbRMAwfeadCJHRsg2GmeRa/cJxsVY9Rbd57JcMxBkKHag5U/x7TSBpScF4U8ElVzg==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.2.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.33.5.tgz", + "integrity": "sha512-T36PblLaTwuVJ/zw/LaH0PdZkRz5rd3SmMHX8GSmR7vtNSP5Z6bQkExdSK7xGWyxLw4sUknBuugTelgw2faBbQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.33.5.tgz", + "integrity": "sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@pivanov/utils": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/@pivanov/utils/-/utils-0.0.2.tgz", + "integrity": "sha512-q9CN0bFWxWgMY5hVVYyBgez1jGiLBa6I+LkG37ycylPhFvEGOOeaADGtUSu46CaZasPnlY8fCdVJZmrgKb1EPA==", + "license": "MIT", + "peerDependencies": { + "react": ">=18", + "react-dom": ">=18" } }, "node_modules/@preact/signals": { @@ -4459,6 +5022,16 @@ "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", @@ -4658,6 +5231,16 @@ "dequal": "^2.0.3" } }, + "node_modules/as-table": { + "version": "1.0.55", + "resolved": "https://registry.npmjs.org/as-table/-/as-table-1.0.55.tgz", + "integrity": "sha512-xvsWESUJn0JN421Xb9MQw6AsMHRCUknCe0Wjlxvjud80mU4E6hQf1A6NzQKcYNmYw62MfzEtXc+badstZP3JpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "printable-characters": "^1.0.42" + } + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -4811,6 +5394,13 @@ "react": ">=17.0.1" } }, + "node_modules/blake3-wasm": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/blake3-wasm/-/blake3-wasm-2.1.5.tgz", + "integrity": "sha512-F1+K8EbfOZE49dtoPtmxUQrpXaBIl3ICvasLh+nJta0xkz+9kF/7uet9fLnwKqhDrmj6g+6K3Tw9yQPUg2ka5g==", + "dev": true, + "license": "MIT" + }, "node_modules/boxen": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/boxen/-/boxen-7.0.0.tgz", @@ -5092,6 +5682,21 @@ "node": ">=6" } }, + "node_modules/color": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/color/-/color-4.2.3.tgz", + "integrity": "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "color-convert": "^2.0.1", + "color-string": "^1.9.0" + }, + "engines": { + "node": ">=12.5.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -5110,6 +5715,18 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, + "node_modules/color-string": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/color-string/-/color-string-1.9.1.tgz", + "integrity": "sha512-shrVawQFojnZv6xM40anx4CkoDP+fZsw/ZerEMsW/pyzsRbElpsL/DBVW7q3ExxwusdNXI3lXpuhEZkzs8p5Eg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "color-name": "^1.0.0", + "simple-swizzle": "^0.2.2" + } + }, "node_modules/commander": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", @@ -5185,6 +5802,16 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "license": "MIT" }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/cookie-es": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/cookie-es/-/cookie-es-2.0.0.tgz", @@ -5641,6 +6268,13 @@ "node": ">=12" } }, + "node_modules/data-uri-to-buffer": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-2.0.2.tgz", + "integrity": "sha512-ND9qDTLc6diwj+Xe5cdAgVTbLVdXbtxTJRXRhli8Mowuaan+0EJOtdqJ0QCHNSSPyoXGx9HX2/VMnKeC34AChA==", + "dev": true, + "license": "MIT" + }, "node_modules/data-urls": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.0.tgz", @@ -5717,6 +6351,13 @@ "dev": true, "license": "MIT" }, + "node_modules/defu": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/defu/-/defu-6.1.4.tgz", + "integrity": "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg==", + "dev": true, + "license": "MIT" + }, "node_modules/delaunator": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.1.tgz", @@ -6157,6 +6798,19 @@ "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, + "node_modules/exit-hook": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/exit-hook/-/exit-hook-2.2.1.tgz", + "integrity": "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/expect-type": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.2.2.tgz", @@ -6167,6 +6821,13 @@ "node": ">=12.0.0" } }, + "node_modules/exsolve": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.8.tgz", + "integrity": "sha512-LmDxfWXwcTArk8fUEnOfSZpHOJ6zOMUJKOtFLFqJLoKJetuQG874Uc7/Kki7zFLzYybmZhp1M7+98pfMqeX8yA==", + "dev": true, + "license": "MIT" + }, "node_modules/fake-indexeddb": { "version": "6.2.5", "resolved": "https://registry.npmjs.org/fake-indexeddb/-/fake-indexeddb-6.2.5.tgz", @@ -6361,6 +7022,27 @@ "node": ">=6" } }, + "node_modules/get-source": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/get-source/-/get-source-2.0.12.tgz", + "integrity": "sha512-X5+4+iD+HoSeEED+uwrQ07BOQr0kEDFMVqqpBuI+RaZBpBpHCuXxo70bjar6f0b0u/DQJsJ7ssurpP0V60Az+w==", + "dev": true, + "license": "Unlicense", + "dependencies": { + "data-uri-to-buffer": "^2.0.0", + "source-map": "^0.6.1" + } + }, + "node_modules/get-source/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", @@ -6398,6 +7080,13 @@ "node": ">= 6" } }, + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/globals": { "version": "16.5.0", "resolved": "https://registry.npmjs.org/globals/-/globals-16.5.0.tgz", @@ -6615,6 +7304,14 @@ "node": ">=12" } }, + "node_modules/is-arrayish": { + "version": "0.3.4", + "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.3.4.tgz", + "integrity": "sha512-m6UrgzFVUYawGBh1dUsWR5M2Clqic9RVXC/9f8ceNlv2IcO9j9J/z8UoCLPqtsPBFNzEpfR3xftohbfqDx8EQA==", + "dev": true, + "license": "MIT", + "optional": true + }, "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", @@ -7268,6 +7965,19 @@ "node": ">=8.6" } }, + "node_modules/mime": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-3.0.0.tgz", + "integrity": "sha512-jSCU7/VB1loIWBZe14aEYHU/+1UMEHoaO7qxCOVJOw9GgH72VAWppxNcjU+x9a2k3GSIBXNKxXQFqRvvZ7vr3A==", + "dev": true, + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", @@ -7307,6 +8017,77 @@ "node": ">=6" } }, + "node_modules/miniflare": { + "version": "3.20250718.3", + "resolved": "https://registry.npmjs.org/miniflare/-/miniflare-3.20250718.3.tgz", + "integrity": "sha512-JuPrDJhwLrNLEJiNLWO7ZzJrv/Vv9kZuwMYCfv0LskQDM6Eonw4OvywO3CH/wCGjgHzha/qyjUh8JQ068TjDgQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@cspotcode/source-map-support": "0.8.1", + "acorn": "8.14.0", + "acorn-walk": "8.3.2", + "exit-hook": "2.2.1", + "glob-to-regexp": "0.4.1", + "stoppable": "1.1.0", + "undici": "^5.28.5", + "workerd": "1.20250718.0", + "ws": "8.18.0", + "youch": "3.3.4", + "zod": "3.22.3" + }, + "bin": { + "miniflare": "bootstrap.js" + }, + "engines": { + "node": ">=16.13" + } + }, + "node_modules/miniflare/node_modules/acorn": { + "version": "8.14.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.14.0.tgz", + "integrity": "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/miniflare/node_modules/ws": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", + "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/miniflare/node_modules/zod": { + "version": "3.22.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.3.tgz", + "integrity": "sha512-EjIevzuJRiRPbVH4mGc8nApb/lVLKVpmUhAaR5R5doKGfAnGJ6Gr3CViAVjP+4FWSxCsybeWQdcgCtbX+7oZug==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -7391,6 +8172,16 @@ "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, + "node_modules/mustache": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", + "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", + "dev": true, + "license": "MIT", + "bin": { + "mustache": "bin/mustache" + } + }, "node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", @@ -7473,6 +8264,13 @@ "node": ">=8" } }, + "node_modules/ohash": { + "version": "2.0.11", + "resolved": "https://registry.npmjs.org/ohash/-/ohash-2.0.11.tgz", + "integrity": "sha512-RdR9FQrFwNBNXAr4GixM8YaRZRJ5PUWbKYbE5eOsrwAjJW0q2REGcf79oYPsLyskQCZG1PLN+S/K1V00joZAoQ==", + "dev": true, + "license": "MIT" + }, "node_modules/on-headers": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", @@ -7859,6 +8657,13 @@ "dev": true, "license": "MIT" }, + "node_modules/printable-characters": { + "version": "1.0.42", + "resolved": "https://registry.npmjs.org/printable-characters/-/printable-characters-1.0.42.tgz", + "integrity": "sha512-dKp+C4iXWK4vVYZmYSd0KBH5F/h1HoZRsbJ82AVKRO3PEo8L4lBS/vLwhVtpwwuYcoIsVY+1JYKR268yn480uQ==", + "dev": true, + "license": "Unlicense" + }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", @@ -8382,6 +9187,63 @@ "fsevents": "~2.3.2" } }, + "node_modules/rollup-plugin-inject": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/rollup-plugin-inject/-/rollup-plugin-inject-3.0.2.tgz", + "integrity": "sha512-ptg9PQwzs3orn4jkgXJ74bfs5vYz1NCZlSQMBUA0wKcGp5i5pA1AO3fOUEte8enhGUC+iapTCzEWw2jEFFUO/w==", + "deprecated": "This package has been deprecated and is no longer maintained. Please use @rollup/plugin-inject.", + "dev": true, + "license": "MIT", + "dependencies": { + "estree-walker": "^0.6.1", + "magic-string": "^0.25.3", + "rollup-pluginutils": "^2.8.1" + } + }, + "node_modules/rollup-plugin-inject/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/rollup-plugin-inject/node_modules/magic-string": { + "version": "0.25.9", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.9.tgz", + "integrity": "sha512-RmF0AsMzgt25qzqqLc1+MbHmhdx0ojF2Fvs4XnOqz2ZOBXzzkEwc/dJQZCYHAn7v1jbVOjAZfK8msRn4BxO4VQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "sourcemap-codec": "^1.4.8" + } + }, + "node_modules/rollup-plugin-node-polyfills": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/rollup-plugin-node-polyfills/-/rollup-plugin-node-polyfills-0.2.1.tgz", + "integrity": "sha512-4kCrKPTJ6sK4/gLL/U5QzVT8cxJcofO0OU74tnB19F40cmuAKSzH5/siithxlofFEjwvw1YAhPmbvGNA6jEroA==", + "dev": true, + "license": "MIT", + "dependencies": { + "rollup-plugin-inject": "^3.0.0" + } + }, + "node_modules/rollup-pluginutils": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/rollup-pluginutils/-/rollup-pluginutils-2.8.2.tgz", + "integrity": "sha512-EEp9NhnUkwY8aif6bxgovPHMoMoNr2FulJziTndpt5H9RdwC47GSGuII9XxpSdzVGM0GWrNPHV6ie1LTNJPaLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "estree-walker": "^0.6.1" + } + }, + "node_modules/rollup-pluginutils/node_modules/estree-walker": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-0.6.1.tgz", + "integrity": "sha512-SqmZANLWS0mnatqbSfRP5g8OXZC12Fgg1IwNtLsyHDzJizORW4khDfjPqJZsemPWBB2uqykUah5YpQ6epsqC/w==", + "dev": true, + "license": "MIT" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -8570,6 +9432,61 @@ "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT" }, + "node_modules/sharp": { + "version": "0.33.5", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.33.5.tgz", + "integrity": "sha512-haPVm1EkS9pgvHrQ/F3Xy+hgcuMV0Wm9vfIBSiwZ05k+xgb0PkBQpGsAA/oWdDobNaZTH5ppvHtzCFbnSEwHVw==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "optional": true, + "dependencies": { + "color": "^4.2.3", + "detect-libc": "^2.0.3", + "semver": "^7.6.3" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.33.5", + "@img/sharp-darwin-x64": "0.33.5", + "@img/sharp-libvips-darwin-arm64": "1.0.4", + "@img/sharp-libvips-darwin-x64": "1.0.4", + "@img/sharp-libvips-linux-arm": "1.0.5", + "@img/sharp-libvips-linux-arm64": "1.0.4", + "@img/sharp-libvips-linux-s390x": "1.0.4", + "@img/sharp-libvips-linux-x64": "1.0.4", + "@img/sharp-libvips-linuxmusl-arm64": "1.0.4", + "@img/sharp-libvips-linuxmusl-x64": "1.0.4", + "@img/sharp-linux-arm": "0.33.5", + "@img/sharp-linux-arm64": "0.33.5", + "@img/sharp-linux-s390x": "0.33.5", + "@img/sharp-linux-x64": "0.33.5", + "@img/sharp-linuxmusl-arm64": "0.33.5", + "@img/sharp-linuxmusl-x64": "0.33.5", + "@img/sharp-wasm32": "0.33.5", + "@img/sharp-win32-ia32": "0.33.5", + "@img/sharp-win32-x64": "0.33.5" + } + }, + "node_modules/sharp/node_modules/semver": { + "version": "7.7.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", + "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -8604,6 +9521,17 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC" }, + "node_modules/simple-swizzle": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.4.tgz", + "integrity": "sha512-nAu1WFPQSMNr2Zn9PGSZK9AGn4t/y97lEm+MXTtUDwfP0ksAIX4nO+6ruD9Jwut4C49SB1Ws+fbXsm/yScWOHw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "is-arrayish": "^0.3.1" + } + }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", @@ -8651,6 +9579,14 @@ "node": ">=0.10.0" } }, + "node_modules/sourcemap-codec": { + "version": "1.4.8", + "resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz", + "integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==", + "deprecated": "Please use @jridgewell/sourcemap-codec instead", + "dev": true, + "license": "MIT" + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", @@ -8658,6 +9594,17 @@ "dev": true, "license": "MIT" }, + "node_modules/stacktracey": { + "version": "2.1.8", + "resolved": "https://registry.npmjs.org/stacktracey/-/stacktracey-2.1.8.tgz", + "integrity": "sha512-Kpij9riA+UNg7TnphqjH7/CzctQ/owJGNbFkfEeve4Z4uxT5+JapVLFXcsurIfN34gnTWZNJ/f7NMG0E8JDzTw==", + "dev": true, + "license": "Unlicense", + "dependencies": { + "as-table": "^1.0.36", + "get-source": "^2.0.12" + } + }, "node_modules/std-env": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", @@ -8665,6 +9612,17 @@ "dev": true, "license": "MIT" }, + "node_modules/stoppable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", + "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4", + "npm": ">=6" + } + }, "node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", @@ -9062,6 +10020,26 @@ "typescript": ">=4.8.4 <6.0.0" } }, + "node_modules/ufo": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/ufo/-/ufo-1.6.3.tgz", + "integrity": "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, "node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", @@ -9069,6 +10047,20 @@ "devOptional": true, "license": "MIT" }, + "node_modules/unenv": { + "version": "2.0.0-rc.14", + "resolved": "https://registry.npmjs.org/unenv/-/unenv-2.0.0-rc.14.tgz", + "integrity": "sha512-od496pShMen7nOy5VmVJCnq8rptd45vh6Nx/r2iPbrba6pa6p+tS2ywuIHRZ/OBvSbQZB0kWvpO9XBNVFXHD3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "defu": "^6.1.4", + "exsolve": "^1.0.1", + "ohash": "^2.0.10", + "pathe": "^2.0.3", + "ufo": "^1.5.4" + } + }, "node_modules/unplugin": { "version": "2.3.10", "resolved": "https://registry.npmjs.org/unplugin/-/unplugin-2.3.10.tgz", @@ -9304,275 +10296,753 @@ } } }, - "node_modules/vite-tsconfig-paths": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", - "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", + "node_modules/vite-tsconfig-paths": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", + "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "globrex": "^0.1.2", + "tsconfck": "^3.0.3" + }, + "peerDependencies": { + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/vite/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vitest": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.10.tgz", + "integrity": "sha512-2Fqty3MM9CDwOVet/jaQalYlbcjATZwPYGcqpiYQqgQ/dLC7GuHdISKgTYIVF/kaishKxLzleKWWfbSDklyIKg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.10", + "@vitest/mocker": "4.0.10", + "@vitest/pretty-format": "4.0.10", + "@vitest/runner": "4.0.10", + "@vitest/snapshot": "4.0.10", + "@vitest/spy": "4.0.10", + "@vitest/utils": "4.0.10", + "debug": "^4.4.3", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^0.3.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.0.0", + "why-is-node-running": "^2.3.0" + }, + "bin": { + "vitest": "vitest.mjs" + }, + "engines": { + "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@edge-runtime/vm": "*", + "@types/debug": "^4.1.12", + "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", + "@vitest/browser-playwright": "4.0.10", + "@vitest/browser-preview": "4.0.10", + "@vitest/browser-webdriverio": "4.0.10", + "@vitest/ui": "4.0.10", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@types/debug": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "node_modules/vitest/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/vlq": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/vlq/-/vlq-2.0.4.tgz", + "integrity": "sha512-aodjPa2wPQFkra1G8CzJBTHXhgk3EVSwxSWXNPr1fgdFLUb8kvLV1iEb6rFgasIsjP82HWI6dsb5Io26DDnasA==", + "license": "MIT" + }, + "node_modules/w3c-xmlserializer": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "xml-name-validator": "^5.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/webidl-conversions": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.0.tgz", + "integrity": "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=20" + } + }, + "node_modules/webpack-virtual-modules": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", + "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", + "devOptional": true, + "license": "MIT" + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-url": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz", + "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", + "dev": true, "license": "MIT", "dependencies": { - "debug": "^4.1.1", - "globrex": "^0.1.2", - "tsconfck": "^3.0.3" + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.0" }, - "peerDependencies": { - "vite": "*" + "engines": { + "node": ">=20" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" }, - "peerDependenciesMeta": { - "vite": { - "optional": true - } + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/vite/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "node_modules/why-is-node-running": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "dev": true, "license": "MIT", - "engines": { - "node": ">=12.0.0" + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" }, - "peerDependencies": { - "picomatch": "^3 || ^4" + "bin": { + "why-is-node-running": "cli.js" }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } + "engines": { + "node": ">=8" } }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/widest-line": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", + "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", "license": "MIT", + "dependencies": { + "string-width": "^5.0.1" + }, "engines": { "node": ">=12" }, "funding": { - "url": "https://github.com/sponsors/jonschlinkert" + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/vitest": { - "version": "4.0.10", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.10.tgz", - "integrity": "sha512-2Fqty3MM9CDwOVet/jaQalYlbcjATZwPYGcqpiYQqgQ/dLC7GuHdISKgTYIVF/kaishKxLzleKWWfbSDklyIKg==", + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", - "dependencies": { - "@vitest/expect": "4.0.10", - "@vitest/mocker": "4.0.10", - "@vitest/pretty-format": "4.0.10", - "@vitest/runner": "4.0.10", - "@vitest/snapshot": "4.0.10", - "@vitest/spy": "4.0.10", - "@vitest/utils": "4.0.10", - "debug": "^4.4.3", - "es-module-lexer": "^1.7.0", - "expect-type": "^1.2.2", - "magic-string": "^0.30.21", - "pathe": "^2.0.3", - "picomatch": "^4.0.3", - "std-env": "^3.10.0", - "tinybench": "^2.9.0", - "tinyexec": "^0.3.2", - "tinyglobby": "^0.2.15", - "tinyrainbow": "^3.0.3", - "vite": "^6.0.0 || ^7.0.0", - "why-is-node-running": "^2.3.0" + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/workerd": { + "version": "1.20250718.0", + "resolved": "https://registry.npmjs.org/workerd/-/workerd-1.20250718.0.tgz", + "integrity": "sha512-kqkIJP/eOfDlUyBzU7joBg+tl8aB25gEAGqDap+nFWb+WHhnooxjGHgxPBy3ipw2hnShPFNOQt5lFRxbwALirg==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "bin": { + "workerd": "bin/workerd" + }, + "engines": { + "node": ">=16" + }, + "optionalDependencies": { + "@cloudflare/workerd-darwin-64": "1.20250718.0", + "@cloudflare/workerd-darwin-arm64": "1.20250718.0", + "@cloudflare/workerd-linux-64": "1.20250718.0", + "@cloudflare/workerd-linux-arm64": "1.20250718.0", + "@cloudflare/workerd-windows-64": "1.20250718.0" + } + }, + "node_modules/wrangler": { + "version": "3.114.17", + "resolved": "https://registry.npmjs.org/wrangler/-/wrangler-3.114.17.tgz", + "integrity": "sha512-tAvf7ly+tB+zwwrmjsCyJ2pJnnc7SZhbnNwXbH+OIdVas3zTSmjcZOjmLKcGGptssAA3RyTKhcF9BvKZzMUycA==", + "dev": true, + "license": "MIT OR Apache-2.0", + "dependencies": { + "@cloudflare/kv-asset-handler": "0.3.4", + "@cloudflare/unenv-preset": "2.0.2", + "@esbuild-plugins/node-globals-polyfill": "0.2.3", + "@esbuild-plugins/node-modules-polyfill": "0.2.2", + "blake3-wasm": "2.1.5", + "esbuild": "0.17.19", + "miniflare": "3.20250718.3", + "path-to-regexp": "6.3.0", + "unenv": "2.0.0-rc.14", + "workerd": "1.20250718.0" }, "bin": { - "vitest": "vitest.mjs" + "wrangler": "bin/wrangler.js", + "wrangler2": "bin/wrangler.js" }, "engines": { - "node": "^20.0.0 || ^22.0.0 || >=24.0.0" + "node": ">=16.17.0" }, - "funding": { - "url": "https://opencollective.com/vitest" + "optionalDependencies": { + "fsevents": "~2.3.2", + "sharp": "^0.33.5" }, "peerDependencies": { - "@edge-runtime/vm": "*", - "@types/debug": "^4.1.12", - "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "4.0.10", - "@vitest/browser-preview": "4.0.10", - "@vitest/browser-webdriverio": "4.0.10", - "@vitest/ui": "4.0.10", - "happy-dom": "*", - "jsdom": "*" + "@cloudflare/workers-types": "^4.20250408.0" }, "peerDependenciesMeta": { - "@edge-runtime/vm": { - "optional": true - }, - "@types/debug": { - "optional": true - }, - "@types/node": { - "optional": true - }, - "@vitest/browser-playwright": { - "optional": true - }, - "@vitest/browser-preview": { - "optional": true - }, - "@vitest/browser-webdriverio": { - "optional": true - }, - "@vitest/ui": { - "optional": true - }, - "happy-dom": { - "optional": true - }, - "jsdom": { + "@cloudflare/workers-types": { "optional": true } } }, - "node_modules/vitest/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "node_modules/wrangler/node_modules/@esbuild/android-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.17.19.tgz", + "integrity": "sha512-rIKddzqhmav7MSmoFCmDIb6e2W57geRsM94gV2l38fzhXMwq7hZoClug9USI2pFRGL06f4IOPHHpFNOkWieR8A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/wrangler/node_modules/@esbuild/android-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.17.19.tgz", + "integrity": "sha512-KBMWvEZooR7+kzY0BtbTQn0OAYY7CsiydT63pVEaPtVYF0hXbUaOyZog37DKxK7NF3XacBJOpYT4adIJh+avxA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/wrangler/node_modules/@esbuild/android-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.17.19.tgz", + "integrity": "sha512-uUTTc4xGNDT7YSArp/zbtmbhO0uEEK9/ETW29Wk1thYUJBz3IVnvgEiEwEa9IeLyvnpKrWK64Utw2bgUmDveww==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/wrangler/node_modules/@esbuild/darwin-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.17.19.tgz", + "integrity": "sha512-80wEoCfF/hFKM6WE1FyBHc9SfUblloAWx6FJkFWTWiCoht9Mc0ARGEM47e67W9rI09YoUxJL68WHfDRYEAvOhg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/wrangler/node_modules/@esbuild/darwin-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.17.19.tgz", + "integrity": "sha512-IJM4JJsLhRYr9xdtLytPLSH9k/oxR3boaUIYiHkAawtwNOXKE8KoU8tMvryogdcT8AU+Bflmh81Xn6Q0vTZbQw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/wrangler/node_modules/@esbuild/freebsd-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.17.19.tgz", + "integrity": "sha512-pBwbc7DufluUeGdjSU5Si+P3SoMF5DQ/F/UmTSb8HXO80ZEAJmrykPyzo1IfNbAoaqw48YRpv8shwd1NoI0jcQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/wrangler/node_modules/@esbuild/freebsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.17.19.tgz", + "integrity": "sha512-4lu+n8Wk0XlajEhbEffdy2xy53dpR06SlzvhGByyg36qJw6Kpfk7cp45DR/62aPH9mtJRmIyrXAS5UWBrJT6TQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/wrangler/node_modules/@esbuild/linux-arm": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.17.19.tgz", + "integrity": "sha512-cdmT3KxjlOQ/gZ2cjfrQOtmhG4HJs6hhvm3mWSRDPtZ/lP5oe8FWceS10JaSJC13GBd4eH/haHnqf7hhGNLerA==", + "cpu": [ + "arm" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/vlq": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/vlq/-/vlq-2.0.4.tgz", - "integrity": "sha512-aodjPa2wPQFkra1G8CzJBTHXhgk3EVSwxSWXNPr1fgdFLUb8kvLV1iEb6rFgasIsjP82HWI6dsb5Io26DDnasA==", - "license": "MIT" + "node_modules/wrangler/node_modules/@esbuild/linux-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.17.19.tgz", + "integrity": "sha512-ct1Tg3WGwd3P+oZYqic+YZF4snNl2bsnMKRkb3ozHmnM0dGWuxcPTTntAF6bOP0Sp4x0PjSF+4uHQ1xvxfRKqg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/w3c-xmlserializer": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", - "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", + "node_modules/wrangler/node_modules/@esbuild/linux-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.17.19.tgz", + "integrity": "sha512-w4IRhSy1VbsNxHRQpeGCHEmibqdTUx61Vc38APcsRbuVgK0OPEnQ0YD39Brymn96mOx48Y2laBQGqgZ0j9w6SQ==", + "cpu": [ + "ia32" + ], "dev": true, "license": "MIT", - "dependencies": { - "xml-name-validator": "^5.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18" + "node": ">=12" } }, - "node_modules/webidl-conversions": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.0.tgz", - "integrity": "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==", + "node_modules/wrangler/node_modules/@esbuild/linux-loong64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.17.19.tgz", + "integrity": "sha512-2iAngUbBPMq439a+z//gE+9WBldoMp1s5GWsUSgqHLzLJ9WoZLZhpwWuym0u0u/4XmZ3gpHmzV84PonE+9IIdQ==", + "cpu": [ + "loong64" + ], "dev": true, - "license": "BSD-2-Clause", + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=20" + "node": ">=12" } }, - "node_modules/webpack-virtual-modules": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/webpack-virtual-modules/-/webpack-virtual-modules-0.6.2.tgz", - "integrity": "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ==", - "devOptional": true, - "license": "MIT" + "node_modules/wrangler/node_modules/@esbuild/linux-mips64el": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.17.19.tgz", + "integrity": "sha512-LKJltc4LVdMKHsrFe4MGNPp0hqDFA1Wpt3jE1gEyM3nKUvOiO//9PheZZHfYRfYl6AwdTH4aTcXSqBerX0ml4A==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } }, - "node_modules/whatwg-encoding": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", - "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "node_modules/wrangler/node_modules/@esbuild/linux-ppc64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.17.19.tgz", + "integrity": "sha512-/c/DGybs95WXNS8y3Ti/ytqETiW7EU44MEKuCAcpPto3YjQbyK3IQVKfF6nbghD7EcLUGl0NbiL5Rt5DMhn5tg==", + "cpu": [ + "ppc64" + ], "dev": true, "license": "MIT", - "dependencies": { - "iconv-lite": "0.6.3" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18" + "node": ">=12" } }, - "node_modules/whatwg-mimetype": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", - "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "node_modules/wrangler/node_modules/@esbuild/linux-riscv64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.17.19.tgz", + "integrity": "sha512-FC3nUAWhvFoutlhAkgHf8f5HwFWUL6bYdvLc/TTuxKlvLi3+pPzdZiFKSWz/PF30TB1K19SuCxDTI5KcqASJqA==", + "cpu": [ + "riscv64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=18" + "node": ">=12" } }, - "node_modules/whatwg-url": { - "version": "15.1.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz", - "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", + "node_modules/wrangler/node_modules/@esbuild/linux-s390x": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.17.19.tgz", + "integrity": "sha512-IbFsFbxMWLuKEbH+7sTkKzL6NJmG2vRyy6K7JJo55w+8xDk7RElYn6xvXtDW8HCfoKBFK69f3pgBJSUSQPr+4Q==", + "cpu": [ + "s390x" + ], "dev": true, "license": "MIT", - "dependencies": { - "tr46": "^6.0.0", - "webidl-conversions": "^8.0.0" - }, + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">=20" + "node": ">=12" } }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, + "node_modules/wrangler/node_modules/@esbuild/linux-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.17.19.tgz", + "integrity": "sha512-68ngA9lg2H6zkZcyp22tsVt38mlhWde8l3eJLWkyLrp4HwMUr3c1s/M2t7+kHIhvMjglIBrFpncX1SzMckomGw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": ">= 8" + "node": ">=12" } }, - "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "node_modules/wrangler/node_modules/@esbuild/netbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.17.19.tgz", + "integrity": "sha512-CwFq42rXCR8TYIjIfpXCbRX0rp1jo6cPIUPSaWwzbVI4aOfX96OXY8M6KNmtPcg7QjYeDmN+DD0Wp3LaBOLf4Q==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, - "bin": { - "why-is-node-running": "cli.js" - }, + "optional": true, + "os": [ + "netbsd" + ], "engines": { - "node": ">=8" + "node": ">=12" } }, - "node_modules/widest-line": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", - "integrity": "sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==", + "node_modules/wrangler/node_modules/@esbuild/openbsd-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.17.19.tgz", + "integrity": "sha512-cnq5brJYrSZ2CF6c35eCmviIN3k3RczmHz8eYaVlNasVqsNY+JKohZU5MKmaOI+KkllCdzOKKdPs762VCPC20g==", + "cpu": [ + "x64" + ], + "dev": true, "license": "MIT", - "dependencies": { - "string-width": "^5.0.1" - }, + "optional": true, + "os": [ + "openbsd" + ], "engines": { "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "node_modules/wrangler/node_modules/@esbuild/sunos-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.17.19.tgz", + "integrity": "sha512-vCRT7yP3zX+bKWFeP/zdS6SqdWB8OIpaRq/mbXQxTGHnIxspRtigpkUcDMlSCOejlHowLqII7K2JKevwyRP2rg==", + "cpu": [ + "x64" + ], "dev": true, "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], "engines": { - "node": ">=0.10.0" + "node": ">=12" + } + }, + "node_modules/wrangler/node_modules/@esbuild/win32-arm64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.17.19.tgz", + "integrity": "sha512-yYx+8jwowUstVdorcMdNlzklLYhPxjniHWFKgRqH7IFlUEa0Umu3KuYplf1HUZZ422e3NU9F4LGb+4O0Kdcaag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/wrangler/node_modules/@esbuild/win32-ia32": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.17.19.tgz", + "integrity": "sha512-eggDKanJszUtCdlVs0RB+h35wNlb5v4TWEkq4vZcmVt5u/HiDZrTXe2bWFQUez3RgNHwx/x4sk5++4NSSicKkw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/wrangler/node_modules/@esbuild/win32-x64": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.17.19.tgz", + "integrity": "sha512-lAhycmKnVOuRYNtRtatQR1LPQf2oYCkRGkSFnseDAKPl8lu5SOsK/e1sXe5a0Pc5kHIHe6P2I/ilntNv2xf3cA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" } }, + "node_modules/wrangler/node_modules/esbuild": { + "version": "0.17.19", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", + "integrity": "sha512-XQ0jAPFkK/u3LcVRcvVHQcTIqD6E2H1fvZMA5dQPSOWb3suUbWbfbRf94pjc0bNzRYLfIrDRQXr7X+LHIm5oHw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/android-arm": "0.17.19", + "@esbuild/android-arm64": "0.17.19", + "@esbuild/android-x64": "0.17.19", + "@esbuild/darwin-arm64": "0.17.19", + "@esbuild/darwin-x64": "0.17.19", + "@esbuild/freebsd-arm64": "0.17.19", + "@esbuild/freebsd-x64": "0.17.19", + "@esbuild/linux-arm": "0.17.19", + "@esbuild/linux-arm64": "0.17.19", + "@esbuild/linux-ia32": "0.17.19", + "@esbuild/linux-loong64": "0.17.19", + "@esbuild/linux-mips64el": "0.17.19", + "@esbuild/linux-ppc64": "0.17.19", + "@esbuild/linux-riscv64": "0.17.19", + "@esbuild/linux-s390x": "0.17.19", + "@esbuild/linux-x64": "0.17.19", + "@esbuild/netbsd-x64": "0.17.19", + "@esbuild/openbsd-x64": "0.17.19", + "@esbuild/sunos-x64": "0.17.19", + "@esbuild/win32-arm64": "0.17.19", + "@esbuild/win32-ia32": "0.17.19", + "@esbuild/win32-x64": "0.17.19" + } + }, + "node_modules/wrangler/node_modules/path-to-regexp": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", + "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", + "dev": true, + "license": "MIT" + }, "node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", @@ -9660,6 +11130,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/youch": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/youch/-/youch-3.3.4.tgz", + "integrity": "sha512-UeVBXie8cA35DS6+nBkls68xaBBXCye0CNznrhszZjTbRVnJKQuNsyLKBTTL4ln1o1rh2PKtv35twV7irj5SEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cookie": "^0.7.1", + "mustache": "^4.2.0", + "stacktracey": "^2.1.8" + } + }, "node_modules/zod": { "version": "3.25.76", "resolved": "https://registry.npmjs.org/zod/-/zod-3.25.76.tgz", diff --git a/package.json b/package.json index c26ec31..a95f6eb 100644 --- a/package.json +++ b/package.json @@ -30,7 +30,8 @@ "prettier:check": "prettier --check .", "test:watch": "vitest", "test": "vitest --run", - "ci": "npm run lint && npm run prettier:fix && npm run type:check && npm run build && vitest --run" + "ci": "npm run lint && npm run prettier:fix && npm run type:check && npm run build && vitest --run", + "fetch-prices": "node scripts/fetch-algo-price-history.js" }, "dependencies": { "@algorandfoundation/algokit-utils": "^9.1.2", @@ -97,7 +98,8 @@ "typescript": "~5.9.3", "typescript-eslint": "^8.47.0", "vite": "^7.2.2", - "vitest": "^4.0.10" + "vitest": "^4.0.10", + "wrangler": "^3.109.2" }, "engines": { "node": ">=22.0.0", diff --git a/scripts/fetch-algo-price-history.js b/scripts/fetch-algo-price-history.js new file mode 100644 index 0000000..56505fa --- /dev/null +++ b/scripts/fetch-algo-price-history.js @@ -0,0 +1,122 @@ +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); + +const SYMBOL = "ALGOUSDT"; +const INTERVAL = "1d"; +const START_DATE = "2019-09-20"; +const OUTPUT_FILE = path.join(__dirname, "../src/data/algo-price-history.json"); +const MEXC_API = "https://api.mexc.com/api/v3/klines"; + +async function fetchKlines(startTime) { + const url = `${MEXC_API}?symbol=${SYMBOL}&interval=${INTERVAL}&startTime=${startTime}&limit=1000`; + console.log(`Fetching: ${url}`); + const response = await fetch(url); + if (!response.ok) { + const text = await response.text(); + throw new Error( + `Failed to fetch: ${response.status} ${response.statusText} - ${text}`, + ); + } + return response.json(); +} + +function formatDate(timestamp) { + const date = new Date(timestamp); + return date.toISOString().split("T")[0]; +} + +async function main() { + let existingData = {}; + if (fs.existsSync(OUTPUT_FILE)) { + try { + existingData = JSON.parse(fs.readFileSync(OUTPUT_FILE, "utf8")); + console.log( + `Loaded ${Object.keys(existingData).length} existing entries.`, + ); + } catch (e) { + console.error("Error reading existing file, starting fresh."); + } + } + + // Ensure src/data exists + const dataDir = path.dirname(OUTPUT_FILE); + if (!fs.existsSync(dataDir)) { + fs.mkdirSync(dataDir, { recursive: true }); + } + + const now = new Date(); + const today = Date.UTC( + now.getUTCFullYear(), + now.getUTCMonth(), + now.getUTCDate(), + ); + let currentStartTime = new Date(START_DATE).getTime(); + + // If we have existing data, we can start from the last date + 1 day + const dates = Object.keys(existingData).sort(); + if (dates.length > 0) { + const lastDate = dates[dates.length - 1]; + const lastTime = new Date(lastDate).getTime(); + currentStartTime = lastTime + 24 * 60 * 60 * 1000; + + // If lastTime is already today or later, we don't need to fetch + if (currentStartTime >= today) { + console.log("Already up to date."); + return; + } + + console.log(`Starting/Resuming from ${formatDate(currentStartTime)}`); + } else { + console.log(`Starting from scratch: ${START_DATE}`); + } + + let newDataCount = 0; + while (currentStartTime < today) { + console.log(`Fetching from ${formatDate(currentStartTime)}...`); + const klines = await fetchKlines(currentStartTime); + + if (klines.length === 0) break; + + for (const candle of klines) { + const openTime = candle[0]; + const closePrice = parseFloat(candle[4]); + const dateStr = formatDate(openTime); + + if (openTime < today) { + existingData[dateStr] = closePrice; + newDataCount++; + } + + currentStartTime = openTime + 24 * 60 * 60 * 1000; + } + + // Small delay to avoid aggressive rate limiting + await new Promise((resolve) => setTimeout(resolve, 100)); + } + + if (newDataCount > 0) { + // Sort keys before writing + const sortedData = {}; + Object.keys(existingData) + .sort() + .forEach((key) => { + sortedData[key] = existingData[key]; + }); + + fs.writeFileSync(OUTPUT_FILE, JSON.stringify(sortedData, null, 2)); + console.log( + `Done! Added ${newDataCount} new entries. Total: ${Object.keys(sortedData).length}`, + ); + } else { + console.log("Already up to date."); + } +} + +main().catch((err) => { + console.error(err); + process.exit(1); +}); diff --git a/src/components/address/address-view.tsx b/src/components/address/address-view.tsx index 8ffa6c1..aadea05 100644 --- a/src/components/address/address-view.tsx +++ b/src/components/address/address-view.tsx @@ -22,6 +22,9 @@ const AccountStatus = lazy(() => import("./stats/status/status")); const CumulativeRewardsChart = lazy( () => import("@/components/address/charts/cumulative-rewards-chart"), ); +const PriceImpactSection = lazy( + () => import("@/components/address/price-impact-section"), +); const CumulativeBlocksChart = lazy( () => import("@/components/address/charts/cumulative-blocks-chart"), ); @@ -288,6 +291,15 @@ export default function AddressView({ addresses }: { addresses: string }) { + + }> + + + + }> diff --git a/src/components/address/charts/cumulative-rewards-chart.tsx b/src/components/address/charts/cumulative-rewards-chart.tsx index e41e588..aaf8541 100644 --- a/src/components/address/charts/cumulative-rewards-chart.tsx +++ b/src/components/address/charts/cumulative-rewards-chart.tsx @@ -1,4 +1,4 @@ -import React, { useMemo } from "react"; +import React, { useMemo, useState } from "react"; import { MinimalBlock } from "@/lib/block-types"; import { Area, @@ -16,6 +16,11 @@ import { generateDateRange } from "@/lib/date-utils"; import AlgoAmountDisplay from "@/components/algo-amount-display"; import { useIsSmallScreen } from "@/hooks/useIsSmallScreen.ts"; +import { useSearch } from "@tanstack/react-router"; +import { useAlgoPrice } from "@/hooks/queries/useAlgoPrice"; +import { getAlgoUsdPrice } from "@/utils/algoPrice"; +import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; +import { CURRENCIES } from "@/lib/currencies"; type ChartData = { date: string; @@ -23,6 +28,8 @@ type ChartData = { dailyRewards: number; cumulativeAlgos: number; dailyAlgos: number; + cumulativeFiat: number | null; + dailyFiat: number | null; }; const CumulativeRewardsChart = React.memo(function CumulativeRewardsChart({ @@ -34,6 +41,23 @@ const CumulativeRewardsChart = React.memo(function CumulativeRewardsChart({ }) { const { theme } = useTheme(); const isSmall = useIsSmallScreen(640); + const search = useSearch({ from: "/$addresses" }); + const currency = search.currency || "USD"; + const [displayMode, setDisplayMode] = useState<"algo" | "fiat">("algo"); + + const { data: currentUsdPrice } = useAlgoPrice("USD"); + const { data: currentFiatPrice } = useAlgoPrice(currency); + + const usdToFiat = useMemo(() => { + if (currency === "USD") return 1; + return currentUsdPrice && currentFiatPrice + ? currentFiatPrice / currentUsdPrice + : 1; + }, [currency, currentUsdPrice, currentFiatPrice]); + + const currencyInfo = useMemo(() => { + return CURRENCIES.find((c) => c.value === currency) || CURRENCIES[0]; + }, [currency]); const data = useMemo(() => { if (!blocks.length) return []; @@ -49,37 +73,23 @@ const CumulativeRewardsChart = React.memo(function CumulativeRewardsChart({ sortedBlocks.forEach((block) => { if (!block.timestamp) return; - // Convert UTC timestamp to local date string (user's timezone) const date = new Date(block.timestamp * 1000); - // Use local timezone formatting instead of UTC ISO string const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); const day = String(date.getDate()).padStart(2, "0"); - const dateStr = `${year}-${month}-${day}`; // YYYY-MM-DD format in local timezone + const dateStr = `${year}-${month}-${day}`; const reward = block.proposerPayout || 0; const currentDayReward = dailyRewards.get(dateStr) || 0; dailyRewards.set(dateStr, currentDayReward + reward); }); - // Create an array of all dates between first block and today - const firstBlockDate = - sortedBlocks.length > 0 && sortedBlocks[0].timestamp - ? new Date(sortedBlocks[0].timestamp * 1000) - : new Date(); - - // Set to start of day in local timezone - firstBlockDate.setHours(0, 0, 0, 0); - - const today = new Date(); - today.setHours(0, 0, 0, 0); - const allDates = generateDateRange( sortedBlocks.length > 0 ? sortedBlocks[0].timestamp : undefined, ); - // Build cumulative data with zero values for days with no rewards let cumulativeRewards = 0; + let cumulativeFiat = 0; const chartData: ChartData[] = []; @@ -87,23 +97,31 @@ const CumulativeRewardsChart = React.memo(function CumulativeRewardsChart({ const dayReward = dailyRewards.get(dateStr) || 0; cumulativeRewards += dayReward; - // Ensure these are integer values by using Math.floor const dailyRewardInt = Math.floor(dayReward); const cumulativeRewardsInt = Math.floor(cumulativeRewards); + const usdPrice = getAlgoUsdPrice(dateStr); + const fiatPrice = usdPrice !== null ? usdPrice * usdToFiat : null; + + const dailyFiat = + fiatPrice !== null ? (dayReward / 1e6) * fiatPrice : null; + if (dailyFiat !== null) { + cumulativeFiat += dailyFiat; + } + chartData.push({ date: dateStr, - // Store integer microAlgo values for AlgoAmountDisplay cumulativeRewards: cumulativeRewardsInt, dailyRewards: dailyRewardInt, - // Store display values for axis labels cumulativeAlgos: cumulativeRewardsInt / 1e6, dailyAlgos: dailyRewardInt / 1e6, + dailyFiat: dailyFiat, + cumulativeFiat: cumulativeFiat, }); }); return chartData; - }, [blocks]); + }, [blocks, usdToFiat]); if (!data.length) { return ( @@ -141,13 +159,50 @@ const CumulativeRewardsChart = React.memo(function CumulativeRewardsChart({ }); }; + const formatCurrency = (value: number) => { + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: currency, + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(value); + }; + const textColor = theme === "dark" ? "#d1d5db" : "#374151"; + const isFiat = displayMode === "fiat"; + const cumulativeDataKey = isFiat ? "cumulativeFiat" : "cumulativeAlgos"; + const dailyDataKey = isFiat ? "dailyFiat" : "dailyAlgos"; + return (
-

- Rewards History -

+
+

+ Rewards History +

+ { + if (value) setDisplayMode(value as "algo" | "fiat"); + }} + className="gap-0 rounded-md border border-gray-200 bg-white p-0.5 dark:border-gray-700 dark:bg-gray-800" + > + + ALGO + + + {currency} + + +
+
`${value.toFixed(0)}`} - width={40} + tickFormatter={(value) => + isFiat + ? `${currencyInfo.symbol}${value.toFixed(0)}` + : `${value.toFixed(0)}` + } + width={isFiat ? 55 : 40} axisLine={false} tickLine={false} /> `${value.toFixed()}`} - width={40} + tickFormatter={(value) => + isFiat ? `${value.toFixed(2)}` : `${value.toFixed()}` + } + width={isFiat ? 50 : 40} axisLine={false} tickLine={false} /> { + formatter={(value, name, entry) => { const dataPoint = entry.payload; + if (isFiat) { + const val = value as number | null; + const displayVal = + val === null ? "No price data" : formatCurrency(val); + return [ + + {hideBalance ? "*****" : displayVal} + , + name, + ]; + } + if (name === "Total Rewards") { return [ diff --git a/src/components/address/charts/daily-reward-fiat-value-chart.tsx b/src/components/address/charts/daily-reward-fiat-value-chart.tsx new file mode 100644 index 0000000..edfcd73 --- /dev/null +++ b/src/components/address/charts/daily-reward-fiat-value-chart.tsx @@ -0,0 +1,231 @@ +import React from "react"; +import { + Bar, + Line, + XAxis, + YAxis, + Tooltip, + ResponsiveContainer, + CartesianGrid, + Legend, + ComposedChart, +} from "recharts"; +import { useTheme } from "@/components/theme-provider"; +import { PriceImpactDataPoint } from "@/hooks/usePriceImpactData"; + +interface Props { + data: PriceImpactDataPoint[]; + hideBalance?: boolean; +} + +const DailyRewardFiatValueChart = React.memo( + function DailyRewardFiatValueChart({ data, hideBalance }: Props) { + const { theme } = useTheme(); + + const parseISODate = (dateStr: string) => { + const arr = dateStr.split("-").map((s) => Number(s)); + return new Date(arr[0], --arr[1], arr[2]); + }; + + const formatDate = (dateStr: string) => { + const date = parseISODate(dateStr); + return date.toLocaleDateString(undefined, { + month: "short", + day: "numeric", + }); + }; + + const formatTooltipDate = (dateStr: string) => { + const date = parseISODate(dateStr); + return date.toLocaleDateString(undefined, { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + }); + }; + + const formatCurrency = (value: number) => { + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(value); + }; + + const textColor = theme === "dark" ? "#d1d5db" : "#374151"; + + // Existing Rewards History bar color + const barColor = "#60a8fb"; + // Amber for the "value when received" line to match Chart A + const lineSeriesColor = "#f59e0b"; + + return ( +
+

+ Daily Rewards: ALGO vs USD Value at Receipt +

+
+ + + + + + `$${v.toFixed(2)}`} + width={45} + axisLine={false} + tickLine={false} + label={{ + value: "USD value", + angle: 90, + position: "insideRight", + style: { fontSize: "10px", fill: lineSeriesColor }, + }} + /> + , + name: string, + ) => { + const numValue = + typeof value === "number" ? value : Number(value); + if (name === "USD Value at Receipt") { + const val = !isNaN(numValue) + ? formatCurrency(numValue) + : "Price missing"; + return [hideBalance ? "*****" : val, name]; + } + const algoVal = + numValue === 0 + ? "No reward" + : `${numValue.toFixed(4)} ALGO`; + return [hideBalance ? "*****" : algoVal, name]; + }} + // Add price to tooltip + itemSorter={(item) => + item.name === "Daily ALGO Reward" ? 1 : 0 + } + content={({ active, payload, label }) => { + if ( + active && + payload && + payload.length && + typeof label === "string" + ) { + const dataPoint = payload[0] + .payload as PriceImpactDataPoint; + return ( +
+

+ {formatTooltipDate(label)} +

+
+

+ + ALGO: + {" "} + {hideBalance + ? "*****" + : dataPoint.rewardAlgo === 0 + ? "No reward" + : `${dataPoint.rewardAlgo.toFixed(4)}`} +

+

+ + Value at Receipt: + {" "} + {hideBalance + ? "*****" + : dataPoint.rewardAlgo === 0 + ? "$0.00" + : dataPoint.valueAtReceipt !== null + ? formatCurrency(dataPoint.valueAtReceipt) + : "Price missing"} +

+

+ + ALGO Price: + {" "} + {dataPoint.priceAtReceipt !== null + ? `$${dataPoint.priceAtReceipt.toFixed(4)}` + : "Unknown"} +

+
+
+ ); + } + return null; + }} + /> + + + + + +
+
+
+
+ ); + }, +); + +export default DailyRewardFiatValueChart; diff --git a/src/components/address/charts/price-vs-blocks-chart.tsx b/src/components/address/charts/price-vs-blocks-chart.tsx new file mode 100644 index 0000000..824a42d --- /dev/null +++ b/src/components/address/charts/price-vs-blocks-chart.tsx @@ -0,0 +1,196 @@ +import React, { useState } from "react"; +import { + Bar, + Line, + XAxis, + YAxis, + Tooltip, + ResponsiveContainer, + CartesianGrid, + Legend, + ComposedChart, +} from "recharts"; +import { useTheme } from "@/components/theme-provider"; +import { PriceImpactDataPoint } from "@/hooks/usePriceImpactData"; +import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group"; + +interface Props { + data: PriceImpactDataPoint[]; +} + +const PriceVsBlocksChart = React.memo(function PriceVsBlocksChart({ + data, +}: Props) { + const { theme } = useTheme(); + const [metric, setMetric] = useState<"blocks" | "reward">("blocks"); + + const parseISODate = (dateStr: string) => { + const arr = dateStr.split("-").map((s) => Number(s)); + return new Date(arr[0], --arr[1], arr[2]); + }; + + const formatDate = (dateStr: string) => { + const date = parseISODate(dateStr); + return date.toLocaleDateString(undefined, { + month: "short", + day: "numeric", + }); + }; + + const formatTooltipDate = (dateStr: string) => { + const date = parseISODate(dateStr); + return date.toLocaleDateString(undefined, { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + }); + }; + + const textColor = theme === "dark" ? "#d1d5db" : "#374151"; + + // Interpolate missing prices for a continuous line + const interpolatedData = React.useMemo(() => { + const result = [...data]; + for (let i = 0; i < result.length; i++) { + if (result[i].priceAtReceipt === null) { + // Find next available price + let nextIndex = i + 1; + while ( + nextIndex < result.length && + result[nextIndex].priceAtReceipt === null + ) { + nextIndex++; + } + + if (nextIndex < result.length && i > 0) { + const prevPrice = result[i - 1].priceAtReceipt!; + const nextPrice = result[nextIndex].priceAtReceipt!; + const step = (nextPrice - prevPrice) / (nextIndex - (i - 1)); + result[i] = { + ...result[i], + priceAtReceipt: prevPrice + step * (i - (i - 1)), + }; + } else if (i > 0) { + result[i] = { + ...result[i], + priceAtReceipt: result[i - 1].priceAtReceipt!, + }; + } else if (nextIndex < result.length) { + result[i] = { + ...result[i], + priceAtReceipt: result[nextIndex].priceAtReceipt!, + }; + } + } + } + return result; + }, [data]); + + return ( +
+
+

+ Block Activity vs ALGO Price +

+ v && setMetric(v as "blocks" | "reward")} + className="gap-0 rounded-md border border-gray-200 bg-white p-0.5 dark:border-gray-700 dark:bg-gray-800" + > + + Blocks + + + ALGO + + +
+ +
+ + + + + + `$${v.toFixed(2)}`} + width={45} + axisLine={false} + tickLine={false} + /> + , + name: string, + ) => { + const numValue = + typeof value === "number" ? value : Number(value); + if (name === "ALGO Price") + return [`$${numValue.toFixed(4)}`, name]; + return [value, name]; + }} + /> + + + + + + + +
+
+ ); +}); + +export default PriceVsBlocksChart; diff --git a/src/components/address/charts/reward-scatter-plot-chart.tsx b/src/components/address/charts/reward-scatter-plot-chart.tsx new file mode 100644 index 0000000..c4ce29a --- /dev/null +++ b/src/components/address/charts/reward-scatter-plot-chart.tsx @@ -0,0 +1,283 @@ +import React, { useMemo } from "react"; +import { + ScatterChart, + Scatter, + XAxis, + YAxis, + ZAxis, + Tooltip, + ResponsiveContainer, + CartesianGrid, + Cell, +} from "recharts"; +import { useTheme } from "@/components/theme-provider"; +import { PriceImpactDataPoint } from "@/hooks/usePriceImpactData"; + +interface Props { + data: PriceImpactDataPoint[]; + hideBalance?: boolean; +} + +const RewardScatterPlotChart = React.memo(function RewardScatterPlotChart({ + data, + hideBalance, +}: Props) { + const { theme } = useTheme(); + + const parseISODate = (dateStr: string) => { + const [year, month, day] = dateStr.split("-").map((s) => Number(s)); + return Date.UTC(year, month - 1, day); + }; + + const formatDate = (time: number) => { + return new Date(time).toLocaleDateString(undefined, { + month: "short", + day: "numeric", + }); + }; + + const formatTooltipDate = (time: number) => { + return new Date(time).toLocaleDateString(undefined, { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + }); + }; + + const formatCurrency = (value: number) => { + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + minimumFractionDigits: 2, + }).format(value); + }; + + // Convert dates to timestamps for X-axis + const chartData = useMemo(() => { + return data.map((d) => ({ + ...d, + timestamp: parseISODate(d.date), + })); + }, [data]); + + const { xMin, xMax } = useMemo(() => { + if (chartData.length === 0) { + const now = Date.now(); + return { xMin: now - 24 * 60 * 60 * 1000, xMax: now }; + } + + let minTs = Infinity; + let maxTs = -Infinity; + + chartData.forEach((d) => { + if (d.timestamp < minTs) minTs = d.timestamp; + if (d.timestamp > maxTs) maxTs = d.timestamp; + }); + + const span = Math.max(1, maxTs - minTs); + const padMs = Math.max(6 * 60 * 60 * 1000, Math.floor(span * 0.015)); + + const now = new Date(); + const todayEndUtc = Date.UTC( + now.getUTCFullYear(), + now.getUTCMonth(), + now.getUTCDate(), + 23, + 59, + 59, + 999, + ); + const paddedMin = minTs - padMs; + const clampedMax = Math.min(maxTs + padMs, todayEndUtc); + + return { + xMin: paddedMin, + xMax: Math.max(paddedMin, clampedMax), + }; + }, [chartData]); + + const { maxUSD, minUSD, minAlgo } = useMemo(() => { + let maxU = 0; + let minU = Infinity; + let minA = Infinity; + data.forEach((d) => { + if ((d.valueAtReceipt || 0) > maxU) maxU = d.valueAtReceipt || 0; + if ((d.valueAtReceipt || 0) < minU) minU = d.valueAtReceipt || 0; + if (d.rewardAlgo < minA) minA = d.rewardAlgo; + }); + return { + maxUSD: maxU, + minUSD: minU === Infinity ? 0 : minU, + minAlgo: minA === Infinity ? 0 : minA, + }; + }, [data]); + + const getColor = (usdValue: number) => { + const range = maxUSD - minUSD || 1; + const ratio = (usdValue - minUSD) / range; + + // Gradient: Blue -> Purple -> Pink -> Orange + // Removing Amber (#f59e0b) to avoid yellow tones as requested + if (ratio < 0.25) return "#312e81"; // Blue + if (ratio < 0.5) return "#7c3aed"; // Purple + if (ratio < 0.75) return "#db2777"; // Pink + return "#ea580c"; // Orange + }; + + const textColor = theme === "dark" ? "#d1d5db" : "#374151"; + const yMin = Math.max(0, Math.floor(minAlgo - 1)); + + return ( +
+
+

+ Reward Value at Receipt +

+

+ Reward size reflects Algorand's declining block reward schedule over + time. Colour indicates USD value at time of receipt using finalized + daily close prices (UTC), so the current UTC day may appear after the + next price refresh. +

+
+ +
+ + + + + + + { + if (active && payload && payload.length) { + const d = payload[0].payload as PriceImpactDataPoint & { + timestamp: number; + }; + return ( +
+

+ {formatTooltipDate(d.timestamp)} +

+
+

+ + Rewards: + + + {hideBalance + ? "*****" + : `${d.rewardAlgo.toFixed(4)} ALGO`} + +

+

+ + Value at Receipt: + + + {hideBalance + ? "*****" + : formatCurrency(d.valueAtReceipt || 0)} + +

+

+ + ALGO Price then: + + + ${d.priceAtReceipt?.toFixed(4)} + +

+
+
+ ); + } + return null; + }} + /> + + {chartData.map((entry, index) => ( + + ))} + +
+
+
+ + {/* Mini Legends */} +
+
+ + USD Value at Receipt + +
+ + Low + +
+ + High + +
+
+
+ + Size + +
+
+
+ + ALGO Amount + +
+
+
+
+ ); +}); + +export default RewardScatterPlotChart; diff --git a/src/components/address/charts/reward-value-comparison-chart.tsx b/src/components/address/charts/reward-value-comparison-chart.tsx new file mode 100644 index 0000000..2a88623 --- /dev/null +++ b/src/components/address/charts/reward-value-comparison-chart.tsx @@ -0,0 +1,181 @@ +import React from "react"; +import { + Area, + XAxis, + YAxis, + Tooltip, + ResponsiveContainer, + CartesianGrid, + Legend, + ComposedChart, +} from "recharts"; +import { useTheme } from "@/components/theme-provider"; +import { PriceImpactDataPoint } from "@/hooks/usePriceImpactData"; + +interface Props { + data: PriceImpactDataPoint[]; + hideBalance?: boolean; +} + +const RewardValueComparisonChart = React.memo( + function RewardValueComparisonChart({ data, hideBalance }: Props) { + const { theme } = useTheme(); + + const parseISODate = (dateStr: string) => { + const arr = dateStr.split("-").map((s) => Number(s)); + return new Date(arr[0], --arr[1], arr[2]); + }; + + const formatDate = (dateStr: string) => { + const date = parseISODate(dateStr); + return date.toLocaleDateString(undefined, { + month: "short", + day: "numeric", + }); + }; + + const formatTooltipDate = (dateStr: string) => { + const date = parseISODate(dateStr); + return date.toLocaleDateString(undefined, { + weekday: "long", + year: "numeric", + month: "long", + day: "numeric", + }); + }; + + const formatCurrency = (value: number) => { + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + minimumFractionDigits: 2, + maximumFractionDigits: 2, + }).format(value); + }; + + const textColor = theme === "dark" ? "#d1d5db" : "#374151"; + + // Prepare data for shaded area between lines + const chartData = data.map((d) => ({ + ...d, + // Provide range for the area + range: [d.cumulativeValueAtToday, d.cumulativeValueAtReceipt], + })); + + const isDepreciated = + data.length > 0 && + data[data.length - 1].cumulativeValueAtToday < + data[data.length - 1].cumulativeValueAtReceipt; + + return ( +
+

+ Cumulative Reward Value: Then vs Now +

+
+ + + + + + + + + + + `$${value.toFixed(0)}`} + width={45} + axisLine={false} + tickLine={false} + /> + , + name: string, + ) => { + if (name === "range") return null; + const numValue = + typeof value === "number" ? value : Number(value); + const val = !isNaN(numValue) + ? formatCurrency(numValue) + : String(value); + return [hideBalance ? "*****" : val, name]; + }} + /> + + + {/* Shaded area between lines */} + + + + + + + +
+
+ ); + }, +); + +export default RewardValueComparisonChart; diff --git a/src/components/address/price-impact-section.tsx b/src/components/address/price-impact-section.tsx new file mode 100644 index 0000000..5679304 --- /dev/null +++ b/src/components/address/price-impact-section.tsx @@ -0,0 +1,101 @@ +import { MinimalBlock } from "@/lib/block-types"; +import { usePriceImpactData } from "@/hooks/usePriceImpactData"; +import RewardValueComparisonChart from "./charts/reward-value-comparison-chart"; +import RewardScatterPlotChart from "./charts/reward-scatter-plot-chart"; +import { Skeleton } from "@/components/ui/skeleton"; + +interface Props { + blocks: MinimalBlock[]; + hideBalance?: boolean; +} + +export default function PriceImpactSection({ blocks, hideBalance }: Props) { + const { + fullData, + scatterData, + isLoading, + totalValueAtReceipt, + totalValueAtToday, + } = usePriceImpactData(blocks); + + if (isLoading || !fullData.length) { + return ( +
+
+ + + + +
+
+ +
+
+ ); + } + + const difference = totalValueAtToday - totalValueAtReceipt; + const isNegative = difference < 0; + + const formatCurrency = (val: number) => { + return new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + }).format(val); + }; + + return ( +
+ {/* Chart A: Cumulative story with Section Header and Stats */} +
+
+

+ Price Impact Analysis +

+

+ How ALGO's price movement has affected the real-world value of your + rewards over time. +

+
+ +
+

+ Your rewards were worth{" "} + + {hideBalance ? "*****" : formatCurrency(totalValueAtReceipt)} + {" "} + when received. At today's price they are worth{" "} + + {hideBalance ? "*****" : formatCurrency(totalValueAtToday)} + + . + {!hideBalance && ( + + ({isNegative ? "" : "+"} + {formatCurrency(difference)}) + + )} +

+
+
+ +
+
+ + {/* Chart B: Per-reward story (Scatter Plot) */} +
+
+ +
+
+
+ ); +} diff --git a/src/data/algo-price-history.json b/src/data/algo-price-history.json new file mode 100644 index 0000000..6790a28 --- /dev/null +++ b/src/data/algo-price-history.json @@ -0,0 +1,2351 @@ +{ + "2019-09-20": 0.321, + "2019-09-21": 0.315, + "2019-09-22": 0.3, + "2019-09-23": 0.267, + "2019-09-24": 0.193, + "2019-09-25": 0.205, + "2019-09-26": 0.189, + "2019-09-27": 0.189, + "2019-09-28": 0.204, + "2019-09-29": 0.225, + "2019-09-30": 0.25, + "2019-10-01": 0.231, + "2019-10-02": 0.233, + "2019-10-03": 0.23, + "2019-10-04": 0.229, + "2019-10-05": 0.224, + "2019-10-06": 0.213, + "2019-10-07": 0.219, + "2019-10-08": 0.226, + "2019-10-09": 0.239, + "2019-10-10": 0.253, + "2019-10-11": 0.253, + "2019-10-12": 0.257, + "2019-10-13": 0.251, + "2019-10-14": 0.267, + "2019-10-15": 0.248, + "2019-10-16": 0.228, + "2019-10-17": 0.237, + "2019-10-18": 0.226, + "2019-10-19": 0.223, + "2019-10-20": 0.224, + "2019-10-21": 0.2262, + "2019-10-22": 0.2183, + "2019-10-23": 0.2, + "2019-10-24": 0.201, + "2019-10-25": 0.2131, + "2019-10-26": 0.2156, + "2019-10-27": 0.2181, + "2019-10-28": 0.2301, + "2019-10-29": 0.2262, + "2019-10-30": 0.211, + "2019-10-31": 0.221, + "2019-11-01": 0.2596, + "2019-11-02": 0.2584, + "2019-11-03": 0.2639, + "2019-11-04": 0.2577, + "2019-11-05": 0.2834, + "2019-11-06": 0.2843, + "2019-11-07": 0.2709, + "2019-11-08": 0.2556, + "2019-11-09": 0.2623, + "2019-11-10": 0.2674, + "2019-11-11": 0.2826, + "2019-11-12": 0.2726, + "2019-11-13": 0.2873, + "2019-11-14": 0.277, + "2019-11-15": 0.2645, + "2019-11-16": 0.2721, + "2019-11-17": 0.2674, + "2019-11-18": 0.2489, + "2019-11-19": 0.2443, + "2019-11-20": 0.2539, + "2019-11-21": 0.247, + "2019-11-22": 0.2224, + "2019-11-23": 0.2286, + "2019-11-24": 0.2024, + "2019-11-25": 0.2069, + "2019-11-26": 0.2197, + "2019-11-27": 0.2248, + "2019-11-28": 0.2865, + "2019-11-29": 0.2761, + "2019-11-30": 0.2648, + "2019-12-01": 0.2651, + "2019-12-02": 0.2742, + "2019-12-03": 0.2879, + "2019-12-04": 0.2625, + "2019-12-05": 0.2646, + "2019-12-06": 0.2679, + "2019-12-07": 0.2753, + "2019-12-08": 0.2934, + "2019-12-09": 0.2944, + "2019-12-10": 0.2747, + "2019-12-11": 0.2867, + "2019-12-12": 0.2769, + "2019-12-13": 0.2719, + "2019-12-14": 0.2523, + "2019-12-15": 0.2661, + "2019-12-16": 0.2437, + "2019-12-17": 0.2187, + "2019-12-18": 0.2447, + "2019-12-19": 0.2297, + "2019-12-20": 0.2399, + "2019-12-21": 0.24, + "2019-12-22": 0.247, + "2019-12-23": 0.2403, + "2019-12-24": 0.2306, + "2019-12-25": 0.2275, + "2019-12-26": 0.2397, + "2019-12-27": 0.2295, + "2019-12-28": 0.2327, + "2019-12-29": 0.2317, + "2019-12-30": 0.2253, + "2019-12-31": 0.2168, + "2020-01-01": 0.2201, + "2020-01-02": 0.2139, + "2020-01-03": 0.2272, + "2020-01-04": 0.2352, + "2020-01-05": 0.2294, + "2020-01-06": 0.2427, + "2020-01-07": 0.2303, + "2020-01-08": 0.2284, + "2020-01-09": 0.2222, + "2020-01-10": 0.226, + "2020-01-11": 0.2264, + "2020-01-12": 0.2283, + "2020-01-13": 0.227, + "2020-01-14": 0.2296, + "2020-01-15": 0.2474, + "2020-01-16": 0.2414, + "2020-01-17": 0.262, + "2020-01-18": 0.2585, + "2020-01-19": 0.24, + "2020-01-20": 0.237, + "2020-01-21": 0.2482, + "2020-01-22": 0.247, + "2020-01-23": 0.2356, + "2020-01-24": 0.2316, + "2020-01-25": 0.234, + "2020-01-26": 0.2378, + "2020-01-27": 0.24, + "2020-01-28": 0.2437, + "2020-01-29": 0.2405, + "2020-01-30": 0.2415, + "2020-01-31": 0.2474, + "2020-02-01": 0.2475, + "2020-02-02": 0.2528, + "2020-02-03": 0.263, + "2020-02-04": 0.2804, + "2020-02-05": 0.2753, + "2020-02-06": 0.2884, + "2020-02-07": 0.3124, + "2020-02-08": 0.3074, + "2020-02-09": 0.3411, + "2020-02-10": 0.3585, + "2020-02-11": 0.369, + "2020-02-12": 0.3876, + "2020-02-13": 0.3494, + "2020-02-14": 0.3664, + "2020-02-15": 0.3349, + "2020-02-16": 0.345, + "2020-02-17": 0.3299, + "2020-02-18": 0.3495, + "2020-02-19": 0.3476, + "2020-02-20": 0.4342, + "2020-02-21": 0.418, + "2020-02-22": 0.4495, + "2020-02-23": 0.4594, + "2020-02-24": 0.4946, + "2020-02-25": 0.3938, + "2020-02-26": 0.3629, + "2020-02-27": 0.3764, + "2020-02-28": 0.349, + "2020-02-29": 0.3236, + "2020-03-01": 0.3197, + "2020-03-02": 0.3742, + "2020-03-03": 0.3689, + "2020-03-04": 0.3929, + "2020-03-05": 0.379, + "2020-03-06": 0.3732, + "2020-03-07": 0.3375, + "2020-03-08": 0.2904, + "2020-03-09": 0.2787, + "2020-03-10": 0.282, + "2020-03-11": 0.2706, + "2020-03-12": 0.1343, + "2020-03-13": 0.1527, + "2020-03-14": 0.1473, + "2020-03-15": 0.1471, + "2020-03-16": 0.1266, + "2020-03-17": 0.139, + "2020-03-18": 0.1409, + "2020-03-19": 0.1574, + "2020-03-20": 0.1578, + "2020-03-21": 0.153, + "2020-03-22": 0.1455, + "2020-03-23": 0.1595, + "2020-03-24": 0.1598, + "2020-03-25": 0.1536, + "2020-03-26": 0.159, + "2020-03-27": 0.1538, + "2020-03-28": 0.1499, + "2020-03-29": 0.1441, + "2020-03-30": 0.1547, + "2020-03-31": 0.154, + "2020-04-01": 0.154, + "2020-04-02": 0.158, + "2020-04-03": 0.1586, + "2020-04-04": 0.1772, + "2020-04-05": 0.1718, + "2020-04-06": 0.1876, + "2020-04-07": 0.1856, + "2020-04-08": 0.2039, + "2020-04-09": 0.2, + "2020-04-10": 0.1786, + "2020-04-11": 0.1795, + "2020-04-12": 0.1773, + "2020-04-13": 0.1755, + "2020-04-14": 0.1783, + "2020-04-15": 0.1687, + "2020-04-16": 0.1857, + "2020-04-17": 0.1878, + "2020-04-18": 0.1987, + "2020-04-19": 0.1875, + "2020-04-20": 0.1765, + "2020-04-21": 0.1776, + "2020-04-22": 0.1872, + "2020-04-23": 0.1952, + "2020-04-24": 0.209, + "2020-04-25": 0.2108, + "2020-04-26": 0.2257, + "2020-04-27": 0.2168, + "2020-04-28": 0.2154, + "2020-04-29": 0.2302, + "2020-04-30": 0.2093, + "2020-05-01": 0.2139, + "2020-05-02": 0.2188, + "2020-05-03": 0.2055, + "2020-05-04": 0.2055, + "2020-05-05": 0.2085, + "2020-05-06": 0.1976, + "2020-05-07": 0.204, + "2020-05-08": 0.2181, + "2020-05-09": 0.2136, + "2020-05-10": 0.1919, + "2020-05-11": 0.1865, + "2020-05-12": 0.1933, + "2020-05-13": 0.1954, + "2020-05-14": 0.197, + "2020-05-15": 0.192, + "2020-05-16": 0.198, + "2020-05-17": 0.1979, + "2020-05-18": 0.2014, + "2020-05-19": 0.2008, + "2020-05-20": 0.2091, + "2020-05-21": 0.1915, + "2020-05-22": 0.2044, + "2020-05-23": 0.2014, + "2020-05-24": 0.1972, + "2020-05-25": 0.2118, + "2020-05-26": 0.2186, + "2020-05-27": 0.22, + "2020-05-28": 0.227, + "2020-05-29": 0.2281, + "2020-05-30": 0.2438, + "2020-05-31": 0.2332, + "2020-06-01": 0.2499, + "2020-06-02": 0.2458, + "2020-06-03": 0.2421, + "2020-06-04": 0.2421, + "2020-06-05": 0.2321, + "2020-06-06": 0.2373, + "2020-06-07": 0.232, + "2020-06-08": 0.2339, + "2020-06-09": 0.2423, + "2020-06-10": 0.247, + "2020-06-11": 0.2305, + "2020-06-12": 0.2317, + "2020-06-13": 0.2347, + "2020-06-14": 0.2329, + "2020-06-15": 0.2267, + "2020-06-16": 0.2357, + "2020-06-17": 0.2446, + "2020-06-18": 0.2403, + "2020-06-19": 0.2317, + "2020-06-20": 0.2306, + "2020-06-21": 0.2321, + "2020-06-22": 0.2415, + "2020-06-23": 0.25, + "2020-06-24": 0.2391, + "2020-06-25": 0.2343, + "2020-06-26": 0.2227, + "2020-06-27": 0.2052, + "2020-06-28": 0.208, + "2020-06-29": 0.214, + "2020-06-30": 0.2082, + "2020-07-01": 0.2142, + "2020-07-02": 0.2046, + "2020-07-03": 0.2034, + "2020-07-04": 0.2177, + "2020-07-05": 0.2154, + "2020-07-06": 0.229, + "2020-07-07": 0.2329, + "2020-07-08": 0.2468, + "2020-07-09": 0.2417, + "2020-07-10": 0.2501, + "2020-07-11": 0.2794, + "2020-07-12": 0.279, + "2020-07-13": 0.2653, + "2020-07-14": 0.2722, + "2020-07-15": 0.2881, + "2020-07-16": 0.3546, + "2020-07-17": 0.346, + "2020-07-18": 0.3558, + "2020-07-19": 0.3883, + "2020-07-20": 0.3567, + "2020-07-21": 0.3463, + "2020-07-22": 0.3456, + "2020-07-23": 0.3291, + "2020-07-24": 0.3123, + "2020-07-25": 0.319, + "2020-07-26": 0.3211, + "2020-07-27": 0.2873, + "2020-07-28": 0.3143, + "2020-07-29": 0.308, + "2020-07-30": 0.3347, + "2020-07-31": 0.3271, + "2020-08-01": 0.3279, + "2020-08-02": 0.3016, + "2020-08-03": 0.3096, + "2020-08-04": 0.3266, + "2020-08-05": 0.3355, + "2020-08-06": 0.3638, + "2020-08-07": 0.3689, + "2020-08-08": 0.4524, + "2020-08-09": 0.4245, + "2020-08-10": 0.4686, + "2020-08-11": 0.4366, + "2020-08-12": 0.5233, + "2020-08-13": 0.6699, + "2020-08-14": 0.6388, + "2020-08-15": 0.6342, + "2020-08-16": 0.6291, + "2020-08-17": 0.5872, + "2020-08-18": 0.5567, + "2020-08-19": 0.5965, + "2020-08-20": 0.6264, + "2020-08-21": 0.5323, + "2020-08-22": 0.5636, + "2020-08-23": 0.6132, + "2020-08-24": 0.6293, + "2020-08-25": 0.5609, + "2020-08-26": 0.55, + "2020-08-27": 0.5225, + "2020-08-28": 0.5337, + "2020-08-29": 0.5334, + "2020-08-30": 0.5181, + "2020-08-31": 0.5021, + "2020-09-01": 0.5168, + "2020-09-02": 0.4638, + "2020-09-03": 0.3486, + "2020-09-04": 0.374, + "2020-09-05": 0.3415, + "2020-09-06": 0.3757, + "2020-09-07": 0.3722, + "2020-09-08": 0.349, + "2020-09-09": 0.413, + "2020-09-10": 0.4131, + "2020-09-11": 0.411, + "2020-09-12": 0.409, + "2020-09-13": 0.3774, + "2020-09-14": 0.3819, + "2020-09-15": 0.3539, + "2020-09-16": 0.3598, + "2020-09-17": 0.3565, + "2020-09-18": 0.3369, + "2020-09-19": 0.3423, + "2020-09-20": 0.3273, + "2020-09-21": 0.2873, + "2020-09-22": 0.3062, + "2020-09-23": 0.269, + "2020-09-24": 0.2941, + "2020-09-25": 0.3174, + "2020-09-26": 0.3253, + "2020-09-27": 0.3209, + "2020-09-28": 0.327, + "2020-09-29": 0.3392, + "2020-09-30": 0.3469, + "2020-10-01": 0.335, + "2020-10-02": 0.3158, + "2020-10-03": 0.3154, + "2020-10-04": 0.312, + "2020-10-05": 0.3184, + "2020-10-06": 0.2859, + "2020-10-07": 0.3063, + "2020-10-08": 0.3087, + "2020-10-09": 0.3286, + "2020-10-10": 0.3262, + "2020-10-11": 0.3319, + "2020-10-12": 0.3445, + "2020-10-13": 0.3428, + "2020-10-14": 0.3277, + "2020-10-15": 0.3148, + "2020-10-16": 0.3001, + "2020-10-17": 0.3136, + "2020-10-18": 0.3155, + "2020-10-19": 0.3033, + "2020-10-20": 0.2866, + "2020-10-21": 0.3012, + "2020-10-22": 0.316, + "2020-10-23": 0.3093, + "2020-10-24": 0.3078, + "2020-10-25": 0.3021, + "2020-10-26": 0.2907, + "2020-10-27": 0.2878, + "2020-10-28": 0.2763, + "2020-10-29": 0.2569, + "2020-10-30": 0.2539, + "2020-10-31": 0.2488, + "2020-11-01": 0.2589, + "2020-11-02": 0.247, + "2020-11-03": 0.2337, + "2020-11-04": 0.2383, + "2020-11-05": 0.2481, + "2020-11-06": 0.2767, + "2020-11-07": 0.2579, + "2020-11-08": 0.2667, + "2020-11-09": 0.2605, + "2020-11-10": 0.2889, + "2020-11-11": 0.2785, + "2020-11-12": 0.2665, + "2020-11-13": 0.2802, + "2020-11-14": 0.2661, + "2020-11-15": 0.2569, + "2020-11-16": 0.2711, + "2020-11-17": 0.2744, + "2020-11-18": 0.2673, + "2020-11-19": 0.2653, + "2020-11-20": 0.2846, + "2020-11-21": 0.317, + "2020-11-22": 0.298, + "2020-11-23": 0.3357, + "2020-11-24": 0.3576, + "2020-11-25": 0.3429, + "2020-11-26": 0.3016, + "2020-11-27": 0.3002, + "2020-11-28": 0.3127, + "2020-11-29": 0.3171, + "2020-11-30": 0.3289, + "2020-12-01": 0.3082, + "2020-12-02": 0.3411, + "2020-12-03": 0.3404, + "2020-12-04": 0.3257, + "2020-12-05": 0.3431, + "2020-12-06": 0.3367, + "2020-12-07": 0.3311, + "2020-12-08": 0.3011, + "2020-12-09": 0.3056, + "2020-12-10": 0.2936, + "2020-12-11": 0.2887, + "2020-12-12": 0.3091, + "2020-12-13": 0.3092, + "2020-12-14": 0.3129, + "2020-12-15": 0.325, + "2020-12-16": 0.3396, + "2020-12-17": 0.3324, + "2020-12-18": 0.353, + "2020-12-19": 0.3556, + "2020-12-20": 0.3397, + "2020-12-21": 0.3248, + "2020-12-22": 0.3367, + "2020-12-23": 0.2891, + "2020-12-24": 0.3142, + "2020-12-25": 0.3088, + "2020-12-26": 0.3094, + "2020-12-27": 0.319, + "2020-12-28": 0.3578, + "2020-12-29": 0.3461, + "2020-12-30": 0.3355, + "2020-12-31": 0.3353, + "2021-01-01": 0.3978, + "2021-01-02": 0.4075, + "2021-01-03": 0.4253, + "2021-01-04": 0.4463, + "2021-01-05": 0.4794, + "2021-01-06": 0.5111, + "2021-01-07": 0.4611, + "2021-01-08": 0.455, + "2021-01-09": 0.4847, + "2021-01-10": 0.4578, + "2021-01-11": 0.4171, + "2021-01-12": 0.3991, + "2021-01-13": 0.4501, + "2021-01-14": 0.4559, + "2021-01-15": 0.4864, + "2021-01-16": 0.5183, + "2021-01-17": 0.5859, + "2021-01-18": 0.5668, + "2021-01-19": 0.5213, + "2021-01-20": 0.5472, + "2021-01-21": 0.461, + "2021-01-22": 0.5088, + "2021-01-23": 0.5561, + "2021-01-24": 0.5623, + "2021-01-25": 0.556, + "2021-01-26": 0.601, + "2021-01-27": 0.541, + "2021-01-28": 0.5586, + "2021-01-29": 0.6362, + "2021-01-30": 0.6653, + "2021-01-31": 0.6516, + "2021-02-01": 0.6442, + "2021-02-02": 0.6507, + "2021-02-03": 0.7454, + "2021-02-04": 0.7057, + "2021-02-05": 0.8338, + "2021-02-06": 0.8092, + "2021-02-07": 0.8448, + "2021-02-08": 1.0028, + "2021-02-09": 1.029, + "2021-02-10": 1.0312, + "2021-02-11": 1.238, + "2021-02-12": 1.7178, + "2021-02-13": 1.6716, + "2021-02-14": 1.4706, + "2021-02-15": 1.3558, + "2021-02-16": 1.3127, + "2021-02-17": 1.3662, + "2021-02-18": 1.3965, + "2021-02-19": 1.371, + "2021-02-20": 1.3101, + "2021-02-21": 1.3055, + "2021-02-22": 1.143, + "2021-02-23": 1.0051, + "2021-02-24": 1.06, + "2021-02-25": 0.9812, + "2021-02-26": 0.9725, + "2021-02-27": 1.0988, + "2021-02-28": 1.0218, + "2021-03-01": 1.0893, + "2021-03-02": 1.0825, + "2021-03-03": 1.1787, + "2021-03-04": 1.0895, + "2021-03-05": 1.0526, + "2021-03-06": 1.0494, + "2021-03-07": 1.1069, + "2021-03-08": 1.157, + "2021-03-09": 1.1808, + "2021-03-10": 1.1078, + "2021-03-11": 1.0838, + "2021-03-12": 1.0501, + "2021-03-13": 1.1392, + "2021-03-14": 1.1168, + "2021-03-15": 1.1943, + "2021-03-16": 1.2007, + "2021-03-17": 1.2886, + "2021-03-18": 1.257, + "2021-03-19": 1.2493, + "2021-03-20": 1.2097, + "2021-03-21": 1.1794, + "2021-03-22": 1.1014, + "2021-03-23": 1.1102, + "2021-03-24": 1.0274, + "2021-03-25": 1.0424, + "2021-03-26": 1.1174, + "2021-03-27": 1.1597, + "2021-03-28": 1.2354, + "2021-03-29": 1.4019, + "2021-03-30": 1.3245, + "2021-03-31": 1.3667, + "2021-04-01": 1.3215, + "2021-04-02": 1.3589, + "2021-04-03": 1.2622, + "2021-04-04": 1.3094, + "2021-04-05": 1.4013, + "2021-04-06": 1.3984, + "2021-04-07": 1.3024, + "2021-04-08": 1.3508, + "2021-04-09": 1.3936, + "2021-04-10": 1.3901, + "2021-04-11": 1.5212, + "2021-04-12": 1.482, + "2021-04-13": 1.5156, + "2021-04-14": 1.4631, + "2021-04-15": 1.5686, + "2021-04-16": 1.5044, + "2021-04-17": 1.6998, + "2021-04-18": 1.4573, + "2021-04-19": 1.2923, + "2021-04-20": 1.2907, + "2021-04-21": 1.2104, + "2021-04-22": 1.1618, + "2021-04-23": 1.1667, + "2021-04-24": 1.0884, + "2021-04-25": 1.0939, + "2021-04-26": 1.2364, + "2021-04-27": 1.3097, + "2021-04-28": 1.288, + "2021-04-29": 1.3222, + "2021-04-30": 1.4029, + "2021-05-01": 1.3934, + "2021-05-02": 1.3877, + "2021-05-03": 1.4346, + "2021-05-04": 1.2742, + "2021-05-05": 1.4425, + "2021-05-06": 1.5444, + "2021-05-07": 1.5769, + "2021-05-08": 1.5182, + "2021-05-09": 1.5151, + "2021-05-10": 1.3707, + "2021-05-11": 1.474, + "2021-05-12": 1.2734, + "2021-05-13": 1.3526, + "2021-05-14": 1.4775, + "2021-05-15": 1.3888, + "2021-05-16": 1.3956, + "2021-05-17": 1.2842, + "2021-05-18": 1.365, + "2021-05-19": 0.9309, + "2021-05-20": 1.1089, + "2021-05-21": 0.9736, + "2021-05-22": 0.9739, + "2021-05-23": 0.8178, + "2021-05-24": 0.9811, + "2021-05-25": 0.9754, + "2021-05-26": 1.0237, + "2021-05-27": 0.9752, + "2021-05-28": 0.8999, + "2021-05-29": 0.8315, + "2021-05-30": 0.8491, + "2021-05-31": 0.9359, + "2021-06-01": 0.8952, + "2021-06-02": 1.0187, + "2021-06-03": 1.1113, + "2021-06-04": 1.0284, + "2021-06-05": 0.9763, + "2021-06-06": 1.0247, + "2021-06-07": 0.9633, + "2021-06-08": 0.9406, + "2021-06-09": 1.0415, + "2021-06-10": 1.0496, + "2021-06-11": 0.9732, + "2021-06-12": 0.991, + "2021-06-13": 1.0406, + "2021-06-14": 1.0552, + "2021-06-15": 1.0323, + "2021-06-16": 1.0178, + "2021-06-17": 1.0646, + "2021-06-18": 0.992, + "2021-06-19": 0.934, + "2021-06-20": 0.9339, + "2021-06-21": 0.7539, + "2021-06-22": 0.7436, + "2021-06-23": 0.8323, + "2021-06-24": 0.8743, + "2021-06-25": 0.7904, + "2021-06-26": 0.8094, + "2021-06-27": 0.8358, + "2021-06-28": 0.8426, + "2021-06-29": 0.8889, + "2021-06-30": 0.8833, + "2021-07-01": 0.834, + "2021-07-02": 0.8456, + "2021-07-03": 0.8764, + "2021-07-04": 0.88, + "2021-07-05": 0.8728, + "2021-07-06": 0.9132, + "2021-07-07": 0.9062, + "2021-07-08": 0.8683, + "2021-07-09": 0.9058, + "2021-07-10": 0.8918, + "2021-07-11": 0.8952, + "2021-07-12": 0.8675, + "2021-07-13": 0.8753, + "2021-07-14": 0.8587, + "2021-07-15": 0.8347, + "2021-07-16": 0.7842, + "2021-07-17": 0.7843, + "2021-07-18": 0.7981, + "2021-07-19": 0.7505, + "2021-07-20": 0.6961, + "2021-07-21": 0.7701, + "2021-07-22": 0.7961, + "2021-07-23": 0.8078, + "2021-07-24": 0.8262, + "2021-07-25": 0.8905, + "2021-07-26": 0.8476, + "2021-07-27": 0.8569, + "2021-07-28": 0.8481, + "2021-07-29": 0.8462, + "2021-07-30": 0.8529, + "2021-07-31": 0.8395, + "2021-08-01": 0.8117, + "2021-08-02": 0.8167, + "2021-08-03": 0.8187, + "2021-08-04": 0.8282, + "2021-08-05": 0.8507, + "2021-08-06": 0.8551, + "2021-08-07": 0.8755, + "2021-08-08": 0.8253, + "2021-08-09": 0.855, + "2021-08-10": 0.8829, + "2021-08-11": 0.9116, + "2021-08-12": 0.8841, + "2021-08-13": 0.9544, + "2021-08-14": 0.932, + "2021-08-15": 0.9951, + "2021-08-16": 0.9461, + "2021-08-17": 0.8835, + "2021-08-18": 0.9224, + "2021-08-19": 1.0441, + "2021-08-20": 1.1385, + "2021-08-21": 1.1488, + "2021-08-22": 1.157, + "2021-08-23": 1.1587, + "2021-08-24": 1.0368, + "2021-08-25": 1.0651, + "2021-08-26": 0.9887, + "2021-08-27": 1.065, + "2021-08-28": 1.0388, + "2021-08-29": 1.0217, + "2021-08-30": 1.0207, + "2021-08-31": 1.1134, + "2021-09-01": 1.1614, + "2021-09-02": 1.1421, + "2021-09-03": 1.2178, + "2021-09-04": 1.2079, + "2021-09-05": 1.3147, + "2021-09-06": 1.4579, + "2021-09-07": 1.2774, + "2021-09-08": 1.9163, + "2021-09-09": 2.3036, + "2021-09-10": 2.0126, + "2021-09-11": 1.9943, + "2021-09-12": 2.3793, + "2021-09-13": 2.1652, + "2021-09-14": 2.0396, + "2021-09-15": 2.015, + "2021-09-16": 2.0378, + "2021-09-17": 1.9236, + "2021-09-18": 2.0722, + "2021-09-19": 1.9729, + "2021-09-20": 1.6736, + "2021-09-21": 1.5763, + "2021-09-22": 1.8457, + "2021-09-23": 2.0031, + "2021-09-24": 1.7864, + "2021-09-25": 1.731, + "2021-09-26": 1.775, + "2021-09-27": 1.6733, + "2021-09-28": 1.5649, + "2021-09-29": 1.6067, + "2021-09-30": 1.6273, + "2021-10-01": 1.7736, + "2021-10-02": 1.7692, + "2021-10-03": 1.9833, + "2021-10-04": 1.9394, + "2021-10-05": 1.951, + "2021-10-06": 1.8141, + "2021-10-07": 1.8902, + "2021-10-08": 1.898, + "2021-10-09": 1.8927, + "2021-10-10": 1.765, + "2021-10-11": 1.7158, + "2021-10-12": 1.7474, + "2021-10-13": 1.837, + "2021-10-14": 1.8106, + "2021-10-15": 1.7543, + "2021-10-16": 1.7983, + "2021-10-17": 1.7692, + "2021-10-18": 1.7369, + "2021-10-19": 1.7105, + "2021-10-20": 1.791, + "2021-10-21": 1.7845, + "2021-10-22": 1.8609, + "2021-10-23": 1.9076, + "2021-10-24": 1.8151, + "2021-10-25": 2.0496, + "2021-10-26": 2.0445, + "2021-10-27": 1.7948, + "2021-10-28": 1.8447, + "2021-10-29": 1.8756, + "2021-10-30": 1.8199, + "2021-10-31": 1.8284, + "2021-11-01": 1.8322, + "2021-11-02": 1.8647, + "2021-11-03": 1.9342, + "2021-11-04": 1.8587, + "2021-11-05": 1.8421, + "2021-11-06": 1.8362, + "2021-11-07": 1.8664, + "2021-11-08": 2.0468, + "2021-11-09": 1.9461, + "2021-11-10": 1.9321, + "2021-11-11": 2.02, + "2021-11-12": 2.1452, + "2021-11-13": 2.0743, + "2021-11-14": 1.9845, + "2021-11-15": 1.8911, + "2021-11-16": 1.7229, + "2021-11-17": 1.6588, + "2021-11-18": 1.8499, + "2021-11-19": 1.878, + "2021-11-20": 1.8384, + "2021-11-21": 1.8281, + "2021-11-22": 1.7384, + "2021-11-23": 1.8357, + "2021-11-24": 1.7416, + "2021-11-25": 1.8214, + "2021-11-26": 1.6045, + "2021-11-27": 1.6742, + "2021-11-28": 1.7125, + "2021-11-29": 1.7725, + "2021-11-30": 1.8119, + "2021-12-01": 1.9609, + "2021-12-02": 1.8869, + "2021-12-03": 1.7468, + "2021-12-04": 1.6491, + "2021-12-05": 1.765, + "2021-12-06": 1.7629, + "2021-12-07": 1.6852, + "2021-12-08": 1.7015, + "2021-12-09": 1.548, + "2021-12-10": 1.459, + "2021-12-11": 1.5279, + "2021-12-12": 1.5392, + "2021-12-13": 1.3223, + "2021-12-14": 1.3803, + "2021-12-15": 1.4262, + "2021-12-16": 1.3878, + "2021-12-17": 1.3468, + "2021-12-18": 1.3781, + "2021-12-19": 1.3556, + "2021-12-20": 1.2964, + "2021-12-21": 1.3573, + "2021-12-22": 1.3642, + "2021-12-23": 1.4698, + "2021-12-24": 1.5754, + "2021-12-25": 1.5751, + "2021-12-26": 1.6064, + "2021-12-27": 1.6625, + "2021-12-28": 1.4654, + "2021-12-29": 1.5827, + "2021-12-30": 1.7302, + "2021-12-31": 1.6591, + "2022-01-01": 1.7414, + "2022-01-02": 1.6734, + "2022-01-03": 1.7949, + "2022-01-04": 1.6725, + "2022-01-05": 1.5554, + "2022-01-06": 1.5633, + "2022-01-07": 1.4458, + "2022-01-08": 1.385, + "2022-01-09": 1.4004, + "2022-01-10": 1.3623, + "2022-01-11": 1.446, + "2022-01-12": 1.4991, + "2022-01-13": 1.3615, + "2022-01-14": 1.3817, + "2022-01-15": 1.3785, + "2022-01-16": 1.429, + "2022-01-17": 1.3413, + "2022-01-18": 1.3169, + "2022-01-19": 1.2655, + "2022-01-20": 1.1616, + "2022-01-21": 1.0307, + "2022-01-22": 0.9514, + "2022-01-23": 0.989, + "2022-01-24": 0.9125, + "2022-01-25": 0.9224, + "2022-01-26": 0.9231, + "2022-01-27": 0.9361, + "2022-01-28": 0.9584, + "2022-01-29": 0.969, + "2022-01-30": 0.9447, + "2022-01-31": 0.9499, + "2022-02-01": 0.9663, + "2022-02-02": 0.9227, + "2022-02-03": 0.9357, + "2022-02-04": 0.9973, + "2022-02-05": 1.0015, + "2022-02-06": 1.0247, + "2022-02-07": 1.07, + "2022-02-08": 1.0235, + "2022-02-09": 1.0363, + "2022-02-10": 0.9813, + "2022-02-11": 0.9161, + "2022-02-12": 0.9187, + "2022-02-13": 0.8965, + "2022-02-14": 0.8992, + "2022-02-15": 0.969, + "2022-02-16": 0.9779, + "2022-02-17": 0.8972, + "2022-02-18": 0.8941, + "2022-02-19": 0.8994, + "2022-02-20": 0.8451, + "2022-02-21": 0.7847, + "2022-02-22": 0.8107, + "2022-02-23": 0.785, + "2022-02-24": 0.7772, + "2022-02-25": 0.8301, + "2022-02-26": 0.8592, + "2022-02-27": 0.7978, + "2022-02-28": 0.8681, + "2022-03-01": 0.8565, + "2022-03-02": 0.8199, + "2022-03-03": 0.7966, + "2022-03-04": 0.7599, + "2022-03-05": 0.7735, + "2022-03-06": 0.7465, + "2022-03-07": 0.7279, + "2022-03-08": 0.7399, + "2022-03-09": 0.7671, + "2022-03-10": 0.7309, + "2022-03-11": 0.7014, + "2022-03-12": 0.7052, + "2022-03-13": 0.6807, + "2022-03-14": 0.7063, + "2022-03-15": 0.7159, + "2022-03-16": 0.7365, + "2022-03-17": 0.7418, + "2022-03-18": 0.7548, + "2022-03-19": 0.7649, + "2022-03-20": 0.7414, + "2022-03-21": 0.8276, + "2022-03-22": 0.8267, + "2022-03-23": 0.8388, + "2022-03-24": 0.8911, + "2022-03-25": 0.8887, + "2022-03-26": 0.9116, + "2022-03-27": 0.9493, + "2022-03-28": 0.9068, + "2022-03-29": 0.923, + "2022-03-30": 0.9266, + "2022-03-31": 0.9293, + "2022-04-01": 0.9391, + "2022-04-02": 0.9068, + "2022-04-03": 0.9353, + "2022-04-04": 0.8827, + "2022-04-05": 0.8345, + "2022-04-06": 0.7661, + "2022-04-07": 0.7993, + "2022-04-08": 0.7595, + "2022-04-09": 0.788, + "2022-04-10": 0.7503, + "2022-04-11": 0.6936, + "2022-04-12": 0.7258, + "2022-04-13": 0.7461, + "2022-04-14": 0.7267, + "2022-04-15": 0.7393, + "2022-04-16": 0.7432, + "2022-04-17": 0.7105, + "2022-04-18": 0.7365, + "2022-04-19": 0.7589, + "2022-04-20": 0.7415, + "2022-04-21": 0.7184, + "2022-04-22": 0.7191, + "2022-04-23": 0.7074, + "2022-04-24": 0.7011, + "2022-04-25": 0.703, + "2022-04-26": 0.656, + "2022-04-27": 0.6712, + "2022-04-28": 0.6982, + "2022-04-29": 0.6381, + "2022-04-30": 0.5673, + "2022-05-01": 0.6016, + "2022-05-02": 0.6161, + "2022-05-03": 0.671, + "2022-05-04": 0.7157, + "2022-05-05": 0.634, + "2022-05-06": 0.6973, + "2022-05-07": 0.7487, + "2022-05-08": 0.7292, + "2022-05-09": 0.5746, + "2022-05-10": 0.5852, + "2022-05-11": 0.4504, + "2022-05-12": 0.4232, + "2022-05-13": 0.4435, + "2022-05-14": 0.4635, + "2022-05-15": 0.4906, + "2022-05-16": 0.4603, + "2022-05-17": 0.495, + "2022-05-18": 0.4334, + "2022-05-19": 0.4516, + "2022-05-20": 0.4335, + "2022-05-21": 0.435, + "2022-05-22": 0.4427, + "2022-05-23": 0.4171, + "2022-05-24": 0.4191, + "2022-05-25": 0.4057, + "2022-05-26": 0.3794, + "2022-05-27": 0.3606, + "2022-05-28": 0.3674, + "2022-05-29": 0.3788, + "2022-05-30": 0.4201, + "2022-05-31": 0.4113, + "2022-06-01": 0.385, + "2022-06-02": 0.3947, + "2022-06-03": 0.3811, + "2022-06-04": 0.3883, + "2022-06-05": 0.3918, + "2022-06-06": 0.4128, + "2022-06-07": 0.3989, + "2022-06-08": 0.4165, + "2022-06-09": 0.4042, + "2022-06-10": 0.3689, + "2022-06-11": 0.3506, + "2022-06-12": 0.3248, + "2022-06-13": 0.3039, + "2022-06-14": 0.3086, + "2022-06-15": 0.3402, + "2022-06-16": 0.302, + "2022-06-17": 0.3102, + "2022-06-18": 0.2959, + "2022-06-19": 0.3146, + "2022-06-20": 0.3186, + "2022-06-21": 0.3257, + "2022-06-22": 0.3138, + "2022-06-23": 0.3361, + "2022-06-24": 0.3497, + "2022-06-25": 0.3515, + "2022-06-26": 0.3321, + "2022-06-27": 0.3311, + "2022-06-28": 0.3126, + "2022-06-29": 0.3017, + "2022-06-30": 0.3147, + "2022-07-01": 0.3066, + "2022-07-02": 0.3076, + "2022-07-03": 0.3096, + "2022-07-04": 0.3196, + "2022-07-05": 0.3078, + "2022-07-06": 0.3108, + "2022-07-07": 0.3224, + "2022-07-08": 0.3141, + "2022-07-09": 0.3214, + "2022-07-10": 0.3101, + "2022-07-11": 0.297, + "2022-07-12": 0.2926, + "2022-07-13": 0.31, + "2022-07-14": 0.3296, + "2022-07-15": 0.3275, + "2022-07-16": 0.3405, + "2022-07-17": 0.3365, + "2022-07-18": 0.3595, + "2022-07-19": 0.372, + "2022-07-20": 0.3446, + "2022-07-21": 0.3492, + "2022-07-22": 0.3375, + "2022-07-23": 0.3348, + "2022-07-24": 0.3402, + "2022-07-25": 0.3119, + "2022-07-26": 0.3113, + "2022-07-27": 0.3288, + "2022-07-28": 0.3365, + "2022-07-29": 0.3369, + "2022-07-30": 0.3377, + "2022-07-31": 0.3361, + "2022-08-01": 0.3425, + "2022-08-02": 0.3266, + "2022-08-03": 0.3266, + "2022-08-04": 0.3363, + "2022-08-05": 0.3586, + "2022-08-06": 0.3491, + "2022-08-07": 0.3559, + "2022-08-08": 0.3629, + "2022-08-09": 0.3453, + "2022-08-10": 0.3642, + "2022-08-11": 0.3611, + "2022-08-12": 0.3652, + "2022-08-13": 0.3683, + "2022-08-14": 0.3639, + "2022-08-15": 0.3576, + "2022-08-16": 0.3527, + "2022-08-17": 0.3405, + "2022-08-18": 0.3336, + "2022-08-19": 0.3029, + "2022-08-20": 0.3002, + "2022-08-21": 0.3063, + "2022-08-22": 0.303, + "2022-08-23": 0.3075, + "2022-08-24": 0.3022, + "2022-08-25": 0.3071, + "2022-08-26": 0.2828, + "2022-08-27": 0.2888, + "2022-08-28": 0.2824, + "2022-08-29": 0.298, + "2022-08-30": 0.2916, + "2022-08-31": 0.2885, + "2022-09-01": 0.2936, + "2022-09-02": 0.3023, + "2022-09-03": 0.3032, + "2022-09-04": 0.3126, + "2022-09-05": 0.3045, + "2022-09-06": 0.2852, + "2022-09-07": 0.2989, + "2022-09-08": 0.3077, + "2022-09-09": 0.3244, + "2022-09-10": 0.3219, + "2022-09-11": 0.3266, + "2022-09-12": 0.3236, + "2022-09-13": 0.2965, + "2022-09-14": 0.303, + "2022-09-15": 0.2949, + "2022-09-16": 0.2979, + "2022-09-17": 0.311, + "2022-09-18": 0.293, + "2022-09-19": 0.3171, + "2022-09-20": 0.3297, + "2022-09-21": 0.3352, + "2022-09-22": 0.3699, + "2022-09-23": 0.3991, + "2022-09-24": 0.3853, + "2022-09-25": 0.3882, + "2022-09-26": 0.3686, + "2022-09-27": 0.3521, + "2022-09-28": 0.3494, + "2022-09-29": 0.3549, + "2022-09-30": 0.3535, + "2022-10-01": 0.3548, + "2022-10-02": 0.3543, + "2022-10-03": 0.3544, + "2022-10-04": 0.3577, + "2022-10-05": 0.3538, + "2022-10-06": 0.3491, + "2022-10-07": 0.3416, + "2022-10-08": 0.3348, + "2022-10-09": 0.3389, + "2022-10-10": 0.3194, + "2022-10-11": 0.3178, + "2022-10-12": 0.3135, + "2022-10-13": 0.3196, + "2022-10-14": 0.3174, + "2022-10-15": 0.3189, + "2022-10-16": 0.3225, + "2022-10-17": 0.3302, + "2022-10-18": 0.3194, + "2022-10-19": 0.3076, + "2022-10-20": 0.3066, + "2022-10-21": 0.3131, + "2022-10-22": 0.3137, + "2022-10-23": 0.3158, + "2022-10-24": 0.3147, + "2022-10-25": 0.3312, + "2022-10-26": 0.3391, + "2022-10-27": 0.3258, + "2022-10-28": 0.333, + "2022-10-29": 0.3396, + "2022-10-30": 0.3592, + "2022-10-31": 0.3586, + "2022-11-01": 0.3601, + "2022-11-02": 0.3616, + "2022-11-03": 0.3697, + "2022-11-04": 0.4152, + "2022-11-05": 0.4239, + "2022-11-06": 0.4025, + "2022-11-07": 0.4193, + "2022-11-08": 0.3334, + "2022-11-09": 0.2656, + "2022-11-10": 0.3209, + "2022-11-11": 0.3029, + "2022-11-12": 0.2739, + "2022-11-13": 0.262, + "2022-11-14": 0.2616, + "2022-11-15": 0.2814, + "2022-11-16": 0.2758, + "2022-11-17": 0.2677, + "2022-11-18": 0.2982, + "2022-11-19": 0.2982, + "2022-11-20": 0.2563, + "2022-11-21": 0.2447, + "2022-11-22": 0.2414, + "2022-11-23": 0.2473, + "2022-11-24": 0.2424, + "2022-11-25": 0.2437, + "2022-11-26": 0.2409, + "2022-11-27": 0.2403, + "2022-11-28": 0.2359, + "2022-11-29": 0.2408, + "2022-11-30": 0.2477, + "2022-12-01": 0.2409, + "2022-12-02": 0.2441, + "2022-12-03": 0.2375, + "2022-12-04": 0.2393, + "2022-12-05": 0.2364, + "2022-12-06": 0.2339, + "2022-12-07": 0.221, + "2022-12-08": 0.2244, + "2022-12-09": 0.2258, + "2022-12-10": 0.2268, + "2022-12-11": 0.2225, + "2022-12-12": 0.2184, + "2022-12-13": 0.2239, + "2022-12-14": 0.2207, + "2022-12-15": 0.2102, + "2022-12-16": 0.1849, + "2022-12-17": 0.1945, + "2022-12-18": 0.1881, + "2022-12-19": 0.1742, + "2022-12-20": 0.1787, + "2022-12-21": 0.1706, + "2022-12-22": 0.1719, + "2022-12-23": 0.1698, + "2022-12-24": 0.1677, + "2022-12-25": 0.1655, + "2022-12-26": 0.1715, + "2022-12-27": 0.1701, + "2022-12-28": 0.167, + "2022-12-29": 0.1648, + "2022-12-30": 0.1691, + "2022-12-31": 0.1725, + "2023-01-01": 0.1773, + "2023-01-02": 0.1809, + "2023-01-03": 0.1843, + "2023-01-04": 0.1869, + "2023-01-05": 0.1835, + "2023-01-06": 0.189, + "2023-01-07": 0.1939, + "2023-01-08": 0.1993, + "2023-01-09": 0.1997, + "2023-01-10": 0.2037, + "2023-01-11": 0.2102, + "2023-01-12": 0.2124, + "2023-01-13": 0.2207, + "2023-01-14": 0.2292, + "2023-01-15": 0.2405, + "2023-01-16": 0.2341, + "2023-01-17": 0.2283, + "2023-01-18": 0.2087, + "2023-01-19": 0.2171, + "2023-01-20": 0.2392, + "2023-01-21": 0.2388, + "2023-01-22": 0.2458, + "2023-01-23": 0.2496, + "2023-01-24": 0.2364, + "2023-01-25": 0.2443, + "2023-01-26": 0.2452, + "2023-01-27": 0.2545, + "2023-01-28": 0.2572, + "2023-01-29": 0.2616, + "2023-01-30": 0.2393, + "2023-01-31": 0.2414, + "2023-02-01": 0.2509, + "2023-02-02": 0.2551, + "2023-02-03": 0.271, + "2023-02-04": 0.2671, + "2023-02-05": 0.257, + "2023-02-06": 0.2546, + "2023-02-07": 0.2801, + "2023-02-08": 0.2899, + "2023-02-09": 0.2548, + "2023-02-10": 0.251, + "2023-02-11": 0.2566, + "2023-02-12": 0.2474, + "2023-02-13": 0.2432, + "2023-02-14": 0.2533, + "2023-02-15": 0.2722, + "2023-02-16": 0.2575, + "2023-02-17": 0.2712, + "2023-02-18": 0.2729, + "2023-02-19": 0.2777, + "2023-02-20": 0.285, + "2023-02-21": 0.2788, + "2023-02-22": 0.2714, + "2023-02-23": 0.2685, + "2023-02-24": 0.2566, + "2023-02-25": 0.2505, + "2023-02-26": 0.2553, + "2023-02-27": 0.2548, + "2023-02-28": 0.2444, + "2023-03-01": 0.2507, + "2023-03-02": 0.242, + "2023-03-03": 0.2331, + "2023-03-04": 0.2245, + "2023-03-05": 0.2257, + "2023-03-06": 0.2251, + "2023-03-07": 0.2172, + "2023-03-08": 0.2017, + "2023-03-09": 0.1934, + "2023-03-10": 0.1883, + "2023-03-11": 0.1872, + "2023-03-12": 0.2011, + "2023-03-13": 0.2122, + "2023-03-14": 0.2222, + "2023-03-15": 0.2039, + "2023-03-16": 0.2084, + "2023-03-17": 0.2264, + "2023-03-18": 0.2145, + "2023-03-19": 0.2197, + "2023-03-20": 0.2094, + "2023-03-21": 0.223, + "2023-03-22": 0.2134, + "2023-03-23": 0.2194, + "2023-03-24": 0.2088, + "2023-03-25": 0.2063, + "2023-03-26": 0.2105, + "2023-03-27": 0.2, + "2023-03-28": 0.2018, + "2023-03-29": 0.2353, + "2023-03-30": 0.2247, + "2023-03-31": 0.226, + "2023-04-01": 0.2231, + "2023-04-02": 0.2139, + "2023-04-03": 0.2143, + "2023-04-04": 0.2178, + "2023-04-05": 0.2229, + "2023-04-06": 0.218, + "2023-04-07": 0.2189, + "2023-04-08": 0.216, + "2023-04-09": 0.2164, + "2023-04-10": 0.221, + "2023-04-11": 0.22, + "2023-04-12": 0.215, + "2023-04-13": 0.2212, + "2023-04-14": 0.2273, + "2023-04-15": 0.2314, + "2023-04-16": 0.2345, + "2023-04-17": 0.221, + "2023-04-18": 0.2223, + "2023-04-19": 0.1991, + "2023-04-20": 0.1905, + "2023-04-21": 0.1853, + "2023-04-22": 0.1898, + "2023-04-23": 0.1881, + "2023-04-24": 0.1853, + "2023-04-25": 0.1877, + "2023-04-26": 0.181, + "2023-04-27": 0.184, + "2023-04-28": 0.1836, + "2023-04-29": 0.1836, + "2023-04-30": 0.1801, + "2023-05-01": 0.1774, + "2023-05-02": 0.1788, + "2023-05-03": 0.181, + "2023-05-04": 0.177, + "2023-05-05": 0.1812, + "2023-05-06": 0.1752, + "2023-05-07": 0.1732, + "2023-05-08": 0.1641, + "2023-05-09": 0.1649, + "2023-05-10": 0.1688, + "2023-05-11": 0.1623, + "2023-05-12": 0.1647, + "2023-05-13": 0.1622, + "2023-05-14": 0.1629, + "2023-05-15": 0.1669, + "2023-05-16": 0.1693, + "2023-05-17": 0.1722, + "2023-05-18": 0.1651, + "2023-05-19": 0.1648, + "2023-05-20": 0.1645, + "2023-05-21": 0.1616, + "2023-05-22": 0.1612, + "2023-05-23": 0.1623, + "2023-05-24": 0.1543, + "2023-05-25": 0.1502, + "2023-05-26": 0.1499, + "2023-05-27": 0.1505, + "2023-05-28": 0.1531, + "2023-05-29": 0.1515, + "2023-05-30": 0.1514, + "2023-05-31": 0.1477, + "2023-06-01": 0.1482, + "2023-06-02": 0.1516, + "2023-06-03": 0.1508, + "2023-06-04": 0.1517, + "2023-06-05": 0.1377, + "2023-06-06": 0.1373, + "2023-06-07": 0.1249, + "2023-06-08": 0.1233, + "2023-06-09": 0.1238, + "2023-06-10": 0.1109, + "2023-06-11": 0.1107, + "2023-06-12": 0.1127, + "2023-06-13": 0.1129, + "2023-06-14": 0.1129, + "2023-06-15": 0.1122, + "2023-06-16": 0.116, + "2023-06-17": 0.1184, + "2023-06-18": 0.115, + "2023-06-19": 0.1165, + "2023-06-20": 0.1214, + "2023-06-21": 0.1258, + "2023-06-22": 0.1248, + "2023-06-23": 0.1351, + "2023-06-24": 0.1328, + "2023-06-25": 0.137, + "2023-06-26": 0.1294, + "2023-06-27": 0.1272, + "2023-06-28": 0.1186, + "2023-06-29": 0.1201, + "2023-06-30": 0.1217, + "2023-07-01": 0.128, + "2023-07-02": 0.1236, + "2023-07-03": 0.1283, + "2023-07-04": 0.1241, + "2023-07-05": 0.1198, + "2023-07-06": 0.113, + "2023-07-07": 0.1123, + "2023-07-08": 0.1116, + "2023-07-09": 0.1105, + "2023-07-10": 0.11, + "2023-07-11": 0.1087, + "2023-07-12": 0.1058, + "2023-07-13": 0.1219, + "2023-07-14": 0.1133, + "2023-07-15": 0.1137, + "2023-07-16": 0.1157, + "2023-07-17": 0.1203, + "2023-07-18": 0.114, + "2023-07-19": 0.1138, + "2023-07-20": 0.1151, + "2023-07-21": 0.1154, + "2023-07-22": 0.1145, + "2023-07-23": 0.1158, + "2023-07-24": 0.1098, + "2023-07-25": 0.11, + "2023-07-26": 0.1108, + "2023-07-27": 0.11, + "2023-07-28": 0.1105, + "2023-07-29": 0.1114, + "2023-07-30": 0.1095, + "2023-07-31": 0.1098, + "2023-08-01": 0.1109, + "2023-08-02": 0.1069, + "2023-08-03": 0.1063, + "2023-08-04": 0.106, + "2023-08-05": 0.1065, + "2023-08-06": 0.1098, + "2023-08-07": 0.112, + "2023-08-08": 0.1132, + "2023-08-09": 0.113, + "2023-08-10": 0.1125, + "2023-08-11": 0.1123, + "2023-08-12": 0.1157, + "2023-08-13": 0.1169, + "2023-08-14": 0.115, + "2023-08-15": 0.1083, + "2023-08-16": 0.1028, + "2023-08-17": 0.093, + "2023-08-18": 0.0954, + "2023-08-19": 0.0967, + "2023-08-20": 0.0972, + "2023-08-21": 0.0963, + "2023-08-22": 0.0973, + "2023-08-23": 0.0987, + "2023-08-24": 0.0975, + "2023-08-25": 0.0968, + "2023-08-26": 0.0954, + "2023-08-27": 0.0953, + "2023-08-28": 0.0973, + "2023-08-29": 0.1006, + "2023-08-30": 0.0969, + "2023-08-31": 0.0931, + "2023-09-01": 0.0921, + "2023-09-02": 0.0944, + "2023-09-03": 0.0945, + "2023-09-04": 0.094, + "2023-09-05": 0.0942, + "2023-09-06": 0.0942, + "2023-09-07": 0.0979, + "2023-09-08": 0.0963, + "2023-09-09": 0.0941, + "2023-09-10": 0.0921, + "2023-09-11": 0.0887, + "2023-09-12": 0.0895, + "2023-09-13": 0.0909, + "2023-09-14": 0.0934, + "2023-09-15": 0.0964, + "2023-09-16": 0.0976, + "2023-09-17": 0.095, + "2023-09-18": 0.0964, + "2023-09-19": 0.0972, + "2023-09-20": 0.1003, + "2023-09-21": 0.0973, + "2023-09-22": 0.102, + "2023-09-23": 0.1006, + "2023-09-24": 0.1012, + "2023-09-25": 0.0977, + "2023-09-26": 0.0955, + "2023-09-27": 0.0957, + "2023-09-28": 0.0975, + "2023-09-29": 0.1015, + "2023-09-30": 0.1033, + "2023-10-01": 0.1063, + "2023-10-02": 0.1013, + "2023-10-03": 0.1012, + "2023-10-04": 0.0993, + "2023-10-05": 0.1002, + "2023-10-06": 0.1007, + "2023-10-07": 0.1, + "2023-10-08": 0.1013, + "2023-10-09": 0.0965, + "2023-10-10": 0.0951, + "2023-10-11": 0.0951, + "2023-10-12": 0.0936, + "2023-10-13": 0.0951, + "2023-10-14": 0.0953, + "2023-10-15": 0.0943, + "2023-10-16": 0.0948, + "2023-10-17": 0.0914, + "2023-10-18": 0.0899, + "2023-10-19": 0.0897, + "2023-10-20": 0.0913, + "2023-10-21": 0.0942, + "2023-10-22": 0.0943, + "2023-10-23": 0.0983, + "2023-10-24": 0.1008, + "2023-10-25": 0.1009, + "2023-10-26": 0.1012, + "2023-10-27": 0.0992, + "2023-10-28": 0.1017, + "2023-10-29": 0.1065, + "2023-10-30": 0.1121, + "2023-10-31": 0.1093, + "2023-11-01": 0.1144, + "2023-11-02": 0.1113, + "2023-11-03": 0.1115, + "2023-11-04": 0.1149, + "2023-11-05": 0.1212, + "2023-11-06": 0.1291, + "2023-11-07": 0.1235, + "2023-11-08": 0.127, + "2023-11-09": 0.1238, + "2023-11-10": 0.128, + "2023-11-11": 0.1335, + "2023-11-12": 0.1461, + "2023-11-13": 0.1333, + "2023-11-14": 0.1291, + "2023-11-15": 0.1422, + "2023-11-16": 0.1357, + "2023-11-17": 0.136, + "2023-11-18": 0.1345, + "2023-11-19": 0.1406, + "2023-11-20": 0.1414, + "2023-11-21": 0.1234, + "2023-11-22": 0.1323, + "2023-11-23": 0.1306, + "2023-11-24": 0.1335, + "2023-11-25": 0.1404, + "2023-11-26": 0.1361, + "2023-11-27": 0.1317, + "2023-11-28": 0.133, + "2023-11-29": 0.1341, + "2023-11-30": 0.1338, + "2023-12-01": 0.1394, + "2023-12-02": 0.1479, + "2023-12-03": 0.1513, + "2023-12-04": 0.1534, + "2023-12-05": 0.1537, + "2023-12-06": 0.1528, + "2023-12-07": 0.1638, + "2023-12-08": 0.1843, + "2023-12-09": 0.1995, + "2023-12-10": 0.2015, + "2023-12-11": 0.2026, + "2023-12-12": 0.1981, + "2023-12-13": 0.2009, + "2023-12-14": 0.2163, + "2023-12-15": 0.1961, + "2023-12-16": 0.1982, + "2023-12-17": 0.1904, + "2023-12-18": 0.1955, + "2023-12-19": 0.1923, + "2023-12-20": 0.1983, + "2023-12-21": 0.2239, + "2023-12-22": 0.2396, + "2023-12-23": 0.2354, + "2023-12-24": 0.2307, + "2023-12-25": 0.2383, + "2023-12-26": 0.235, + "2023-12-27": 0.2288, + "2023-12-28": 0.2339, + "2023-12-29": 0.2267, + "2023-12-30": 0.2211, + "2023-12-31": 0.2229, + "2024-01-01": 0.2387, + "2024-01-02": 0.2268, + "2024-01-03": 0.2047, + "2024-01-04": 0.2073, + "2024-01-05": 0.1965, + "2024-01-06": 0.1897, + "2024-01-07": 0.1782, + "2024-01-08": 0.1949, + "2024-01-09": 0.1857, + "2024-01-10": 0.1994, + "2024-01-11": 0.2026, + "2024-01-12": 0.1866, + "2024-01-13": 0.1892, + "2024-01-14": 0.1837, + "2024-01-15": 0.1884, + "2024-01-16": 0.1959, + "2024-01-17": 0.1911, + "2024-01-18": 0.179, + "2024-01-19": 0.1732, + "2024-01-20": 0.1721, + "2024-01-21": 0.1701, + "2024-01-22": 0.1613, + "2024-01-23": 0.1601, + "2024-01-24": 0.1634, + "2024-01-25": 0.1611, + "2024-01-26": 0.1665, + "2024-01-27": 0.1675, + "2024-01-28": 0.1642, + "2024-01-29": 0.1689, + "2024-01-30": 0.1645, + "2024-01-31": 0.1592, + "2024-02-01": 0.1588, + "2024-02-02": 0.1609, + "2024-02-03": 0.1657, + "2024-02-04": 0.1588, + "2024-02-05": 0.16, + "2024-02-06": 0.1622, + "2024-02-07": 0.1681, + "2024-02-08": 0.1689, + "2024-02-09": 0.1743, + "2024-02-10": 0.1754, + "2024-02-11": 0.1801, + "2024-02-12": 0.1829, + "2024-02-13": 0.1777, + "2024-02-14": 0.1835, + "2024-02-15": 0.1873, + "2024-02-16": 0.1942, + "2024-02-17": 0.1904, + "2024-02-18": 0.1964, + "2024-02-19": 0.2036, + "2024-02-20": 0.1963, + "2024-02-21": 0.189, + "2024-02-22": 0.1888, + "2024-02-23": 0.1854, + "2024-02-24": 0.1972, + "2024-02-25": 0.2078, + "2024-02-26": 0.2121, + "2024-02-27": 0.2083, + "2024-02-28": 0.2106, + "2024-02-29": 0.2118, + "2024-03-01": 0.2281, + "2024-03-02": 0.248, + "2024-03-03": 0.2511, + "2024-03-04": 0.2506, + "2024-03-05": 0.2314, + "2024-03-06": 0.2592, + "2024-03-07": 0.2799, + "2024-03-08": 0.2713, + "2024-03-09": 0.2692, + "2024-03-10": 0.2628, + "2024-03-11": 0.2918, + "2024-03-12": 0.3178, + "2024-03-13": 0.3187, + "2024-03-14": 0.3118, + "2024-03-15": 0.2992, + "2024-03-16": 0.2673, + "2024-03-17": 0.2754, + "2024-03-18": 0.2516, + "2024-03-19": 0.2303, + "2024-03-20": 0.2506, + "2024-03-21": 0.2487, + "2024-03-22": 0.2412, + "2024-03-23": 0.2484, + "2024-03-24": 0.2562, + "2024-03-25": 0.268, + "2024-03-26": 0.2799, + "2024-03-27": 0.2708, + "2024-03-28": 0.2728, + "2024-03-29": 0.2687, + "2024-03-30": 0.2602, + "2024-03-31": 0.268, + "2024-04-01": 0.2513, + "2024-04-02": 0.2343, + "2024-04-03": 0.2321, + "2024-04-04": 0.2371, + "2024-04-05": 0.2294, + "2024-04-06": 0.2336, + "2024-04-07": 0.2352, + "2024-04-08": 0.2453, + "2024-04-09": 0.2319, + "2024-04-10": 0.2305, + "2024-04-11": 0.2277, + "2024-04-12": 0.1936, + "2024-04-13": 0.1679, + "2024-04-14": 0.1794, + "2024-04-15": 0.1703, + "2024-04-16": 0.1744, + "2024-04-17": 0.1685, + "2024-04-18": 0.1746, + "2024-04-19": 0.1748, + "2024-04-20": 0.1916, + "2024-04-21": 0.1874, + "2024-04-22": 0.1951, + "2024-04-23": 0.1936, + "2024-04-24": 0.2113, + "2024-04-25": 0.2, + "2024-04-26": 0.1978, + "2024-04-27": 0.1983, + "2024-04-28": 0.1928, + "2024-04-29": 0.1906, + "2024-04-30": 0.1781, + "2024-05-01": 0.1807, + "2024-05-02": 0.1812, + "2024-05-03": 0.192, + "2024-05-04": 0.1927, + "2024-05-05": 0.1964, + "2024-05-06": 0.1938, + "2024-05-07": 0.1907, + "2024-05-08": 0.1871, + "2024-05-09": 0.1934, + "2024-05-10": 0.1833, + "2024-05-11": 0.1789, + "2024-05-12": 0.1768, + "2024-05-13": 0.1777, + "2024-05-14": 0.1713, + "2024-05-15": 0.1799, + "2024-05-16": 0.1741, + "2024-05-17": 0.1829, + "2024-05-18": 0.1828, + "2024-05-19": 0.1749, + "2024-05-20": 0.1886, + "2024-05-21": 0.1884, + "2024-05-22": 0.1857, + "2024-05-23": 0.1817, + "2024-05-24": 0.1886, + "2024-05-25": 0.1935, + "2024-05-26": 0.1895, + "2024-05-27": 0.1967, + "2024-05-28": 0.1941, + "2024-05-29": 0.1894, + "2024-05-30": 0.1897, + "2024-05-31": 0.1881, + "2024-06-01": 0.1881, + "2024-06-02": 0.1823, + "2024-06-03": 0.1837, + "2024-06-04": 0.186, + "2024-06-05": 0.1887, + "2024-06-06": 0.1857, + "2024-06-07": 0.1749, + "2024-06-08": 0.166, + "2024-06-09": 0.1704, + "2024-06-10": 0.1666, + "2024-06-11": 0.1589, + "2024-06-12": 0.166, + "2024-06-13": 0.1559, + "2024-06-14": 0.1512, + "2024-06-15": 0.1524, + "2024-06-16": 0.1557, + "2024-06-17": 0.1451, + "2024-06-18": 0.1338, + "2024-06-19": 0.1387, + "2024-06-20": 0.1382, + "2024-06-21": 0.1393, + "2024-06-22": 0.1339, + "2024-06-23": 0.1324, + "2024-06-24": 0.1352, + "2024-06-25": 0.1411, + "2024-06-26": 0.1408, + "2024-06-27": 0.1428, + "2024-06-28": 0.1397, + "2024-06-29": 0.1393, + "2024-06-30": 0.1445, + "2024-07-01": 0.1471, + "2024-07-02": 0.1502, + "2024-07-03": 0.1426, + "2024-07-04": 0.1313, + "2024-07-05": 0.1307, + "2024-07-06": 0.1386, + "2024-07-07": 0.1266, + "2024-07-08": 0.1333, + "2024-07-09": 0.1397, + "2024-07-10": 0.1394, + "2024-07-11": 0.1372, + "2024-07-12": 0.1381, + "2024-07-13": 0.1433, + "2024-07-14": 0.152, + "2024-07-15": 0.1577, + "2024-07-16": 0.1585, + "2024-07-17": 0.1545, + "2024-07-18": 0.1518, + "2024-07-19": 0.1577, + "2024-07-20": 0.1581, + "2024-07-21": 0.1592, + "2024-07-22": 0.151, + "2024-07-23": 0.1435, + "2024-07-24": 0.1397, + "2024-07-25": 0.1371, + "2024-07-26": 0.1425, + "2024-07-27": 0.1428, + "2024-07-28": 0.1393, + "2024-07-29": 0.1389, + "2024-07-30": 0.1375, + "2024-07-31": 0.1357, + "2024-08-01": 0.1349, + "2024-08-02": 0.1241, + "2024-08-03": 0.1204, + "2024-08-04": 0.1147, + "2024-08-05": 0.1101, + "2024-08-06": 0.1126, + "2024-08-07": 0.1098, + "2024-08-08": 0.1222, + "2024-08-09": 0.1201, + "2024-08-10": 0.1209, + "2024-08-11": 0.1152, + "2024-08-12": 0.1196, + "2024-08-13": 0.1213, + "2024-08-14": 0.1195, + "2024-08-15": 0.1155, + "2024-08-16": 0.1156, + "2024-08-17": 0.1186, + "2024-08-18": 0.1188, + "2024-08-19": 0.1237, + "2024-08-20": 0.1279, + "2024-08-21": 0.1346, + "2024-08-22": 0.1358, + "2024-08-23": 0.1441, + "2024-08-24": 0.1465, + "2024-08-25": 0.1415, + "2024-08-26": 0.1346, + "2024-08-27": 0.1283, + "2024-08-28": 0.127, + "2024-08-29": 0.1267, + "2024-08-30": 0.1284, + "2024-08-31": 0.1233, + "2024-09-01": 0.1184, + "2024-09-02": 0.1257, + "2024-09-03": 0.1194, + "2024-09-04": 0.1224, + "2024-09-05": 0.1176, + "2024-09-06": 0.1164, + "2024-09-07": 0.1208, + "2024-09-08": 0.125, + "2024-09-09": 0.1279, + "2024-09-10": 0.1296, + "2024-09-11": 0.1269, + "2024-09-12": 0.1301, + "2024-09-13": 0.1332, + "2024-09-14": 0.1311, + "2024-09-15": 0.1261, + "2024-09-16": 0.1236, + "2024-09-17": 0.1241, + "2024-09-18": 0.1278, + "2024-09-19": 0.1315, + "2024-09-20": 0.1329, + "2024-09-21": 0.1364, + "2024-09-22": 0.1322, + "2024-09-23": 0.1343, + "2024-09-24": 0.1384, + "2024-09-25": 0.1373, + "2024-09-26": 0.1437, + "2024-09-27": 0.1441, + "2024-09-28": 0.1413, + "2024-09-29": 0.1411, + "2024-09-30": 0.1341, + "2024-10-01": 0.1248, + "2024-10-02": 0.121, + "2024-10-03": 0.1222, + "2024-10-04": 0.126, + "2024-10-05": 0.1258, + "2024-10-06": 0.1269, + "2024-10-07": 0.1244, + "2024-10-08": 0.1214, + "2024-10-09": 0.1188, + "2024-10-10": 0.1185, + "2024-10-11": 0.1214, + "2024-10-12": 0.1218, + "2024-10-13": 0.1209, + "2024-10-14": 0.1268, + "2024-10-15": 0.1258, + "2024-10-16": 0.1231, + "2024-10-17": 0.1198, + "2024-10-18": 0.1232, + "2024-10-19": 0.1233, + "2024-10-20": 0.1304, + "2024-10-21": 0.1257, + "2024-10-22": 0.1242, + "2024-10-23": 0.1213, + "2024-10-24": 0.1221, + "2024-10-25": 0.1146, + "2024-10-26": 0.1146, + "2024-10-27": 0.1159, + "2024-10-28": 0.1184, + "2024-10-29": 0.1239, + "2024-10-30": 0.1206, + "2024-10-31": 0.1143, + "2024-11-01": 0.1146, + "2024-11-02": 0.1137, + "2024-11-03": 0.1096, + "2024-11-04": 0.1094, + "2024-11-05": 0.1141, + "2024-11-06": 0.1217, + "2024-11-07": 0.1225, + "2024-11-08": 0.1313, + "2024-11-09": 0.1338, + "2024-11-10": 0.1453, + "2024-11-11": 0.1593, + "2024-11-12": 0.1541, + "2024-11-13": 0.1453, + "2024-11-14": 0.1454, + "2024-11-15": 0.1853, + "2024-11-16": 0.1966, + "2024-11-17": 0.1838, + "2024-11-18": 0.216, + "2024-11-19": 0.223, + "2024-11-20": 0.2109, + "2024-11-21": 0.2142, + "2024-11-22": 0.2585, + "2024-11-23": 0.2936, + "2024-11-24": 0.284, + "2024-11-25": 0.2597, + "2024-11-26": 0.2974, + "2024-11-27": 0.293, + "2024-11-28": 0.3225, + "2024-11-29": 0.4417, + "2024-11-30": 0.4438, + "2024-12-01": 0.4853, + "2024-12-02": 0.5043, + "2024-12-03": 0.5056, + "2024-12-04": 0.4782, + "2024-12-05": 0.4497, + "2024-12-06": 0.4984, + "2024-12-07": 0.5089, + "2024-12-08": 0.4969, + "2024-12-09": 0.4161, + "2024-12-10": 0.4236, + "2024-12-11": 0.452, + "2024-12-12": 0.428, + "2024-12-13": 0.4374, + "2024-12-14": 0.4211, + "2024-12-15": 0.4318, + "2024-12-16": 0.4131, + "2024-12-17": 0.4121, + "2024-12-18": 0.3681, + "2024-12-19": 0.3322, + "2024-12-20": 0.3475, + "2024-12-21": 0.3282, + "2024-12-22": 0.3476, + "2024-12-23": 0.3764, + "2024-12-24": 0.3963, + "2024-12-25": 0.3811, + "2024-12-26": 0.3465, + "2024-12-27": 0.3331, + "2024-12-28": 0.3452, + "2024-12-29": 0.3253, + "2024-12-30": 0.3283, + "2024-12-31": 0.3345, + "2025-01-01": 0.3761, + "2025-01-02": 0.3923, + "2025-01-03": 0.419, + "2025-01-04": 0.41, + "2025-01-05": 0.405, + "2025-01-06": 0.415, + "2025-01-07": 0.3726, + "2025-01-08": 0.3592, + "2025-01-09": 0.3417, + "2025-01-10": 0.3725, + "2025-01-11": 0.3777, + "2025-01-12": 0.3603, + "2025-01-13": 0.3493, + "2025-01-14": 0.3726, + "2025-01-15": 0.4445, + "2025-01-16": 0.4633, + "2025-01-17": 0.4683, + "2025-01-18": 0.4456, + "2025-01-19": 0.398, + "2025-01-20": 0.43, + "2025-01-21": 0.4243, + "2025-01-22": 0.4054, + "2025-01-23": 0.4102, + "2025-01-24": 0.4001, + "2025-01-25": 0.4034, + "2025-01-26": 0.3815, + "2025-01-27": 0.3867, + "2025-01-28": 0.363, + "2025-01-29": 0.3694, + "2025-01-30": 0.3875, + "2025-01-31": 0.3791, + "2025-02-01": 0.3476, + "2025-02-02": 0.3, + "2025-02-03": 0.3129, + "2025-02-04": 0.2912, + "2025-02-05": 0.2813, + "2025-02-06": 0.267, + "2025-02-07": 0.2696, + "2025-02-08": 0.2761, + "2025-02-09": 0.2752, + "2025-02-10": 0.2902, + "2025-02-11": 0.286, + "2025-02-12": 0.3025, + "2025-02-13": 0.2934, + "2025-02-14": 0.2964, + "2025-02-15": 0.2844, + "2025-02-16": 0.2752, + "2025-02-17": 0.2702, + "2025-02-18": 0.2574, + "2025-02-19": 0.2614, + "2025-02-20": 0.271, + "2025-02-21": 0.2639, + "2025-02-22": 0.2662, + "2025-02-23": 0.2596, + "2025-02-24": 0.2273, + "2025-02-25": 0.2319, + "2025-02-26": 0.2362, + "2025-02-27": 0.2373, + "2025-02-28": 0.2379, + "2025-03-01": 0.2533, + "2025-03-02": 0.2909, + "2025-03-03": 0.2381, + "2025-03-04": 0.2384, + "2025-03-05": 0.2501, + "2025-03-06": 0.2402, + "2025-03-07": 0.2331, + "2025-03-08": 0.2247, + "2025-03-09": 0.2011, + "2025-03-10": 0.1839, + "2025-03-11": 0.1912, + "2025-03-12": 0.1957, + "2025-03-13": 0.1958, + "2025-03-14": 0.1985, + "2025-03-15": 0.1998, + "2025-03-16": 0.1864, + "2025-03-17": 0.1912, + "2025-03-18": 0.1865, + "2025-03-19": 0.1997, + "2025-03-20": 0.1952, + "2025-03-21": 0.1874, + "2025-03-22": 0.1881, + "2025-03-23": 0.1918, + "2025-03-24": 0.2036, + "2025-03-25": 0.2119, + "2025-03-26": 0.2067, + "2025-03-27": 0.212, + "2025-03-28": 0.1997, + "2025-03-29": 0.1841, + "2025-03-30": 0.1795, + "2025-03-31": 0.18, + "2025-04-01": 0.1889, + "2025-04-02": 0.1768, + "2025-04-03": 0.181, + "2025-04-04": 0.1862, + "2025-04-05": 0.1833, + "2025-04-06": 0.1622, + "2025-04-07": 0.1666, + "2025-04-08": 0.1551, + "2025-04-09": 0.1801, + "2025-04-10": 0.1765, + "2025-04-11": 0.1797, + "2025-04-12": 0.191, + "2025-04-13": 0.1829, + "2025-04-14": 0.1889, + "2025-04-15": 0.1795, + "2025-04-16": 0.1813, + "2025-04-17": 0.187, + "2025-04-18": 0.1893, + "2025-04-19": 0.1929, + "2025-04-20": 0.1929, + "2025-04-21": 0.1929, + "2025-04-22": 0.2096, + "2025-04-23": 0.2137, + "2025-04-24": 0.2257, + "2025-04-25": 0.2275, + "2025-04-26": 0.2285, + "2025-04-27": 0.2214, + "2025-04-28": 0.2307, + "2025-04-29": 0.2265, + "2025-04-30": 0.22, + "2025-05-01": 0.2172, + "2025-05-02": 0.2156, + "2025-05-03": 0.2073, + "2025-05-04": 0.1995, + "2025-05-05": 0.1992, + "2025-05-06": 0.1993, + "2025-05-07": 0.1993, + "2025-05-08": 0.2257, + "2025-05-09": 0.2328, + "2025-05-10": 0.2534, + "2025-05-11": 0.2396, + "2025-05-12": 0.2421, + "2025-05-13": 0.2471, + "2025-05-14": 0.2367, + "2025-05-15": 0.2259, + "2025-05-16": 0.2231, + "2025-05-17": 0.2158, + "2025-05-18": 0.2273, + "2025-05-19": 0.2221, + "2025-05-20": 0.2244, + "2025-05-21": 0.2306, + "2025-05-22": 0.2415, + "2025-05-23": 0.2239, + "2025-05-24": 0.2207, + "2025-05-25": 0.2216, + "2025-05-26": 0.219, + "2025-05-27": 0.2219, + "2025-05-28": 0.2175, + "2025-05-29": 0.2096, + "2025-05-30": 0.1924, + "2025-05-31": 0.1938, + "2025-06-01": 0.1955, + "2025-06-02": 0.1986, + "2025-06-03": 0.1973, + "2025-06-04": 0.1914, + "2025-06-05": 0.1809, + "2025-06-06": 0.1872, + "2025-06-07": 0.1909, + "2025-06-08": 0.1906, + "2025-06-09": 0.1995, + "2025-06-10": 0.2082, + "2025-06-11": 0.1971, + "2025-06-12": 0.1804, + "2025-06-13": 0.1807, + "2025-06-14": 0.177, + "2025-06-15": 0.1765, + "2025-06-16": 0.1759, + "2025-06-17": 0.1695, + "2025-06-18": 0.1706, + "2025-06-19": 0.1692, + "2025-06-20": 0.1672, + "2025-06-21": 0.1627, + "2025-06-22": 0.1601, + "2025-06-23": 0.1791, + "2025-06-24": 0.1827, + "2025-06-25": 0.1756, + "2025-06-26": 0.1721, + "2025-06-27": 0.1747, + "2025-06-28": 0.1764, + "2025-06-29": 0.1858, + "2025-06-30": 0.1854, + "2025-07-01": 0.1709, + "2025-07-02": 0.1811, + "2025-07-03": 0.1821, + "2025-07-04": 0.1735, + "2025-07-05": 0.1745, + "2025-07-06": 0.177, + "2025-07-07": 0.1759, + "2025-07-08": 0.1792, + "2025-07-09": 0.1921, + "2025-07-10": 0.2121, + "2025-07-11": 0.2182, + "2025-07-12": 0.219, + "2025-07-13": 0.2638, + "2025-07-14": 0.2796, + "2025-07-15": 0.2837, + "2025-07-16": 0.2846, + "2025-07-17": 0.3186, + "2025-07-18": 0.2936, + "2025-07-19": 0.2913, + "2025-07-20": 0.2911, + "2025-07-21": 0.2981, + "2025-07-22": 0.2967, + "2025-07-23": 0.2675, + "2025-07-24": 0.2616, + "2025-07-25": 0.2669, + "2025-07-26": 0.2745, + "2025-07-27": 0.2859, + "2025-07-28": 0.2651, + "2025-07-29": 0.2606, + "2025-07-30": 0.2537, + "2025-07-31": 0.2441, + "2025-08-01": 0.236, + "2025-08-02": 0.2275, + "2025-08-03": 0.2439, + "2025-08-04": 0.2482, + "2025-08-05": 0.2376, + "2025-08-06": 0.2428, + "2025-08-07": 0.2644, + "2025-08-08": 0.2664, + "2025-08-09": 0.27, + "2025-08-10": 0.2664, + "2025-08-11": 0.2531, + "2025-08-12": 0.2714, + "2025-08-13": 0.2805, + "2025-08-14": 0.2537, + "2025-08-15": 0.254, + "2025-08-16": 0.2608, + "2025-08-17": 0.2649, + "2025-08-18": 0.255, + "2025-08-19": 0.238, + "2025-08-20": 0.2568, + "2025-08-21": 0.2484, + "2025-08-22": 0.2665, + "2025-08-23": 0.264, + "2025-08-24": 0.2715, + "2025-08-25": 0.246, + "2025-08-26": 0.2547, + "2025-08-27": 0.2474, + "2025-08-28": 0.2529, + "2025-08-29": 0.2364, + "2025-08-30": 0.2336, + "2025-08-31": 0.2315, + "2025-09-01": 0.2277, + "2025-09-02": 0.2333, + "2025-09-03": 0.2329, + "2025-09-04": 0.2272, + "2025-09-05": 0.2295, + "2025-09-06": 0.2273, + "2025-09-07": 0.2307, + "2025-09-08": 0.2364, + "2025-09-09": 0.2331, + "2025-09-10": 0.2394, + "2025-09-11": 0.2442, + "2025-09-12": 0.2485, + "2025-09-13": 0.2529, + "2025-09-14": 0.2394, + "2025-09-15": 0.2327, + "2025-09-16": 0.2375, + "2025-09-17": 0.2442, + "2025-09-18": 0.2461, + "2025-09-19": 0.234, + "2025-09-20": 0.2355, + "2025-09-21": 0.231, + "2025-09-22": 0.214, + "2025-09-23": 0.2108, + "2025-09-24": 0.2128, + "2025-09-25": 0.203, + "2025-09-26": 0.2085, + "2025-09-27": 0.2056, + "2025-09-28": 0.2081, + "2025-09-29": 0.2075, + "2025-09-30": 0.2079, + "2025-10-01": 0.221, + "2025-10-02": 0.2251, + "2025-10-03": 0.2248, + "2025-10-04": 0.2197, + "2025-10-05": 0.2186, + "2025-10-06": 0.2292, + "2025-10-07": 0.2189, + "2025-10-08": 0.2241, + "2025-10-09": 0.2203, + "2025-10-10": 0.1752, + "2025-10-11": 0.1817, + "2025-10-12": 0.2025, + "2025-10-13": 0.213, + "2025-10-14": 0.2054, + "2025-10-15": 0.1965, + "2025-10-16": 0.1852, + "2025-10-17": 0.1772, + "2025-10-18": 0.1798, + "2025-10-19": 0.1836, + "2025-10-20": 0.1867, + "2025-10-21": 0.1812, + "2025-10-22": 0.1784, + "2025-10-23": 0.1814, + "2025-10-24": 0.1856, + "2025-10-25": 0.1853, + "2025-10-26": 0.1907, + "2025-10-27": 0.1857, + "2025-10-28": 0.1844, + "2025-10-29": 0.1837, + "2025-10-30": 0.174, + "2025-10-31": 0.1773, + "2025-11-01": 0.1793, + "2025-11-02": 0.1783, + "2025-11-03": 0.1602, + "2025-11-04": 0.1539, + "2025-11-05": 0.1616, + "2025-11-06": 0.1573, + "2025-11-07": 0.1811, + "2025-11-08": 0.1779, + "2025-11-09": 0.1779, + "2025-11-10": 0.1888, + "2025-11-11": 0.1779, + "2025-11-12": 0.1721, + "2025-11-13": 0.1675, + "2025-11-14": 0.161, + "2025-11-15": 0.1637, + "2025-11-16": 0.1589, + "2025-11-17": 0.1525, + "2025-11-18": 0.1569, + "2025-11-19": 0.1521, + "2025-11-20": 0.1441, + "2025-11-21": 0.137, + "2025-11-22": 0.1357, + "2025-11-23": 0.1439, + "2025-11-24": 0.1437, + "2025-11-25": 0.1465, + "2025-11-26": 0.1463, + "2025-11-27": 0.1443, + "2025-11-28": 0.1433, + "2025-11-29": 0.1394, + "2025-11-30": 0.1366, + "2025-12-01": 0.1304, + "2025-12-02": 0.1388, + "2025-12-03": 0.1437, + "2025-12-04": 0.1386, + "2025-12-05": 0.1327, + "2025-12-06": 0.1335, + "2025-12-07": 0.133, + "2025-12-08": 0.1336, + "2025-12-09": 0.138, + "2025-12-10": 0.1349, + "2025-12-11": 0.1299, + "2025-12-12": 0.1228, + "2025-12-13": 0.1219, + "2025-12-14": 0.1188, + "2025-12-15": 0.1156, + "2025-12-16": 0.1168, + "2025-12-17": 0.1116, + "2025-12-18": 0.1073, + "2025-12-19": 0.1147, + "2025-12-20": 0.1149, + "2025-12-21": 0.1115, + "2025-12-22": 0.1117, + "2025-12-23": 0.1137, + "2025-12-24": 0.1131, + "2025-12-25": 0.1148, + "2025-12-26": 0.1184, + "2025-12-27": 0.1203, + "2025-12-28": 0.1198, + "2025-12-29": 0.1174, + "2025-12-30": 0.1149, + "2025-12-31": 0.1104, + "2026-01-01": 0.1201, + "2026-01-02": 0.1275, + "2026-01-03": 0.1278, + "2026-01-04": 0.1362, + "2026-01-05": 0.1397, + "2026-01-06": 0.1424, + "2026-01-07": 0.1364, + "2026-01-08": 0.1344, + "2026-01-09": 0.1332, + "2026-01-10": 0.1338, + "2026-01-11": 0.1322, + "2026-01-12": 0.129, + "2026-01-13": 0.1386, + "2026-01-14": 0.1364, + "2026-01-15": 0.1281, + "2026-01-16": 0.1302, + "2026-01-17": 0.1314, + "2026-01-18": 0.1228, + "2026-01-19": 0.1209, + "2026-01-20": 0.1143, + "2026-01-21": 0.1176, + "2026-01-22": 0.1187, + "2026-01-23": 0.1202, + "2026-01-24": 0.1186, + "2026-01-25": 0.1147, + "2026-01-26": 0.1221, + "2026-01-27": 0.1218, + "2026-01-28": 0.1262, + "2026-01-29": 0.1144, + "2026-01-30": 0.1121, + "2026-01-31": 0.1041, + "2026-02-01": 0.1005, + "2026-02-02": 0.1049, + "2026-02-03": 0.1063, + "2026-02-04": 0.1031, + "2026-02-05": 0.0884, + "2026-02-06": 0.0988, + "2026-02-07": 0.0976, + "2026-02-08": 0.0968, + "2026-02-09": 0.096, + "2026-02-10": 0.09165, + "2026-02-11": 0.08897, + "2026-02-12": 0.09189, + "2026-02-13": 0.09407, + "2026-02-14": 0.09972, + "2026-02-15": 0.09599, + "2026-02-16": 0.09465, + "2026-02-17": 0.09306, + "2026-02-18": 0.09094, + "2026-02-19": 0.08919, + "2026-02-20": 0.09055, + "2026-02-21": 0.08976, + "2026-02-22": 0.08684, + "2026-02-23": 0.08385 +} \ No newline at end of file diff --git a/src/hooks/usePriceImpactData.test.ts b/src/hooks/usePriceImpactData.test.ts new file mode 100644 index 0000000..d637387 --- /dev/null +++ b/src/hooks/usePriceImpactData.test.ts @@ -0,0 +1,85 @@ +import { beforeEach, describe, expect, it, vi } from "vitest"; +import { renderHook } from "@testing-library/react"; +import { usePriceImpactData } from "./usePriceImpactData"; +import { MinimalBlock } from "@/lib/block-types"; +import { useAlgoPrice } from "@/hooks/queries/useAlgoPrice"; +import { getAlgoUsdPrice } from "@/utils/algoPrice"; +import { generateDateRange } from "@/lib/date-utils"; + +vi.mock("@/hooks/queries/useAlgoPrice", () => ({ + useAlgoPrice: vi.fn(), +})); + +vi.mock("@/utils/algoPrice", () => ({ + getAlgoUsdPrice: vi.fn(), +})); + +vi.mock("@/lib/date-utils", () => ({ + generateDateRange: vi.fn(), +})); + +const mockUseAlgoPrice = vi.mocked(useAlgoPrice); +const mockGetAlgoUsdPrice = vi.mocked(getAlgoUsdPrice); +const mockGenerateDateRange = vi.mocked(generateDateRange); + +function createMockBlock(timestampIso: string, proposerPayout: number): MinimalBlock { + return { + round: 1, + timestamp: Math.floor(new Date(timestampIso).getTime() / 1000), + proposer: "TESTADDR", + proposerPayout, + }; +} + +describe("usePriceImpactData", () => { + beforeEach(() => { + vi.clearAllMocks(); + mockUseAlgoPrice.mockReturnValue({ + data: 0.1, + isLoading: false, + } as ReturnType); + }); + + it("groups rewards by UTC date across timezone boundaries", () => { + const block = createMockBlock("2026-02-23T23:30:00Z", 2_000_000); + mockGenerateDateRange.mockReturnValue(["2026-02-23", "2026-02-24"]); + mockGetAlgoUsdPrice.mockImplementation((date) => + date === "2026-02-23" ? 0.08 : 0.09, + ); + + const { result } = renderHook(() => usePriceImpactData([block])); + + const feb23 = result.current.fullData.find((d) => d.date === "2026-02-23"); + const feb24 = result.current.fullData.find((d) => d.date === "2026-02-24"); + + expect(feb23?.rewardAlgo).toBe(2); + expect(feb24?.rewardAlgo).toBe(0); + expect(result.current.scatterData).toHaveLength(1); + expect(result.current.scatterData[0].date).toBe("2026-02-23"); + }); + + it("excludes reward days from scatter data when close price is missing", () => { + const block = createMockBlock("2026-02-24T03:00:00Z", 1_500_000); + mockGenerateDateRange.mockReturnValue(["2026-02-24"]); + mockGetAlgoUsdPrice.mockReturnValue(null); + + const { result } = renderHook(() => usePriceImpactData([block])); + + expect(result.current.fullData[0].rewardAlgo).toBe(1.5); + expect(result.current.fullData[0].priceAtReceipt).toBeNull(); + expect(result.current.fullData[0].valueAtReceipt).toBeNull(); + expect(result.current.scatterData).toHaveLength(0); + }); + + it("includes reward days in scatter data once close price is available", () => { + const block = createMockBlock("2026-02-24T03:00:00Z", 1_500_000); + mockGenerateDateRange.mockReturnValue(["2026-02-24"]); + mockGetAlgoUsdPrice.mockReturnValue(0.08385); + + const { result } = renderHook(() => usePriceImpactData([block])); + + expect(result.current.scatterData).toHaveLength(1); + expect(result.current.scatterData[0].date).toBe("2026-02-24"); + expect(result.current.scatterData[0].valueAtReceipt).toBeCloseTo(0.125775, 6); + }); +}); diff --git a/src/hooks/usePriceImpactData.ts b/src/hooks/usePriceImpactData.ts new file mode 100644 index 0000000..f39954a --- /dev/null +++ b/src/hooks/usePriceImpactData.ts @@ -0,0 +1,109 @@ +import { useMemo } from "react"; +import { MinimalBlock } from "@/lib/block-types"; +import { useAlgoPrice } from "@/hooks/queries/useAlgoPrice"; +import { getAlgoUsdPrice } from "@/utils/algoPrice"; +import { generateDateRange } from "@/lib/date-utils"; + +export interface PriceImpactDataPoint { + date: string; + rewardAlgo: number; + blocks: number; + priceAtReceipt: number | null; + valueAtReceipt: number | null; + valueAtToday: number | null; + cumulativeValueAtReceipt: number; + cumulativeValueAtToday: number; +} + +export function usePriceImpactData(blocks: MinimalBlock[]) { + const { data: currentPrice, isLoading: isPriceLoading } = useAlgoPrice("USD"); + + const fullData = useMemo(() => { + if (!blocks.length || currentPrice === undefined) return []; + + // Sort blocks by timestamp + const sortedBlocks = [...blocks].sort( + (a, b) => (a.timestamp || 0) - (b.timestamp || 0), + ); + + // Group blocks by day + const dailyStats = new Map< + string, + { rewardAlgo: number; blocks: number } + >(); + + sortedBlocks.forEach((block) => { + const date = new Date(block.timestamp * 1000); + const year = date.getUTCFullYear(); + const month = String(date.getUTCMonth() + 1).padStart(2, "0"); + const day = String(date.getUTCDate()).padStart(2, "0"); + const dateStr = `${year}-${month}-${day}`; + + const reward = block.proposerPayout / 1e6; + const stats = dailyStats.get(dateStr) || { rewardAlgo: 0, blocks: 0 }; + dailyStats.set(dateStr, { + rewardAlgo: stats.rewardAlgo + reward, + blocks: stats.blocks + 1, + }); + }); + + const allDates = generateDateRange( + sortedBlocks.length > 0 ? sortedBlocks[0].timestamp : undefined, + ); + + let cumulativeValueAtReceipt = 0; + let cumulativeValueAtToday = 0; + + const result: PriceImpactDataPoint[] = []; + + allDates.forEach((dateStr) => { + const stats = dailyStats.get(dateStr) || { rewardAlgo: 0, blocks: 0 }; + const priceAtReceipt = getAlgoUsdPrice(dateStr); + + let valueAtReceipt = null; + let valueAtToday = null; + + if (stats.rewardAlgo > 0 && priceAtReceipt !== null) { + valueAtReceipt = stats.rewardAlgo * priceAtReceipt; + valueAtToday = stats.rewardAlgo * currentPrice; + + cumulativeValueAtReceipt += valueAtReceipt; + cumulativeValueAtToday += valueAtToday; + } + + result.push({ + date: dateStr, + rewardAlgo: stats.rewardAlgo, + blocks: stats.blocks, + priceAtReceipt, + valueAtReceipt, + valueAtToday, + cumulativeValueAtReceipt, + cumulativeValueAtToday, + }); + }); + + return result; + }, [blocks, currentPrice]); + + const scatterData = useMemo(() => { + return fullData.filter( + (d) => d.rewardAlgo > 0 && d.valueAtReceipt !== null, + ); + }, [fullData]); + + return { + fullData, + scatterData, + currentPrice, + isLoading: isPriceLoading, + totalValueAtReceipt: + fullData.length > 0 + ? fullData[fullData.length - 1].cumulativeValueAtReceipt + : 0, + totalValueAtToday: + fullData.length > 0 + ? fullData[fullData.length - 1].cumulativeValueAtToday + : 0, + }; +} diff --git a/src/utils/algoPrice.ts b/src/utils/algoPrice.ts new file mode 100644 index 0000000..296d6d2 --- /dev/null +++ b/src/utils/algoPrice.ts @@ -0,0 +1,26 @@ +import algoPriceHistory from "../data/algo-price-history.json"; + +const priceMap = algoPriceHistory as Record; + +/** + * Returns the ALGO/USDT closing price for a given date. + * @param date YYYY-MM-DD format + * @returns number | null + */ +export function getAlgoUsdPrice(date: string): number | null { + return priceMap[date] || null; +} + +/** + * Returns the ALGO price in a target fiat currency using a provided USD exchange rate. + * @param date YYYY-MM-DD format + * @param usdToFiat The exchange rate from USD to the target fiat (e.g., USD/AUD rate) + * @returns number | null + */ +export function getAlgoFiatPrice( + date: string, + usdToFiat: number, +): number | null { + const usdPrice = getAlgoUsdPrice(date); + return usdPrice ? usdPrice * usdToFiat : null; +} diff --git a/wrangler.jsonc b/wrangler.jsonc new file mode 100644 index 0000000..44d56f5 --- /dev/null +++ b/wrangler.jsonc @@ -0,0 +1,8 @@ +{ + "$schema": "node_modules/wrangler/config-schema.json", + "name": "algonoderewards", + "compatibility_date": "2024-04-01", + "assets": { + "directory": "./dist", + }, +}