From 2a1ce04ae96f5e9c43a7b01d5492e209be3b046a Mon Sep 17 00:00:00 2001 From: theonpham Date: Mon, 18 May 2026 17:07:27 +0700 Subject: [PATCH] init fork --- .gitignore | 34 + backend/data/seed.json | 151 + backend/database.sqlite | Bin 0 -> 20480 bytes backend/package-lock.json | 2001 ++++++++++ backend/package.json | 28 + backend/src/db.ts | 57 + backend/src/index.ts | 27 + backend/src/middleware/auth.ts | 24 + backend/src/routes/coins.ts | 91 + backend/src/routes/exchangeRate.ts | 31 + backend/src/routes/scores.ts | 54 + backend/src/routes/swap.ts | 41 + backend/src/websocket/scoreboard.ts | 48 + backend/tsconfig.json | 13 + docs/problem1and4/detail.md | 45 + docs/problem1and4/readme.md | 38 + docs/problem2/detail.md | 23 + docs/problem2/image-1.png | Bin 0 -> 54322 bytes docs/problem2/image.png | Bin 0 -> 55106 bytes docs/problem2/readme.md | 20 + docs/problem3/detail.md | 93 + docs/problem3/readme.md | 215 ++ docs/problem5/detail.md | 12 + docs/problem5/readme.md | 15 + docs/problem6/detail.md | 16 + docs/problem6/readme.md | 80 + frontend/.gitignore | 24 + frontend/eslint.config.js | 22 + frontend/index.html | 13 + frontend/package-lock.json | 3656 +++++++++++++++++++ frontend/package.json | 36 + frontend/postcss.config.js | 6 + frontend/public/coins/Bitcoin-filled.svg | 11 + frontend/public/coins/Carbon-filled.svg | 76 + frontend/public/coins/Cosmos-Hub-filled.svg | 16 + frontend/public/coins/Ethereum-filled.svg | 11 + frontend/public/coins/Evmos-filled.svg | 15 + frontend/public/coins/Iris-Hub-filled.svg | 57 + frontend/public/coins/Kujira-filled.svg | 66 + frontend/public/coins/Neo-filled.svg | 11 + frontend/public/coins/OKC-filled.svg | 20 + frontend/public/coins/Osmosis-filled.svg | 126 + frontend/public/coins/Stride-filled.svg | 16 + frontend/public/coins/Terra-filled.svg | 51 + frontend/public/coins/Zilliqa-filled.svg | 14 + frontend/public/favicon.svg | 1 + frontend/public/icons.svg | 24 + frontend/src/App.css | 184 + frontend/src/App.tsx | 108 + frontend/src/assets/hero.png | Bin 0 -> 13057 bytes frontend/src/assets/react.svg | 1 + frontend/src/assets/vite.svg | 1 + frontend/src/components/Layout.tsx | 93 + frontend/src/components/SumCalculator.tsx | 86 + frontend/src/hooks/useCoins.ts | 27 + frontend/src/hooks/useWebSocket.ts | 31 + frontend/src/index.css | 21 + frontend/src/main.tsx | 10 + frontend/src/pages/CoinsPage.tsx | 314 ++ frontend/src/pages/ScoreboardPage.tsx | 70 + frontend/src/pages/SwapPage.tsx | 253 ++ frontend/src/pages/WalletPage.tsx | 204 ++ frontend/src/swap.css | 238 ++ frontend/src/types/index.ts | 18 + frontend/src/utils/sum.ts | 35 + frontend/tailwind.config.js | 12 + frontend/tsconfig.app.json | 25 + frontend/tsconfig.json | 7 + frontend/tsconfig.node.json | 24 + frontend/vite.config.ts | 7 + readme.md | 30 +- run.sh | 22 + src/problem1/.keep | 0 src/problem2/index.html | 27 - src/problem2/script.js | 0 src/problem2/style.css | 8 - src/problem3/.keep | 0 src/problem4/.keep | 0 src/problem5/.keep | 0 79 files changed, 9243 insertions(+), 42 deletions(-) create mode 100644 .gitignore create mode 100644 backend/data/seed.json create mode 100644 backend/database.sqlite create mode 100644 backend/package-lock.json create mode 100644 backend/package.json create mode 100644 backend/src/db.ts create mode 100644 backend/src/index.ts create mode 100644 backend/src/middleware/auth.ts create mode 100644 backend/src/routes/coins.ts create mode 100644 backend/src/routes/exchangeRate.ts create mode 100644 backend/src/routes/scores.ts create mode 100644 backend/src/routes/swap.ts create mode 100644 backend/src/websocket/scoreboard.ts create mode 100644 backend/tsconfig.json create mode 100644 docs/problem1and4/detail.md create mode 100644 docs/problem1and4/readme.md create mode 100644 docs/problem2/detail.md create mode 100644 docs/problem2/image-1.png create mode 100644 docs/problem2/image.png create mode 100644 docs/problem2/readme.md create mode 100644 docs/problem3/detail.md create mode 100644 docs/problem3/readme.md create mode 100644 docs/problem5/detail.md create mode 100644 docs/problem5/readme.md create mode 100644 docs/problem6/detail.md create mode 100644 docs/problem6/readme.md create mode 100644 frontend/.gitignore create mode 100644 frontend/eslint.config.js create mode 100644 frontend/index.html create mode 100644 frontend/package-lock.json create mode 100644 frontend/package.json create mode 100644 frontend/postcss.config.js create mode 100644 frontend/public/coins/Bitcoin-filled.svg create mode 100644 frontend/public/coins/Carbon-filled.svg create mode 100644 frontend/public/coins/Cosmos-Hub-filled.svg create mode 100644 frontend/public/coins/Ethereum-filled.svg create mode 100644 frontend/public/coins/Evmos-filled.svg create mode 100644 frontend/public/coins/Iris-Hub-filled.svg create mode 100644 frontend/public/coins/Kujira-filled.svg create mode 100644 frontend/public/coins/Neo-filled.svg create mode 100644 frontend/public/coins/OKC-filled.svg create mode 100644 frontend/public/coins/Osmosis-filled.svg create mode 100644 frontend/public/coins/Stride-filled.svg create mode 100644 frontend/public/coins/Terra-filled.svg create mode 100644 frontend/public/coins/Zilliqa-filled.svg create mode 100644 frontend/public/favicon.svg create mode 100644 frontend/public/icons.svg create mode 100644 frontend/src/App.css create mode 100644 frontend/src/App.tsx create mode 100644 frontend/src/assets/hero.png create mode 100644 frontend/src/assets/react.svg create mode 100644 frontend/src/assets/vite.svg create mode 100644 frontend/src/components/Layout.tsx create mode 100644 frontend/src/components/SumCalculator.tsx create mode 100644 frontend/src/hooks/useCoins.ts create mode 100644 frontend/src/hooks/useWebSocket.ts create mode 100644 frontend/src/index.css create mode 100644 frontend/src/main.tsx create mode 100644 frontend/src/pages/CoinsPage.tsx create mode 100644 frontend/src/pages/ScoreboardPage.tsx create mode 100644 frontend/src/pages/SwapPage.tsx create mode 100644 frontend/src/pages/WalletPage.tsx create mode 100644 frontend/src/swap.css create mode 100644 frontend/src/types/index.ts create mode 100644 frontend/src/utils/sum.ts create mode 100644 frontend/tailwind.config.js create mode 100644 frontend/tsconfig.app.json create mode 100644 frontend/tsconfig.json create mode 100644 frontend/tsconfig.node.json create mode 100644 frontend/vite.config.ts create mode 100644 run.sh delete mode 100644 src/problem1/.keep delete mode 100644 src/problem2/index.html delete mode 100644 src/problem2/script.js delete mode 100644 src/problem2/style.css delete mode 100644 src/problem3/.keep delete mode 100644 src/problem4/.keep delete mode 100644 src/problem5/.keep diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..4b1cf585dd --- /dev/null +++ b/.gitignore @@ -0,0 +1,34 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +*/node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# Misc +.env +.env.local +.env.development.local +.env.test.local +.env.production.local + +scripts +mock-asset \ No newline at end of file diff --git a/backend/data/seed.json b/backend/data/seed.json new file mode 100644 index 0000000000..a16e00f0bd --- /dev/null +++ b/backend/data/seed.json @@ -0,0 +1,151 @@ +{ + "coins": [ + { + "currency": "ETH", + "blockchain": "Ethereum", + "price": 1645.9337373737374, + "date": "2023-08-29T07:10:52.000Z", + "iconFilename": "Ethereum-filled.svg" + }, + { + "currency": "WBTC", + "blockchain": "Bitcoin", + "price": 26002.82202020202, + "date": "2023-08-29T07:10:52.000Z", + "iconFilename": "Bitcoin-filled.svg" + }, + { + "currency": "ATOM", + "blockchain": "Cosmos Hub", + "price": 7.186657333333334, + "date": "2023-08-29T07:10:50.000Z", + "iconFilename": "Cosmos-Hub-filled.svg" + }, + { + "currency": "OSMO", + "blockchain": "Osmosis", + "price": 0.3772974333333333, + "date": "2023-08-29T07:10:50.000Z", + "iconFilename": "Osmosis-filled.svg" + }, + { + "currency": "LUNA", + "blockchain": "Terra", + "price": 0.40955638983050846, + "date": "2023-08-29T07:10:40.000Z", + "iconFilename": "Terra-filled.svg" + }, + { + "currency": "USDC", + "blockchain": "Ethereum", + "price": 0.989832, + "date": "2023-08-29T07:10:40.000Z", + "iconFilename": "Ethereum-filled.svg" + }, + { + "currency": "ZIL", + "blockchain": "Zilliqa", + "price": 0.01651813559322034, + "date": "2023-08-29T07:10:50.000Z", + "iconFilename": "Zilliqa-filled.svg" + }, + { + "currency": "EVMOS", + "blockchain": "Evmos", + "price": 0.06246181355932203, + "date": "2023-08-29T07:10:40.000Z", + "iconFilename": "Evmos-filled.svg" + }, + { + "currency": "KUJI", + "blockchain": "Kujira", + "price": 0.675, + "date": "2023-08-29T07:10:45.000Z", + "iconFilename": "Kujira-filled.svg" + }, + { + "currency": "SWTH", + "blockchain": "Carbon", + "price": 0.004039850455012084, + "date": "2023-08-29T07:10:45.000Z", + "iconFilename": "Carbon-filled.svg" + }, + { + "currency": "bNEO", + "blockchain": "Neo", + "price": 7.1282679, + "date": "2023-08-29T07:10:50.000Z", + "iconFilename": "Neo-filled.svg" + }, + { + "currency": "OKB", + "blockchain": "OKC", + "price": 42.97562059322034, + "date": "2023-08-29T07:10:40.000Z", + "iconFilename": "OKC-filled.svg" + }, + { + "currency": "IRIS", + "blockchain": "Iris Hub", + "price": 0.0177095593220339, + "date": "2023-08-29T07:10:40.000Z", + "iconFilename": "Iris-Hub-filled.svg" + }, + { + "currency": "STRD", + "blockchain": "Stride", + "price": 0.7386553389830508, + "date": "2023-08-29T07:10:40.000Z", + "iconFilename": "Stride-filled.svg" + }, + { + "currency": "wstETH", + "blockchain": "Ethereum", + "price": 1872.2579742372882, + "date": "2023-08-29T07:10:40.000Z", + "iconFilename": "Ethereum-filled.svg" + } + ], + "scores": [ + { + "username": "CryptoKing", + "score": 142 + }, + { + "username": "SwapMaster", + "score": 128 + }, + { + "username": "DeFiWhale", + "score": 115 + }, + { + "username": "TokenTrader", + "score": 98 + }, + { + "username": "BlockchainBoss", + "score": 87 + }, + { + "username": "CoinHunter", + "score": 76 + }, + { + "username": "Web3Wizard", + "score": 65 + }, + { + "username": "AltcoinAce", + "score": 54 + }, + { + "username": "SatoshiFan", + "score": 43 + }, + { + "username": "EtherEagle", + "score": 31 + } + ] +} \ No newline at end of file diff --git a/backend/database.sqlite b/backend/database.sqlite new file mode 100644 index 0000000000000000000000000000000000000000..5f7c68b02cdaeb4b8a77943a60dc44ffb55ca356 GIT binary patch literal 20480 zcmeI3U2NOd6~{@-i7eS7^HE1}oH$B?Zpw!feoCY#>QYIKWRg#2A`6S)v64i=H`Hf&tZB<8QTdQ?{S907)K_I&^T@YD#7X}Y3rSd}99?RZwWvf=g;Fe9jH!_rB@EP;vv;4s{o!yJf2P#TDFc0= z37-1e;+su9&yrjZay+^{l0bfR;15xuv<~Ia9MDSF72n=TF;(YV{A@)S%xu@atJm9tvQG5Bn$2vg~4N zW-&E8oxJnj>4z`;w3`xe=~+Y>1cP!!vJ1(@1=W~7{-q~>Kz#9jhYIh^6>#ZUWi7Zp zO{ri?4KNG+2rTv7nhT@l3`7~@#v%1|Mh{dQ>(Sup;OON>wd5rM*WjC%ifwx-vZZ( zR*|N@@t0HAzyILX4yo2$wcL6Z)Aw&J^_Atzv+m_N^|#j_M4vnQvktlc_cAW*19}d} z^vXgaqncpKeiyrd4}IoG-Nc$sJ&&x|50o5?i_7ziix!v*&6;}W@$a6V>3hCItHrV9 z*0Y-F18S%IlW~?!WESUV4JZdcG^UTgeP-mv_h0KK=y?859~;HKtr4!yWM*eH8`c%3 z-&G#G^5{Fmoq|mBVr#keJi-zVmZkHd=>5EZ>8+nU{=L$^Zffp=>RAr{{_i9IjFJBY zPv}4d5CKF05kLeG0Ym^1Km-s0L;w*$1Q3BwH-TWEZv=NXW{d@JUl?z0jyMQ^|Bv~9 zh><@hzf9gBj}Y$=_XveJ7y5hXVMq&&4*h!Q?hrrtr@>bS7Y6a*kAu13(ZDYQcLJ9N z-W&MlK+^wj5R48)01-e05CKF05%{ylx&n0 zRbc7@rGk3|t=rBKa6^~^f6OjT*G|A}ypxUNC^5Rxkn4@ASu55yi!oYhaGQeX7WRM3 zl|Jt28AnrWl&{D+Th+`>TGk8vreaA2mDfDopu`DYm;epiopqbiqn>O6E3naZmT5FJ z$}Uq%Mdh=!*f4m$yi4dVP3dEvY$7jGQJWQW+6HGyVm_bUWSAPWCRZyRp|qEp(>Iz) zjlgW+?h~8?L5wres$y8YpqCY+Y{W{%O0k&B$M=>jebm#n7$u5PE7wqkd|9`$YOP$M z1UVb0MY$te+k`q@qnLEqlMRIN(V`M7#kGo9$mwdWs+0}3DC*)KvmNndV`6MPYV(p@ zFog|DisuXY4VI@&q2^s|up==rt2+c>Q)<|Qjpdklw80oOm!k}$!cq!dF7lO6CRqp3JO7O!dinz*Ji^{g!`eBE4Q zt2>6tG85E}VK$pmq$iok(#)9HU^X{+wW2cRH6~Y~OlF4PT?5XBmwsWC8c~(J=v+b+x{LQ(287^Y{M#OS0w%X z9wP@@k%?mS26#eN+g5G&Jw<}Q6}hTr*;TC}nMz`hn)_Rkv*3>V)v9i*W_oXtpWElh z&O$;nTRZk=49i;GTeR5k$Iig$=6_gZOi5AA?A}7-K0kI6hF(^$XsZPY{2R&M0>wTs z);}>D4!H&_wjFd0SS&l>8nDKy$mQG62o(Qi&g$+x3tA ohkR%8_M*l<(su^;R4V#IzLWS?`N7u|+o~z_g?y(1+jWBf0E?=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.0.tgz", + "integrity": "sha512-wqh0ByljabXLKHeWXYLqoJ5jKC4XBaw6Hk08OfMrCRd2nP2ZQ5eleDZC41XHyCNgktBGYMbqnrJKq/K/lzPMSQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.0.tgz", + "integrity": "sha512-+WzIXQOSaGs33tLEgYPYe/yQHf0WTU0X42Jca3y8NWMbUVhp7rUnw+vAsRC/QiDrdD31IszMrZy+qwPOPjd+rw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.0.tgz", + "integrity": "sha512-+VJggoaKhk2VNNqVL7f6S189UzShHC/mR9EE8rDdSkdpN0KflSwWY/gWjDrNxxisg8Fp1ZCD9jLMo4m0OUfeUA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.0.tgz", + "integrity": "sha512-0T+A9WZm+bZ84nZBtk1ckYsOvyA3x7e2Acj1KdVfV4/2tdG4fzUp91YHx+GArWLtwqp77pBXVCPn2We7Letr0Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.0.tgz", + "integrity": "sha512-fyzLm/DLDl/84OCfp2f/XQ4flmORsjU7VKt8HLjvIXChJoFFOIL6pLJPH4Yhd1n1gGFF9mPwtlN5Wf82DZs+LQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.0.tgz", + "integrity": "sha512-l9GeW5UZBT9k9brBYI+0WDffcRxgHQD8ShN2Ur4xWq/NFzUKm3k5lsH4PdaRgb2w7mI9u61nr2gI2mLI27Nh3Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.0.tgz", + "integrity": "sha512-BXoQai/A0wPO6Es3yFJ7APCiKGc1tdAEOgeTNy3SsB491S3aHn4S4r3e976eUnPdU+NbdtmBuLncYir2tMU9Nw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.0.tgz", + "integrity": "sha512-CjaaREJagqJp7iTaNQjjidaNbCKYcd4IDkzbwwxtSvjI7NZm79qiHc8HqciMddQ6CKvJT6aBd8lO9kN/ZudLlw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.0.tgz", + "integrity": "sha512-RVyzfb3FWsGA55n6WY0MEIEPURL1FcbhFE6BffZEMEekfCzCIMtB5yyDcFnVbTnwk+CLAgTujmV/Lgvih56W+A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.0.tgz", + "integrity": "sha512-KBnSTt1kxl9x70q+ydterVdl+Cn0H18ngRMRCEQfrbqdUuntQQ0LoMZv47uB97NljZFzY6HcfqEZ2SAyIUTQBQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.0.tgz", + "integrity": "sha512-zpSlUce1mnxzgBADvxKXX5sl8aYQHo2ezvMNI8I0lbblJtp8V4odlm3Yzlj7gPyt3T8ReksE6bK+pT3WD+aJRg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.0.tgz", + "integrity": "sha512-2jIfP6mmjkdmeTlsX/9vmdmhBmKADrWqN7zcdtHIeNSCH1SqIoNI63cYsjQR8J+wGa4Y5izRcSHSm8K3QWmk3w==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.0.tgz", + "integrity": "sha512-bc0FE9wWeC0WBm49IQMPSPILRocGTQt3j5KPCA8os6VprfuJ7KD+5PzESSrJ6GmPIPJK965ZJHTUlSA6GNYEhg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.0.tgz", + "integrity": "sha512-SQPZOwoTTT/HXFXQJG/vBX8sOFagGqvZyXcgLA3NhIqcBv1BJU1d46c0rGcrij2B56Z2rNiSLaZOYW5cUk7yLQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.0.tgz", + "integrity": "sha512-SCfR0HN8CEEjnYnySJTd2cw0k9OHB/YFzt5zgJEwa+wL/T/raGWYMBqwDNAC6dqFKmJYZoQBRfHjgwLHGSrn3Q==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.0.tgz", + "integrity": "sha512-us0dSb9iFxIi8srnpl931Nvs65it/Jd2a2K3qs7fz2WfGPHqzfzZTfec7oxZJRNPXPnNYZtanmRc4AL/JwVzHQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.0.tgz", + "integrity": "sha512-CR/RYotgtCKwtftMwJlUU7xCVNg3lMYZ0RzTmAHSfLCXw3NtZtNpswLEj/Kkf6kEL3Gw+BpOekRX0BYCtklhUw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.0.tgz", + "integrity": "sha512-nU1yhmYutL+fQ71Kxnhg8uEOdC0pwEW9entHykTgEbna2pw2dkbFSMeqjjyHZoCmt8SBkOSvV+yNmm94aUrrqw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.0.tgz", + "integrity": "sha512-cXb5vApOsRsxsEl4mcZ1XY3D4DzcoMxR/nnc4IyqYs0rTI8ZKmW6kyyg+11Z8yvgMfAEldKzP7AdP64HnSC/6g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.0.tgz", + "integrity": "sha512-8wZM2qqtv9UP3mzy7HiGYNH/zjTA355mpeuA+859TyR+e+Tc08IHYpLJuMsfpDJwoLo1ikIJI8jC3GFjnRClzA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.0.tgz", + "integrity": "sha512-FLGfyizszcef5C3YtoyQDACyg95+dndv79i2EekILBofh5wpCa1KuBqOWKrEHZg3zrL3t5ouE5jgr94vA+Wb2w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.0.tgz", + "integrity": "sha512-1ZgjUoEdHZZl/YlV76TSCz9Hqj9h9YmMGAgAPYd+q4SicWNX3G5GCyx9uhQWSLcbvPW8Ni7lj4gDa1T40akdlw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.0.tgz", + "integrity": "sha512-Q9StnDmQ/enxnpxCCLSg0oo4+34B9TdXpuyPeTedN/6+iXBJ4J+zwfQI28u/Jl40nOYAxGoNi7mFP40RUtkmUA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.0.tgz", + "integrity": "sha512-zF3ag/gfiCe6U2iczcRzSYJKH1DCI+ByzSENHlM2FcDbEeo5Zd2C86Aq0tKUYAJJ1obRP84ymxIAksZUcdztHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.0.tgz", + "integrity": "sha512-pEl1bO9mfAmIC+tW5btTmrKaujg3zGtUmWNdCw/xs70FBjwAL3o9OEKNHvNmnyylD6ubxUERiEhdsL0xBQ9efw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@types/better-sqlite3": { + "version": "7.6.13", + "resolved": "https://registry.npmjs.org/@types/better-sqlite3/-/better-sqlite3-7.6.13.tgz", + "integrity": "sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/body-parser": { + "version": "1.19.6", + "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", + "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/connect": "*", + "@types/node": "*" + } + }, + "node_modules/@types/connect": { + "version": "3.4.38", + "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", + "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/cors": { + "version": "2.8.19", + "resolved": "https://registry.npmjs.org/@types/cors/-/cors-2.8.19.tgz", + "integrity": "sha512-mFNylyeyqN93lfe/9CSxOGREz8cpzAhH+E93xJ4xWQf62V8sQ/24reV2nyzUWM6H6Xji+GGHpkbLe7pVoUEskg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/express": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", + "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/body-parser": "*", + "@types/express-serve-static-core": "^5.0.0", + "@types/serve-static": "^2" + } + }, + "node_modules/@types/express-serve-static-core": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", + "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/qs": "*", + "@types/range-parser": "*", + "@types/send": "*" + } + }, + "node_modules/@types/http-errors": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", + "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "25.8.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.8.0.tgz", + "integrity": "sha512-TCFSk8IZh+iLX1xtksoBVtdmgL+1IX0fC9BeU4QqFSuNdN/K+HUlhqOzEmSYYpZUVsLYcPqc9KX+60iDuninSQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": ">=7.24.0 <7.24.7" + } + }, + "node_modules/@types/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-GZHUBZR9hckSUhrxmp1nG6NwdpM9fCunJwyThLW1X3AyHgd9IlHb6VANpQQqDr2o/qQp6McZ3y/IA2rVzKzSbw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/range-parser": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", + "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", + "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/serve-static": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", + "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-errors": "*", + "@types/node": "*" + } + }, + "node_modules/@types/ws": { + "version": "8.18.1", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", + "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "license": "MIT", + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/better-sqlite3": { + "version": "12.10.0", + "resolved": "https://registry.npmjs.org/better-sqlite3/-/better-sqlite3-12.10.0.tgz", + "integrity": "sha512-CyzaZRQKyHkB2ZInfTTl2nvT33EbDpjkLEbE8/Zck3Ll6O0qqvuGdrJ45HgtH+HykRg88ITY3AdreBGN70aBSQ==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "bindings": "^1.5.0", + "prebuild-install": "^7.1.1" + }, + "engines": { + "node": "20.x || 22.x || 23.x || 24.x || 25.x || 26.x" + } + }, + "node_modules/bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "license": "MIT", + "dependencies": { + "file-uri-to-path": "1.0.0" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "license": "MIT", + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" + }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "license": "MIT", + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "license": "MIT", + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/esbuild": { + "version": "0.28.0", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.0.tgz", + "integrity": "sha512-sNR9MHpXSUV/XB4zmsFKN+QgVG82Cc7+/aaxJ8Adi8hyOac+EXptIp45QBPaVyX3N70664wRbTcLTOemCAnyqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.28.0", + "@esbuild/android-arm": "0.28.0", + "@esbuild/android-arm64": "0.28.0", + "@esbuild/android-x64": "0.28.0", + "@esbuild/darwin-arm64": "0.28.0", + "@esbuild/darwin-x64": "0.28.0", + "@esbuild/freebsd-arm64": "0.28.0", + "@esbuild/freebsd-x64": "0.28.0", + "@esbuild/linux-arm": "0.28.0", + "@esbuild/linux-arm64": "0.28.0", + "@esbuild/linux-ia32": "0.28.0", + "@esbuild/linux-loong64": "0.28.0", + "@esbuild/linux-mips64el": "0.28.0", + "@esbuild/linux-ppc64": "0.28.0", + "@esbuild/linux-riscv64": "0.28.0", + "@esbuild/linux-s390x": "0.28.0", + "@esbuild/linux-x64": "0.28.0", + "@esbuild/netbsd-arm64": "0.28.0", + "@esbuild/netbsd-x64": "0.28.0", + "@esbuild/openbsd-arm64": "0.28.0", + "@esbuild/openbsd-x64": "0.28.0", + "@esbuild/openharmony-arm64": "0.28.0", + "@esbuild/sunos-x64": "0.28.0", + "@esbuild/win32-arm64": "0.28.0", + "@esbuild/win32-ia32": "0.28.0", + "@esbuild/win32-x64": "0.28.0" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", + "engines": { + "node": ">=6" + } + }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "license": "MIT", + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==", + "license": "MIT" + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/node-abi": { + "version": "3.92.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.92.0.tgz", + "integrity": "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==", + "license": "MIT", + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/qs": { + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", + "license": "BSD-3-Clause", + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "license": "MIT", + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tsx": { + "version": "4.22.1", + "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.22.1.tgz", + "integrity": "sha512-TvncJykhxAzFCk0VQZKBTClall4Pm7qXDSodb6uxi8QFa8X8mT6ABjxxsQ2opDRYxG7AzcRWXaFtruz5HJKuWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "~0.28.0" + }, + "bin": { + "tsx": "dist/cli.mjs" + }, + "engines": { + "node": ">=18.0.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-is": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.1.0.tgz", + "integrity": "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA==", + "license": "MIT", + "dependencies": { + "content-type": "^2.0.0", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/type-is/node_modules/content-type": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.0.0.tgz", + "integrity": "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "7.24.6", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.24.6.tgz", + "integrity": "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg==", + "dev": true, + "license": "MIT" + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" + }, + "node_modules/ws": { + "version": "8.20.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.1.tgz", + "integrity": "sha512-It4dO0K5v//JtTXuPkfEOaI3uUN87iYPnqo/ZzqCoG3g8uhA66QUMs/SrM0YK7/NAu+r4LMh/9dq2A7k+rHs+w==", + "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 + } + } + } + } +} diff --git a/backend/package.json b/backend/package.json new file mode 100644 index 0000000000..ef1a88d24f --- /dev/null +++ b/backend/package.json @@ -0,0 +1,28 @@ +{ + "name": "backend", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "dev": "tsx src/index.ts", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "dependencies": { + "better-sqlite3": "^12.10.0", + "cors": "^2.8.6", + "express": "^5.2.1", + "ws": "^8.20.1" + }, + "devDependencies": { + "@types/better-sqlite3": "^7.6.13", + "@types/cors": "^2.8.19", + "@types/express": "^5.0.6", + "@types/node": "^25.8.0", + "@types/ws": "^8.18.1", + "tsx": "^4.22.1", + "typescript": "^6.0.3" + } +} diff --git a/backend/src/db.ts b/backend/src/db.ts new file mode 100644 index 0000000000..994ab6d287 --- /dev/null +++ b/backend/src/db.ts @@ -0,0 +1,57 @@ +import Database from 'better-sqlite3'; +import path from 'path'; +import fs from 'fs'; + +const dbPath = path.join(__dirname, '../database.sqlite'); +const db = new Database(dbPath); + +// Initialize tables +db.exec(` + CREATE TABLE IF NOT EXISTS coins ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + currency TEXT NOT NULL, + blockchain TEXT NOT NULL, + price REAL NOT NULL, + date TEXT NOT NULL, + iconFilename TEXT NOT NULL, + createdAt DATETIME DEFAULT CURRENT_TIMESTAMP, + updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP + ); + + CREATE TABLE IF NOT EXISTS scores ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + username TEXT NOT NULL UNIQUE, + score INTEGER NOT NULL DEFAULT 0, + sessionToken TEXT, + createdAt DATETIME DEFAULT CURRENT_TIMESTAMP, + updatedAt DATETIME DEFAULT CURRENT_TIMESTAMP + ); +`); + +// Seed data if empty +const coinCount = db.prepare('SELECT COUNT(*) as count FROM coins').get() as { count: number }; +if (coinCount.count === 0) { + const seedPath = path.join(__dirname, '../data/seed.json'); + if (fs.existsSync(seedPath)) { + const seedData = JSON.parse(fs.readFileSync(seedPath, 'utf-8')); + + const insertCoin = db.prepare('INSERT INTO coins (currency, blockchain, price, date, iconFilename) VALUES (@currency, @blockchain, @price, @date, @iconFilename)'); + const insertCoins = db.transaction((coins: any[]) => { + for (const coin of coins) { + insertCoin.run(coin); + } + }); + insertCoins(seedData.coins); + + const insertScore = db.prepare('INSERT INTO scores (username, score) VALUES (@username, @score)'); + const insertScores = db.transaction((scores: any[]) => { + for (const score of scores) { + insertScore.run(score); + } + }); + insertScores(seedData.scores); + console.log("Database seeded successfully."); + } +} + +export default db; diff --git a/backend/src/index.ts b/backend/src/index.ts new file mode 100644 index 0000000000..8dd09b2fc9 --- /dev/null +++ b/backend/src/index.ts @@ -0,0 +1,27 @@ +import express from 'express'; +import cors from 'cors'; +import http from 'http'; +import { setupWebSocket } from './websocket/scoreboard'; + +import coinRoutes from './routes/coins'; +import exchangeRateRoutes from './routes/exchangeRate'; +import swapRoutes from './routes/swap'; +import scoreRoutes from './routes/scores'; + +const app = express(); +const server = http.createServer(app); + +app.use(cors()); +app.use(express.json()); + +app.use('/api/coins', coinRoutes); +app.use('/api/exchange-rate', exchangeRateRoutes); +app.use('/api/swap', swapRoutes); +app.use('/api/scores', scoreRoutes); + +setupWebSocket(server); + +const PORT = process.env.PORT || 3001; +server.listen(PORT, () => { + console.log(`Server is running on port ${PORT}`); +}); diff --git a/backend/src/middleware/auth.ts b/backend/src/middleware/auth.ts new file mode 100644 index 0000000000..d9586d5fb5 --- /dev/null +++ b/backend/src/middleware/auth.ts @@ -0,0 +1,24 @@ +import { Request, Response, NextFunction } from 'express'; +import db from '../db'; + +export const requireAuth = (req: Request, res: Response, next: NextFunction) => { + const authHeader = req.headers.authorization; + if (!authHeader || !authHeader.startsWith('Bearer ')) { + return res.status(401).json({ error: 'Unauthorized: missing token' }); + } + + const token = authHeader.split(' ')[1]; + + try { + const user = db.prepare('SELECT id, username, score FROM scores WHERE sessionToken = ?').get(token); + if (!user) { + return res.status(401).json({ error: 'Unauthorized: invalid token' }); + } + + // Attach user info to request + (req as any).user = user; + next(); + } catch (e: any) { + res.status(500).json({ error: e.message }); + } +}; diff --git a/backend/src/routes/coins.ts b/backend/src/routes/coins.ts new file mode 100644 index 0000000000..c73791ff10 --- /dev/null +++ b/backend/src/routes/coins.ts @@ -0,0 +1,91 @@ +import { Router } from 'express'; +import db from '../db'; + +const router = Router(); + +// Get all coins +router.get('/', (req, res) => { + const { blockchain, search, minPrice, maxPrice } = req.query; + let query = 'SELECT * FROM coins WHERE 1=1'; + const params: any[] = []; + + if (blockchain) { + query += ' AND blockchain = ?'; + params.push(blockchain); + } + if (search) { + query += ' AND (currency LIKE ? OR blockchain LIKE ?)'; + params.push(`%${search}%`, `%${search}%`); + } + if (minPrice) { + query += ' AND price >= ?'; + params.push(minPrice); + } + if (maxPrice) { + query += ' AND price <= ?'; + params.push(maxPrice); + } + + try { + const coins = db.prepare(query).all(params); + res.json(coins); + } catch (e: any) { + res.status(500).json({ error: e.message }); + } +}); + +// Get single coin +router.get('/:id', (req, res) => { + try { + const coin = db.prepare('SELECT * FROM coins WHERE id = ?').get(req.params.id); + if (coin) { + res.json(coin); + } else { + res.status(404).json({ error: 'Coin not found' }); + } + } catch (e: any) { + res.status(500).json({ error: e.message }); + } +}); + +// Create coin +router.post('/', (req, res) => { + const { currency, blockchain, price, date, iconFilename } = req.body; + try { + const result = db.prepare('INSERT INTO coins (currency, blockchain, price, date, iconFilename) VALUES (?, ?, ?, ?, ?)').run(currency, blockchain, price, date, iconFilename); + res.status(201).json({ id: result.lastInsertRowid, ...req.body }); + } catch (e: any) { + res.status(500).json({ error: e.message }); + } +}); + +// Update coin +router.put('/:id', (req, res) => { + const { currency, blockchain, price, date, iconFilename } = req.body; + try { + const result = db.prepare('UPDATE coins SET currency = ?, blockchain = ?, price = ?, date = ?, iconFilename = ?, updatedAt = CURRENT_TIMESTAMP WHERE id = ?').run(currency, blockchain, price, date, iconFilename, req.params.id); + if (result.changes > 0) { + res.json({ id: req.params.id, ...req.body }); + } else { + res.status(404).json({ error: 'Coin not found' }); + } + } catch (e: any) { + res.status(500).json({ error: e.message }); + } +}); + +// Delete coin +router.delete('/:id', (req, res) => { + try { + const result = db.prepare('DELETE FROM coins WHERE id = ?').run(req.params.id); + if (result.changes > 0) { + res.status(204).send(); + } else { + res.status(404).json({ error: 'Coin not found' }); + } + } catch (e: any) { + res.status(500).json({ error: e.message }); + } +}); + +export default router; diff --git a/backend/src/routes/exchangeRate.ts b/backend/src/routes/exchangeRate.ts new file mode 100644 index 0000000000..6f8578bec0 --- /dev/null +++ b/backend/src/routes/exchangeRate.ts @@ -0,0 +1,31 @@ +import { Router } from 'express'; +import db from '../db'; + +const router = Router(); + +router.get('/', (req, res) => { + const { from, to } = req.query; + if (!from || !to) { + return res.status(400).json({ error: 'Missing from or to currency' }); + } + + try { + const fromCoin = db.prepare('SELECT price FROM coins WHERE currency = ?').get(from) as { price: number }; + const toCoin = db.prepare('SELECT price FROM coins WHERE currency = ?').get(to) as { price: number }; + + if (!fromCoin || !toCoin) { + return res.status(404).json({ error: 'Currency not found' }); + } + + const rate = fromCoin.price / toCoin.price; + res.json({ + rate, + fromPrice: fromCoin.price, + toPrice: toCoin.price + }); + } catch (e: any) { + res.status(500).json({ error: e.message }); + } +}); + +export default router; diff --git a/backend/src/routes/scores.ts b/backend/src/routes/scores.ts new file mode 100644 index 0000000000..6fa502ae04 --- /dev/null +++ b/backend/src/routes/scores.ts @@ -0,0 +1,54 @@ +import { Router } from 'express'; +import db from '../db'; +import { requireAuth } from '../middleware/auth'; +import { broadcastScores } from '../websocket/scoreboard'; + +const router = Router(); + +// Get top 10 scores +router.get('/', (req, res) => { + try { + const scores = db.prepare('SELECT username, score FROM scores ORDER BY score DESC LIMIT 10').all(); + res.json(scores); + } catch (e: any) { + res.status(500).json({ error: e.message }); + } +}); + +// Get current authenticated user's score +router.get('/me', requireAuth, (req, res) => { + const user = (req as any).user; + res.json({ username: user.username, score: user.score }); +}); + + +// Increment score manually (Problem 6 specifically asked for action completion) +router.post('/action', requireAuth, (req, res) => { + const user = (req as any).user; + try { + db.prepare('UPDATE scores SET score = score + 1 WHERE id = ?').run(user.id); + broadcastScores(); + res.json({ success: true, message: 'Score incremented' }); + } catch (e: any) { + res.status(500).json({ error: e.message }); + } +}); + +// Create a session for a new user +router.post('/session', (req, res) => { + try { + // Generate random username and token + const username = 'User' + Math.floor(Math.random() * 10000); + const token = Math.random().toString(36).substring(2) + Math.random().toString(36).substring(2); + const score = 40; + // As user requested, start with 30 points + db.prepare('INSERT INTO scores (username, sessionToken, score) VALUES (?, ?, ?)').run(username, token,score); + + res.json({ username, token ,score}); // score is server-side only + broadcastScores(); + } catch (e: any) { + res.status(500).json({ error: e.message }); + } +}); + +export default router; diff --git a/backend/src/routes/swap.ts b/backend/src/routes/swap.ts new file mode 100644 index 0000000000..c1387ebaff --- /dev/null +++ b/backend/src/routes/swap.ts @@ -0,0 +1,41 @@ +import { Router } from 'express'; +import db from '../db'; +import { broadcastScores } from '../websocket/scoreboard'; + +const router = Router(); + +router.post('/', (req, res) => { + const { fromCoin, toCoin, amount, sessionToken } = req.body; + + if (!fromCoin || !toCoin || !amount) { + return res.status(400).json({ error: 'Missing required fields' }); + } + + // Delay to simulate network / transaction + setTimeout(() => { + try { + let newScore: number | null = null; + + // Increment score if a valid session token is provided + if (sessionToken) { + const result = db.prepare('UPDATE scores SET score = score + 1 WHERE sessionToken = ?').run(sessionToken); + if (result.changes > 0) { + const row = db.prepare('SELECT score FROM scores WHERE sessionToken = ?').get(sessionToken) as { score: number } | undefined; + newScore = row?.score ?? null; + broadcastScores(); + } + } + + res.json({ + success: true, + message: `Swapped ${amount} ${fromCoin} to ${toCoin}`, + transactionId: Math.random().toString(36).substring(7), + newScore // always return the fresh score so the frontend doesn't need localStorage + }); + } catch (e: any) { + res.status(500).json({ error: e.message }); + } + }, 1500); // 1.5s delay +}); + +export default router; diff --git a/backend/src/websocket/scoreboard.ts b/backend/src/websocket/scoreboard.ts new file mode 100644 index 0000000000..afd8a72c76 --- /dev/null +++ b/backend/src/websocket/scoreboard.ts @@ -0,0 +1,48 @@ +import { Server as WebSocketServer, WebSocket } from 'ws'; +import { Server as HttpServer } from 'http'; +import db from '../db'; + +let wss: WebSocketServer; + +export const setupWebSocket = (server: HttpServer) => { + wss = new WebSocketServer({ server }); + + wss.on('connection', (ws: WebSocket) => { + console.log('Client connected to WebSocket'); + + // Send initial scores + sendScoresToClient(ws); + + ws.on('close', () => { + console.log('Client disconnected'); + }); + }); +}; + +const getTopScores = () => { + return db.prepare('SELECT username, score FROM scores ORDER BY score DESC LIMIT 10').all(); +}; + +const sendScoresToClient = (ws: WebSocket) => { + if (ws.readyState === WebSocket.OPEN) { + ws.send(JSON.stringify({ + type: 'SCORE_UPDATE', + data: getTopScores() + })); + } +}; + +export const broadcastScores = () => { + if (!wss) return; + const scores = getTopScores(); + const payload = JSON.stringify({ + type: 'SCORE_UPDATE', + data: scores + }); + + wss.clients.forEach((client) => { + if (client.readyState === WebSocket.OPEN) { + client.send(payload); + } + }); +}; diff --git a/backend/tsconfig.json b/backend/tsconfig.json new file mode 100644 index 0000000000..c84c684433 --- /dev/null +++ b/backend/tsconfig.json @@ -0,0 +1,13 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "CommonJS", + "rootDir": "./src", + "moduleResolution": "node", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true + }, + "include": ["src/**/*"] +} diff --git a/docs/problem1and4/detail.md b/docs/problem1and4/detail.md new file mode 100644 index 0000000000..a763889880 --- /dev/null +++ b/docs/problem1and4/detail.md @@ -0,0 +1,45 @@ +# Task + +Provide 3 unique implementations of the following function in TypeScript. + +- Comment on the complexity or efficiency of each function. + +**Input**: `n` - any integer + +_Assuming this input will always produce a result lesser than `Number.MAX_SAFE_INTEGER`_. + +**Output**: `return` - summation to `n`, i.e. `sum_to_n(5) === 1 + 2 + 3 + 4 + 5 === 15`. + +```go +/** + * Implementation A: Mathematical formula (Gauss) + * Time Complexity: O(1) - Constant time arithmetic operations + * Space Complexity: O(1) - Constant space + */ +func sum_to_n_a(n: number): number { + return (n * (n + 1)) / 2; + +} + +/** + * Implementation B: Iterative approach + * Time Complexity: O(n) - Loops n times + * Space Complexity: O(1) - Constant space for sum variable + */ +func sum_to_n_b(n: number): number { + let sum = 0; + for (let i = 1; i <= n; i++) { + sum += i; + } + return sum; +} +/** + * Implementation C: Recursive approach + * Time Complexity: O(n) - n recursive calls + * Space Complexity: O(n) - Call stack grows to n + */ +func sum_to_n_c(n: number): number { + if (n <= 1) return n; + return n + sum_to_n_c(n - 1); +} +``` diff --git a/docs/problem1and4/readme.md b/docs/problem1and4/readme.md new file mode 100644 index 0000000000..4cc1ae67e7 --- /dev/null +++ b/docs/problem1and4/readme.md @@ -0,0 +1,38 @@ +For the problem we have 3 solutions: +First is using an algorithm math formula to calculate which is simple and fast O(1) time complexity and O(1) space complexity. +Second is using an iterative approach which is simple and fast O(n) time complexity and O(1) space complexity. +Third is using an recursive approach which is simple and fast O(n) time complexity and O(n) space complexity. + +```go +/** + * Implementation A: Mathematical formula (Gauss) + * Time Complexity: O(1) - Constant time arithmetic operations + * Space Complexity: O(1) - Constant space + */ +func sum_to_n_a(n: number): number { + return (n * (n + 1)) / 2; + +} + +/** + * Implementation B: Iterative approach + * Time Complexity: O(n) - Loops n times + * Space Complexity: O(1) - Constant space for sum variable + */ +func sum_to_n_b(n: number): number { + let sum = 0; + for (let i = 1; i <= n; i++) { + sum += i; + } + return sum; +} +/** + * Implementation C: Recursive approach + * Time Complexity: O(n) - n recursive calls + * Space Complexity: O(n) - Call stack grows to n + */ +func sum_to_n_c(n: number): number { + if (n <= 1) return n; + return n + sum_to_n_c(n - 1); +} +``` diff --git a/docs/problem2/detail.md b/docs/problem2/detail.md new file mode 100644 index 0000000000..59e1375ac1 --- /dev/null +++ b/docs/problem2/detail.md @@ -0,0 +1,23 @@ +# Task + +Create a currency swap form based on the template provided in the folder. A user would use this form to swap assets from one currency to another. + +_You may use any third party plugin, library, and/or framework for this problem._ + +1. You may add input validation/error messages to make the form interactive. +2. Your submission will be rated on its usage intuitiveness and visual attractiveness. +3. Show us your frontend development and design skills, feel free to totally disregard the provided files for this problem. +4. You may use this [repo](https://github.com/Switcheo/token-icons/tree/main/tokens) for token images, e.g. [SVG image](https://raw.githubusercontent.com/Switcheo/token-icons/main/tokens/SWTH.svg). +5. You may use this [URL](https://interview.switcheo.com/prices.json) for token price information and to compute exchange rates (not every token has a price, those that do not can be omitted). + + + +Please submit your solution using the files provided in the skeletal repo, including any additional files your solution may use. + + diff --git a/docs/problem2/image-1.png b/docs/problem2/image-1.png new file mode 100644 index 0000000000000000000000000000000000000000..1ab007482bbd51242056a18864c708ff20240605 GIT binary patch literal 54322 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

}tjOPgwX2Ae29av5#u|V5 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.??#w`!uYHtRM{6OieT3m)bDweJExWnyH3hI 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 KKbLh*2~7a>cGm6y literal 0 HcmV?d00001 diff --git a/docs/problem2/readme.md b/docs/problem2/readme.md new file mode 100644 index 0000000000..633e423ad8 --- /dev/null +++ b/docs/problem2/readme.md @@ -0,0 +1,20 @@ +For this problem i have create a new react project with vite and tailwind css and create a form for currency swap. +The form get the current exchange rate from the backend and display the estimated amount to be received. +When subitmitting the form, it will call the swap api from backend and display the result. + +My solution uses the following features: + +- The form have 2 UI modes. +- React Hooks: useState, useEffect, useRef for state management and event handling. +- Tailwind CSS: For styling and responsive design. +- Axios: For API calls. +- Loading indicator: For submit button. +- Error messages: For input validation and error handling. + +For the seed data, i used the token icons from [Switcheo token-icons repository](https://github.com/Switcheo/token-icons/tree/main/tokens) +And the mock data for exchange rate from [interview.switcheo.com](https://interview.switcheo.com/prices.json) +In both i generated seed data +Here is some result +The component is in [SwapPage.tsx](../../frontend/src/pages/SwapPage.tsx) +![alt text](image.png) +![alt text](image-1.png) diff --git a/docs/problem3/detail.md b/docs/problem3/detail.md new file mode 100644 index 0000000000..8fad889f14 --- /dev/null +++ b/docs/problem3/detail.md @@ -0,0 +1,93 @@ +# Task + +List out the computational inefficiencies and anti-patterns found in the code block below. + +1. This code block uses + 1. ReactJS with TypeScript. + 2. Functional components. + 3. React Hooks +2. You should also provide a refactored version of the code, but more points are awarded to accurately stating the issues and explaining correctly how to improve them. + +```typescript +interface WalletBalance { + currency: string; + amount: number; +} +interface FormattedWalletBalance { + currency: string; + amount: number; + formatted: string; +} + +interface Props extends BoxProps { + +} +const WalletPage: React.FC = (props: Props) => { + const { children, ...rest } = props; + const balances = useWalletBalances(); + const prices = usePrices(); + + const getPriority = (blockchain: any): number => { + switch (blockchain) { + case 'Osmosis': + return 100 + case 'Ethereum': + return 50 + case 'Arbitrum': + return 30 + case 'Zilliqa': + return 20 + case 'Neo': + return 20 + default: + return -99 + } + } + + const sortedBalances = useMemo(() => { + return balances.filter((balance: WalletBalance) => { + const balancePriority = getPriority(balance.blockchain); + if (lhsPriority > -99) { + if (balance.amount <= 0) { + return true; + } + } + return false + }).sort((lhs: WalletBalance, rhs: WalletBalance) => { + const leftPriority = getPriority(lhs.blockchain); + const rightPriority = getPriority(rhs.blockchain); + if (leftPriority > rightPriority) { + return -1; + } else if (rightPriority > leftPriority) { + return 1; + } + }); + }, [balances, prices]); + + const formattedBalances = sortedBalances.map((balance: WalletBalance) => { + return { + ...balance, + formatted: balance.amount.toFixed() + } + }) + + const rows = sortedBalances.map((balance: FormattedWalletBalance, index: number) => { + const usdValue = prices[balance.currency] * balance.amount; + return ( + + ) + }) + + return ( +

+ {rows} +
+ ) +} +``` diff --git a/docs/problem3/readme.md b/docs/problem3/readme.md new file mode 100644 index 0000000000..1575c7b5bd --- /dev/null +++ b/docs/problem3/readme.md @@ -0,0 +1,215 @@ +I have a table generated to document the issues in the original code and compare them with the refactored code. +Here is the updated component that fixed all the issues: +[WalletPage.tsx](../../frontend/src/pages/WalletPage.tsx) + +## Code Analysis: Before & After + +| Issue | Original Code (Before) | Refactored Code (After) | Improvement | +| :------------------------ | :--------------------------------------------------------------------------------------------------------------------------------- | :------------------------------------------------------------------------------------------------------------------------ | :-------------------------------------------------------------------------------------------- | +| **Undefined Variables** | Used `lhsPriority` and `leftPriority` which were never declared, causing runtime crashes. | Properly scoped variables `priorityA` and `priorityB` extracted directly from the items being compared. | **Bug Fix**: Code now executes without throwing `ReferenceError`. | +| **Inverted Filter Logic** | `if (balance.amount <= 0) return true;` kept empty/negative balances and discarded positive ones. | `return priority > -99 && balance.amount > 0;` correctly filters out empty balances and unsupported chains. | **Bug Fix**: Displays correct data to the user. | +| **Missing Sort Return** | The sort function did not return `0` for items with equal priority, leading to unstable sorting. | Added a fallback `return 0;` to ensure strict sorting contract compliance. | **Bug Fix**: Stable and predictable array sorting across all browsers. | +| **Redundant Dependency** | `prices` was included in the `useMemo` dependency array but never actually used inside the block. | Removed unused dependencies and integrated pricing calculation cleanly in one pass. | **Performance**: Prevents unnecessary re-computations when prices update but balances do not. | +| **Inefficient Lookups** | `getPriority` used an O(N) `switch` statement that was re-evaluated constantly. | Replaced with a static O(1) `HashMap` (`Record`) defined outside the component. | **Performance**: Faster lookups and prevents recreating the function on every render. | +| **Multiple Array Passes** | Mapped over the sorted array to create `formattedBalances`, then mapped _again_ to create React rows, dropping the formatted data. | Consolidated `.filter`, `.sort`, and `.map` into a single `useMemo` pipeline that handles formatting and USD calculation. | **Performance**: Reduced memory allocation and fewer iterations over the array. | +| **Index as React Key** | Used `key={index}` when rendering ``, which causes UI bugs during reordering/filtering. | Used `key={balance.currency}` to ensure stable DOM elements during state changes. | **Performance & UX**: Optimized React reconciliation and prevents component state leakage. | + +## Comparison + +# Before + +```typescript +interface WalletBalance { + currency: string; + amount: number; +} +interface FormattedWalletBalance { + currency: string; + amount: number; + formatted: string; +} + +interface Props extends BoxProps { + +} +const WalletPage: React.FC = (props: Props) => { + const { children, ...rest } = props; + const balances = useWalletBalances(); + const prices = usePrices(); + + const getPriority = (blockchain: any): number => { + switch (blockchain) { + case 'Osmosis': + return 100 + case 'Ethereum': + return 50 + case 'Arbitrum': + return 30 + case 'Zilliqa': + return 20 + case 'Neo': + return 20 + default: + return -99 + } + } + + const sortedBalances = useMemo(() => { + return balances.filter((balance: WalletBalance) => { + const balancePriority = getPriority(balance.blockchain); + if (lhsPriority > -99) { + if (balance.amount <= 0) { + return true; + } + } + return false + }).sort((lhs: WalletBalance, rhs: WalletBalance) => { + const leftPriority = getPriority(lhs.blockchain); + const rightPriority = getPriority(rhs.blockchain); + if (leftPriority > rightPriority) { + return -1; + } else if (rightPriority > leftPriority) { + return 1; + } + }); + }, [balances, prices]); + + const formattedBalances = sortedBalances.map((balance: WalletBalance) => { + return { + ...balance, + formatted: balance.amount.toFixed() + } + }) + + const rows = sortedBalances.map((balance: FormattedWalletBalance, index: number) => { + const usdValue = prices[balance.currency] * balance.amount; + return ( + + ) + }) + + return ( +
+ {rows} +
+ ) +} +``` + +# After + +```typescript +// Problem 3 Fix: Define BLOCKCHAIN_PRIORITY as a HashMap for O(1) lookup instead of O(n) switch +const BLOCKCHAIN_PRIORITY: Record = { + 'Osmosis': 100, + 'Ethereum': 50, + 'Arbitrum': 30, + 'Zilliqa': 20, + 'Neo': 20 +}; + +// Mock balances since we don't have a real wallet connection +const generateMockBalances = (coins: Coin[]) => { + return coins.map((c, i) => ({ + currency: c.currency, + blockchain: c.blockchain, + amount: i % 3 === 0 ? 0 : Math.random() * 100 + 10 // Every 3rd coin is empty + })); +}; + +export const WalletPage: FC = ({ coins }) => { + const [showComparison, setShowComparison] = useState(false); + + // Create mock balances only once + const balances = useMemo(() => generateMockBalances(coins), [coins]); + + // Problem 3 Fix: Single pass filter, sort, and map using useMemo + const processedBalances = useMemo(() => { + return balances + // 1. Filter: Fix inverted logic (amount > 0) + .filter(balance => { + const priority = BLOCKCHAIN_PRIORITY[balance.blockchain] ?? -99; + return priority > -99 && balance.amount > 0; + }) + // 2. Sort: Fix missing return 0 + .sort((a, b) => { + const priorityA = BLOCKCHAIN_PRIORITY[a.blockchain] ?? -99; + const priorityB = BLOCKCHAIN_PRIORITY[b.blockchain] ?? -99; + if (priorityA > priorityB) return -1; + if (priorityA < priorityB) return 1; + return 0; + }) + // 3. Map to include formatted data + USD value (since we have coins array with prices) + .map(balance => { + const coinInfo = coins.find(c => c.currency === balance.currency); + const price = coinInfo ? coinInfo.price : 0; + const usdValue = price * balance.amount; + + return { + ...balance, + formatted: balance.amount.toFixed(4), + usdValue, + iconFilename: coinInfo?.iconFilename + }; + }); + }, [balances, coins]); // Removed 'prices' dependency as they are inside 'coins' + + return ( +
+
+
+

Your Wallet

+

Refactored version of the Problem 3 component

+
+ +
+ + {showComparison ? ( + + ) : ( +
+ {processedBalances.map((balance) => ( +
+
+ {balance.iconFilename ? ( + {balance.currency} + ) : ( +
+ {balance.currency[0]} +
+ )} +
+

{balance.currency}

+ + {balance.blockchain} + +
+
+
+
{balance.formatted}
+
${balance.usdValue.toFixed(2)}
+
+
+ ))} + {processedBalances.length === 0 && ( +
+ No balances found matching priority criteria. +
+ )} +
+ )} +
+ ); +} +``` diff --git a/docs/problem5/detail.md b/docs/problem5/detail.md new file mode 100644 index 0000000000..6e46a91576 --- /dev/null +++ b/docs/problem5/detail.md @@ -0,0 +1,12 @@ +# Task + +Develop a backend server with ExpressJS. You are required to build a set of CRUD interface that allow a user to interact with the service. You are required to use TypeScript for this task. + +1. Interface functionalities: + 1. Create a resource. + 2. List resources with basic filters. + 3. Get details of a resource. + 4. Update resource details. + 5. Delete a resource. +2. You should connect your backend service with a simple database for data persistence. +3. Provide [`README.md`](http://README.md) for the configuration and the way to run application. diff --git a/docs/problem5/readme.md b/docs/problem5/readme.md new file mode 100644 index 0000000000..e5f1a3d355 --- /dev/null +++ b/docs/problem5/readme.md @@ -0,0 +1,15 @@ +For this problem i have created an ExpressJS server for coin management. I have added 5 API endpoints for CRUD operations. + +### Endpoints [coins.ts](../../backend/src/routes/coins.ts) + +- **`GET /api/coins`**: Fetches all coins. Supports filtering by `blockchain`, `search` string, and price ranges (`minPrice`, `maxPrice`). +- **`GET /api/coins/:id`**: Fetches a single coin by its ID. +- **`POST /api/coins`**: Creates a new coin. Expects `currency`, `blockchain`, `price`, `date`, and `iconFilename`. +- **`PUT /api/coins/:id`**: Updates an existing coin's details. +- **`DELETE /api/coins/:id`**: Removes a coin from the database. + +The coins image need to have icon in folder frontend\public\icons\coins\ +I wont be having a upload file form in order to avoid the complexity of file upload. We can add the icon in the folder and use the filename as the icon name. +I also created a coins page to interact with the API. +As you can check in the coin tab file +[CoinsPage.tsx](../../frontend/src/pages/CoinsPage.tsx) diff --git a/docs/problem6/detail.md b/docs/problem6/detail.md new file mode 100644 index 0000000000..34b3062f70 --- /dev/null +++ b/docs/problem6/detail.md @@ -0,0 +1,16 @@ +# Task + +Write the specification for a software module on the API service (backend application server). + +1. Create a documentation for this module on a `README.md` file. +2. Create a diagram to illustrate the flow of execution. +3. Add additional comments for improvement you may have in the documentation. +4. Your specification will be given to a backend engineering team to implement. + +### Software Requirements + +1. We have a website with a score board, which shows the top 10 user’s scores. +2. We want live update of the score board. +3. User can do an action (which we do not need to care what the action is), completing this action will increase the user’s score. +4. Upon completion the action will dispatch an API call to the application server to update the score. +5. We want to prevent malicious users from increasing scores without authorisation. diff --git a/docs/problem6/readme.md b/docs/problem6/readme.md new file mode 100644 index 0000000000..e9fc6e8b21 --- /dev/null +++ b/docs/problem6/readme.md @@ -0,0 +1,80 @@ +For this problem i have generated and updated a detail architecture. The solution was using token based authentication and WebSockets for real-time updates. +As we need to prevent user from increasing scores without authorisation. +There are many way to achive this such as with sever scoring or validation on every score update,rate limiting, signed request, replay events, etc. + +I choosed token based authentication and serverside scoring. It is minimal and secure enough for this problem. + +## Trigger action + +My action will be the swap action from problem2 whenever you swap a coin you will get +1 score. + +## Related file: + +[scores.ts](../../backend/src/routes/scores.ts) +[scoreboard.ts](../../backend/src/websocket/scoreboard.ts) +[ScoreboardPage.tsx](../../frontend/src/pages/ScoreboardPage.tsx) + +## Architecture Overview + +Our implementation spans a full-stack solution utilizing React on the frontend and Node.js/Express with WebSockets on the backend. + +### 1. Security & Anti-Cheat Mechanisms + +A primary requirement for this architecture is preventing users from sending arbitrary scores to the server (e.g., `{"score": 999999}`). + +To solve this, we implemented a **Token-Based Authentication & Server-Side Validation** model: + +- **Session Creation**: When a user first opens the app, the frontend calls `POST /api/scores/session`. The server generates a random username, a secure `sessionToken`, and initializes their score to `30` in the SQLite database. Only the `username` and `token` are stored in `localStorage` — **the score is never cached client-side**. +- **Score on Init**: After restoring a session from `localStorage`, the frontend immediately calls `GET /api/scores/me` with the token in the `Authorization` header. The server returns the current live score for that user. If the token is invalid (e.g., the database was wiped), the frontend automatically creates a fresh session. +- **Action Verification**: When the user performs an action (e.g., executing a token swap), they do _not_ send their new score. Instead, the swap endpoint (`POST /api/swap`) receives the `sessionToken` and the server atomically increments the score. +- **Server-Side Increment**: The server executes `UPDATE scores SET score = score + 1 WHERE sessionToken = ?` and then returns `{ newScore }` in the HTTP response. The frontend updates the header score directly from this value — no WebSocket lookup required. + +### 2. Real-Time Updates (WebSockets) + +Instead of relying on inefficient frontend HTTP polling to keep the top 10 list fresh, we implemented a push-based WebSocket architecture. + +- **WebSocket Server**: Attached to the Express HTTP server using the `ws` library. +- **Broadcasting Mechanism**: Whenever an action successfully alters _any_ user's score in the database, the server invokes a `broadcastScores()` function. This queries the database for the new `LIMIT 10` top scores and pushes a JSON stringified array to all active WebSocket connections. +- **Frontend Hook (`useWebSocket.ts`)**: The React frontend maintains a persistent connection to `ws://localhost:3001`. Upon receiving a message, it updates the local `scores` React state for the leaderboard display. +- **User's Own Score**: The WebSocket only broadcasts the **top 10**. New users with 30 points will not appear there. Their personal score is managed separately via `GET /api/scores/me` on init and via `newScore` in the swap response. + +### 3. Database (SQLite) + +- A `scores` table stores `id`, `username`, `sessionToken`, and `score`. +- Queries fetching the scoreboard are optimized using `ORDER BY score DESC LIMIT 10`. + +--- + +## Data Flow Diagram + +```mermaid +sequenceDiagram + participant User + participant Frontend as React Frontend + participant API as Backend API + participant DB as SQLite DB + participant WS as WebSocket Server + participant Clients as Connected Clients + + Note over Frontend,API: App Init + Frontend->>API: GET /api/scores/me (Authorization: Bearer token) + API->>DB: SELECT score WHERE sessionToken = token + DB-->>API: { score: 30 } + API-->>Frontend: { username, score } + Frontend->>Frontend: setLocalScore(30) + + Note over User,Clients: User performs a Swap + User->>Frontend: Click Confirm Swap + Frontend->>API: POST /api/swap (sessionToken in body) + API->>DB: UPDATE score = score + 1 WHERE sessionToken = ? + DB-->>API: changes: 1 + API->>DB: SELECT score WHERE sessionToken = ? + DB-->>API: { score: 31 } + API->>WS: broadcastScores() + WS->>DB: SELECT top 10 scores + DB-->>WS: Top 10 rows + WS->>Clients: Broadcast JSON (top 10 only) + API-->>Frontend: { success, newScore: 31 } + Frontend->>Frontend: setLocalScore(31) from HTTP response + Clients-->>Frontend: WebSocket updates Scoreboard UI +``` diff --git a/frontend/.gitignore b/frontend/.gitignore new file mode 100644 index 0000000000..a547bf36d8 --- /dev/null +++ b/frontend/.gitignore @@ -0,0 +1,24 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* +pnpm-debug.log* +lerna-debug.log* + +node_modules +dist +dist-ssr +*.local + +# Editor directories and files +.vscode/* +!.vscode/extensions.json +.idea +.DS_Store +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? diff --git a/frontend/eslint.config.js b/frontend/eslint.config.js new file mode 100644 index 0000000000..ef614d25c1 --- /dev/null +++ b/frontend/eslint.config.js @@ -0,0 +1,22 @@ +import js from '@eslint/js' +import globals from 'globals' +import reactHooks from 'eslint-plugin-react-hooks' +import reactRefresh from 'eslint-plugin-react-refresh' +import tseslint from 'typescript-eslint' +import { defineConfig, globalIgnores } from 'eslint/config' + +export default defineConfig([ + globalIgnores(['dist']), + { + files: ['**/*.{ts,tsx}'], + extends: [ + js.configs.recommended, + tseslint.configs.recommended, + reactHooks.configs.flat.recommended, + reactRefresh.configs.vite, + ], + languageOptions: { + globals: globals.browser, + }, + }, +]) diff --git a/frontend/index.html b/frontend/index.html new file mode 100644 index 0000000000..0fca6f0434 --- /dev/null +++ b/frontend/index.html @@ -0,0 +1,13 @@ + + + + + + + frontend + + +
+ + + diff --git a/frontend/package-lock.json b/frontend/package-lock.json new file mode 100644 index 0000000000..f545e7068c --- /dev/null +++ b/frontend/package-lock.json @@ -0,0 +1,3656 @@ +{ + "name": "frontend", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "frontend", + "version": "0.0.0", + "dependencies": { + "clsx": "^2.1.1", + "lucide-react": "^1.16.0", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "tailwind-merge": "^3.6.0" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@types/node": "^24.12.3", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "autoprefixer": "^10.5.0", + "eslint": "^10.3.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.6.0", + "postcss": "^8.5.14", + "tailwindcss": "^3.4.19", + "typescript": "~6.0.2", + "typescript-eslint": "^8.59.2", + "vite": "^8.0.12" + } + }, + "node_modules/@alloc/quick-lru": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", + "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.3.tgz", + "integrity": "sha512-LIVqM46zQWZhj17qA8wb4nW/ixr2y1Nw+r1etiAWgRM6U1IqP+LNhL1yg440jYZR72jCWcWbLWzIosH+uP1fqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.3", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.3.tgz", + "integrity": "sha512-b3ctpQwp+PROvU/cttc4OYl4MzfJUWy6FZg+PMXfzmt/+39iHVF0sDfqay8TQM3JA2EUOyKcFZt75jWriQijsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@emnapi/core": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", + "integrity": "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.2.1", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.10.0.tgz", + "integrity": "sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz", + "integrity": "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, + "node_modules/@eslint/config-array": { + "version": "0.23.5", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.23.5.tgz", + "integrity": "sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^3.0.5", + "debug": "^4.3.1", + "minimatch": "^10.2.4" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.6.0.tgz", + "integrity": "sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-1.2.1.tgz", + "integrity": "sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/js": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-10.0.1.tgz", + "integrity": "sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "eslint": "^10.0.0" + }, + "peerDependenciesMeta": { + "eslint": { + "optional": true + } + } + }, + "node_modules/@eslint/object-schema": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-3.0.5.tgz", + "integrity": "sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@eslint/plugin-kit": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.7.1.tgz", + "integrity": "sha512-rZAP3aVgB9ds9KOeUSL+zZ21hPmo8dh6fnIFwRQj5EAZl9gzR7wxYbYXYysAM8CTqGmUGyp2S4kUdV17MnGuWQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^1.2.1", + "levn": "^0.4.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz", + "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/types": "^0.15.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.8", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz", + "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.2", + "@humanfs/types": "^0.15.0", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/types": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz", + "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/retry": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "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==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.4.tgz", + "integrity": "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@tybys/wasm-util": "^0.10.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Brooooooklyn" + }, + "peerDependencies": { + "@emnapi/core": "^1.7.1", + "@emnapi/runtime": "^1.7.1" + } + }, + "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/@oxc-project/types": { + "version": "0.130.0", + "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.130.0.tgz", + "integrity": "sha512-ibD2usx9JRu7f5pu2tMKMI4cpA4NgXJQoYRP4pQ7Pxmn1l6k/53qWtQWZayhYy3X4QZkt90Ot+mJEaeXouio6Q==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/Boshen" + } + }, + "node_modules/@rolldown/binding-android-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.1.tgz", + "integrity": "sha512-fJI3I0r3C3Oj/zdBCpaCmBRZYf07xpaq4yCfDDoSFm+beWNzbIl26puW8RraUdugoJw/95zerNOn6jasAhzSmg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.1.tgz", + "integrity": "sha512-cKnAhWEsV7TPcA/5EAteDp6KcJZBQ2G+BqE7zayMMi7kMvwRsbv7WT9aOnn0WNl4SKEIf43vjS31iUPu80nzXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-darwin-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.1.tgz", + "integrity": "sha512-YKrVwQjIRBPo+5G/u03wGjbdy4q7pyzCe93DK9VJ7zkVmeg8LJ7GbgsiHWdR4xSoe4CAXRD7Bcjgbtr64bkXNg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-freebsd-x64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.1.tgz", + "integrity": "sha512-z/oBsREo46SsFqBwYtFe0kpJeBijAT48O/WXLI4suiCLBkr03RTtTJMCzSdDd2znlh8VJizL09XVkQgk8IZonw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm-gnueabihf": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.1.tgz", + "integrity": "sha512-ik8q7GM11zxvYxFc2PeDcT6TBvhCQMaUxfph/M5l9sKuTs/Sjg3L+Byw0F7w0ZVLBZmx30P+gG0ECzzN+MFcmQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.1.tgz", + "integrity": "sha512-QoSx2EkyrrdZ6kcyE8stqZ62t0Yra8Fs5ia9lOxJrh6TMQJK7gQKmscdTHf7pOXKREKrVwOtJcQG3qVSfc866A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-arm64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.1.tgz", + "integrity": "sha512-uwNwFpwKeNiZawfAWBgg0VIztPTV3ihhh1vV334h9ivnNLorxnQMU6Fz8wG1Zb4Qh9LC1/MkcyT3YlDXG3Rsgg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-ppc64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.1.tgz", + "integrity": "sha512-zY1bul7OWr7DFBiJ++wofXvnr8B45ce3QsQUhKrIhXsygAh7bTkwyeM1bi1a2g5C/yC/N8TZyGDEoMfm/l9mpg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-s390x-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.1.tgz", + "integrity": "sha512-0frlsT/f4Ft6I7SMESTKnF3cZsdicQn1dCMkF/jT9wDLE+gGoiQfv1nmT9e+s7s/fekvvy6tZM2jHvI2tkbJDQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-gnu": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.1.tgz", + "integrity": "sha512-XABVmGp9Tg0WspTVvwduTc4fpqy6JnAUrSQe6OuyqD/03nI7r0O9OWUkMIwFrjKAIqolvqoA4ZrJppgwE0Gxmw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-linux-x64-musl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.1.tgz", + "integrity": "sha512-bV4fzswuzVcKD90o/VM6QqKxnxlDq0g2BISDLNVmxrnhpv1DDbyPhCIjYfvzYLV+MvkKKnQt2Q6AO86SEBULUQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-openharmony-arm64": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.1.tgz", + "integrity": "sha512-/Mh0Zhq3OP7fVs0kcQHZP6lZEthMGTaSf8UBQYSFEZDWGXXlEC+nJ6EqenaK2t4LBXMe3A+K/G2BVXXdtOr4PQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-wasm32-wasi": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.1.tgz", + "integrity": "sha512-+1xc9X45l8ufsBAm6Gjvx2qDRIY9lTVt0cgWNcJ+1gdhXvkbxePA60yRTwSTuXL09CMhyJmjpV7E3NoyxbqFQQ==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "1.10.0", + "@emnapi/runtime": "1.10.0", + "@napi-rs/wasm-runtime": "^1.1.4" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-arm64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.1.tgz", + "integrity": "sha512-1D+UqZdfnuR+Jy1GgMJwi85bD40H21uNmOPRWQhw4oRSuolZ/B5rixZ45DK2KXOTCvmVCecauWgEhbw8bI7tOw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/binding-win32-x64-msvc": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.1.tgz", + "integrity": "sha512-INAycaWuhlOK3wk4mRHGsdgwYWmd9cChdPdE9bwWmy6rn9VqVNYNFGhOdXrofXUxwHIncSiPNb8tNm8knDVIeQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.1.tgz", + "integrity": "sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.2.tgz", + "integrity": "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@types/esrecurse": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/@types/esrecurse/-/esrecurse-4.3.1.tgz", + "integrity": "sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/estree": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", + "integrity": "sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/node": { + "version": "24.12.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.4.tgz", + "integrity": "sha512-GUUEShf+PBCGW2KaXwcIt3Yk+e3pkKwWKb9GSyM9WQVE+ep2jzmHdGsHzu4wgcZy5fN9FBdVzjpBQsYlpfpgLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "dev": true, + "license": "MIT", + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.3.tgz", + "integrity": "sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.12.2", + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/type-utils": "8.59.3", + "@typescript-eslint/utils": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", + "ignore": "^7.0.5", + "natural-compare": "^1.4.0", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^8.59.3", + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/@typescript-eslint/parser": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.3.tgz", + "integrity": "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.3.tgz", + "integrity": "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.59.3", + "@typescript-eslint/types": "^8.59.3", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz", + "integrity": "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.3.tgz", + "integrity": "sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/type-utils": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.3.tgz", + "integrity": "sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/utils": "8.59.3", + "debug": "^4.4.3", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/types": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.3.tgz", + "integrity": "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@typescript-eslint/typescript-estree": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.3.tgz", + "integrity": "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/project-service": "8.59.3", + "@typescript-eslint/tsconfig-utils": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/visitor-keys": "8.59.3", + "debug": "^4.4.3", + "minimatch": "^10.2.2", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.5.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { + "version": "7.8.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.0.tgz", + "integrity": "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.3.tgz", + "integrity": "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.59.3", + "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.3.tgz", + "integrity": "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "8.59.3", + "eslint-visitor-keys": "^5.0.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vitejs/plugin-react": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-6.0.2.tgz", + "integrity": "sha512-DlSMqo4WhThw4vB8Mpn0Woe9J+Jfq1geJ61AKW0QEgLzGMNwtIMdxbDUzLxcun8W7NbJO0e2Jg/Nxm3cCSVzzg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rolldown/pluginutils": "^1.0.0" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "peerDependencies": { + "@rolldown/plugin-babel": "^0.1.7 || ^0.2.0", + "babel-plugin-react-compiler": "^1.0.0", + "vite": "^8.0.0" + }, + "peerDependenciesMeta": { + "@rolldown/plugin-babel": { + "optional": true + }, + "babel-plugin-react-compiler": { + "optional": true + } + } + }, + "node_modules/acorn": { + "version": "8.16.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", + "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } + }, + "node_modules/ajv": { + "version": "6.15.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.15.0.tgz", + "integrity": "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dev": true, + "license": "ISC", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/arg": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/arg/-/arg-5.0.2.tgz", + "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", + "dev": true, + "license": "MIT" + }, + "node_modules/autoprefixer": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/autoprefixer/-/autoprefixer-10.5.0.tgz", + "integrity": "sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/autoprefixer" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "browserslist": "^4.28.2", + "caniuse-lite": "^1.0.30001787", + "fraction.js": "^5.3.4", + "picocolors": "^1.1.1", + "postcss-value-parser": "^4.2.0" + }, + "bin": { + "autoprefixer": "bin/autoprefixer" + }, + "engines": { + "node": "^10 || ^12 || >=14" + }, + "peerDependencies": { + "postcss": "^8.1.0" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.30", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.30.tgz", + "integrity": "sha512-xjOFN16Ha1+Rz4nFYKqHU/LSB+gx/Vi3yQLX7r7sAW+Wa+8hhF2h4pvqTrTMc8+WcDBEunnUurr46Jvv0jk3Vg==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.6.tgz", + "integrity": "sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/camelcase-css": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/camelcase-css/-/camelcase-css-2.0.1.tgz", + "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001793", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001793.tgz", + "integrity": "sha512-iwSsYWaCOoh26cV8NwNRViHlrfUvYsHDfRVcbtmw0Kg6PJIZZXwMkj1442FYLBGkeUf1juAsU3DTfxW579mrPA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/chokidar": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/chokidar/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/commander": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-4.1.1.tgz", + "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "dev": true, + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/didyoumean": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/didyoumean/-/didyoumean-1.2.2.tgz", + "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/dlv": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/dlv/-/dlv-1.1.3.tgz", + "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", + "dev": true, + "license": "MIT" + }, + "node_modules/electron-to-chromium": { + "version": "1.5.357", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.357.tgz", + "integrity": "sha512-NHlTIQDK8fmVwHwuIzmXYEJ1Ewq3D9wDNc0cWXxDGysP6Pb21giwGNkxiTifyKy/4SoPuN5l6GLP1W9Sv7zB2g==", + "dev": true, + "license": "ISC" + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint": { + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-10.4.0.tgz", + "integrity": "sha512-loXy6bWOoP3EP6JA7jo6p5jMpBJmHmsNZM5SFRHLdh1MGOPurMnNBj4ZlAbaqUAaQWbCr7jHV4P7gzAyryZWkQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.2", + "@eslint/config-array": "^0.23.5", + "@eslint/config-helpers": "^0.6.0", + "@eslint/core": "^1.2.1", + "@eslint/plugin-kit": "^0.7.1", + "@humanfs/node": "^0.16.6", + "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.4.2", + "@types/estree": "^1.0.6", + "ajv": "^6.14.0", + "cross-spawn": "^7.0.6", + "debug": "^4.3.2", + "escape-string-regexp": "^4.0.0", + "eslint-scope": "^9.1.2", + "eslint-visitor-keys": "^5.0.1", + "espree": "^11.2.0", + "esquery": "^1.7.0", + "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", + "file-entry-cache": "^8.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "ignore": "^5.2.0", + "imurmurhash": "^0.1.4", + "is-glob": "^4.0.0", + "json-stable-stringify-without-jsonify": "^1.0.1", + "minimatch": "^10.2.4", + "natural-compare": "^1.4.0", + "optionator": "^0.9.3" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } + } + }, + "node_modules/eslint-plugin-react-hooks": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-7.1.1.tgz", + "integrity": "sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.24.4", + "@babel/parser": "^7.24.4", + "hermes-parser": "^0.25.1", + "zod": "^3.25.0 || ^4.0.0", + "zod-validation-error": "^3.5.0 || ^4.0.0" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0" + } + }, + "node_modules/eslint-plugin-react-refresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.5.2.tgz", + "integrity": "sha512-hmgTH57GfzoTFjVN0yBwTggnsVUF2tcqi7RJZHqi9lIezSs4eFyAMktA68YD4r5kNw1mxyY4dmkyoFDb3FIqrA==", + "dev": true, + "license": "MIT", + "peerDependencies": { + "eslint": "^9 || ^10" + } + }, + "node_modules/eslint-scope": { + "version": "9.1.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-9.1.2.tgz", + "integrity": "sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@types/esrecurse": "^4.3.1", + "@types/estree": "^1.0.8", + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/eslint-visitor-keys": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", + "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree": { + "version": "11.2.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-11.2.0.tgz", + "integrity": "sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.16.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^5.0.1" + }, + "engines": { + "node": "^20.19.0 || ^22.13.0 || >=24" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/esquery": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "estraverse": "^5.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esrecurse": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", + "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "estraverse": "^5.2.0" + }, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/file-entry-cache": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "flat-cache": "^4.0.0" + }, + "engines": { + "node": ">=16.0.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "license": "MIT", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "license": "MIT", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/flat-cache": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.4" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/flatted": { + "version": "3.4.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.2.tgz", + "integrity": "sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==", + "dev": true, + "license": "ISC" + }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/globals": { + "version": "17.6.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-17.6.0.tgz", + "integrity": "sha512-sepffkT8stwnIYbsMBpoCHJuJM5l98FUF2AnE07hfvE0m/qp3R586hw4jF4uadbhvg1ooIdzuu7CsfD2jzCaNA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/hasown": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.3.tgz", + "integrity": "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hermes-estree": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-estree/-/hermes-estree-0.25.1.tgz", + "integrity": "sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==", + "dev": true, + "license": "MIT" + }, + "node_modules/hermes-parser": { + "version": "0.25.1", + "resolved": "https://registry.npmjs.org/hermes-parser/-/hermes-parser-0.25.1.tgz", + "integrity": "sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hermes-estree": "0.25.1" + } + }, + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/imurmurhash": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-core-module": { + "version": "2.16.2", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.2.tgz", + "integrity": "sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "hasown": "^2.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "1.21.7", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-1.21.7.tgz", + "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "bin/jiti.js" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-stable-stringify-without-jsonify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/levn": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", + "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1", + "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lilconfig": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", + "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/antonk52" + } + }, + "node_modules/lines-and-columns": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "dev": true, + "license": "MIT" + }, + "node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/lucide-react": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.16.0.tgz", + "integrity": "sha512-dYwyPzb4MEKpGUmNYk3WKWPnMrHs3FKM+q94kAnJrcDIqqn1hq2xY8scaS2ovsOCM5D51ey2gaRG3PBb1vgoYQ==", + "license": "ISC", + "peerDependencies": { + "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/mz": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", + "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0", + "object-assign": "^4.0.1", + "thenify-all": "^1.0.0" + } + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/natural-compare": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" + }, + "node_modules/node-releases": { + "version": "2.0.44", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.44.tgz", + "integrity": "sha512-5WUyunoPMsvvEhS8AxHtRzP+oA8UCkJ7YRxatWKjngndhDGLiqEVAQKWjFAiAiuL8zMRGzGSJxFnLetoa43qGQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-hash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-3.0.0.tgz", + "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", + "dev": true, + "license": "MIT", + "dependencies": { + "deep-is": "^0.1.3", + "fast-levenshtein": "^2.0.6", + "levn": "^0.4.1", + "prelude-ls": "^1.2.1", + "type-check": "^0.4.0", + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/pirates": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 6" + } + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/postcss-import": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-15.1.0.tgz", + "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", + "dev": true, + "license": "MIT", + "dependencies": { + "postcss-value-parser": "^4.0.0", + "read-cache": "^1.0.0", + "resolve": "^1.1.7" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "postcss": "^8.0.0" + } + }, + "node_modules/postcss-js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/postcss-js/-/postcss-js-4.1.0.tgz", + "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "camelcase-css": "^2.0.1" + }, + "engines": { + "node": "^12 || ^14 || >= 16" + }, + "peerDependencies": { + "postcss": "^8.4.21" + } + }, + "node_modules/postcss-load-config": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/postcss-load-config/-/postcss-load-config-6.0.1.tgz", + "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "lilconfig": "^3.1.1" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "jiti": ">=1.21.0", + "postcss": ">=8.0.9", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + }, + "postcss": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/postcss-nested": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/postcss-nested/-/postcss-nested-6.2.0.tgz", + "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "^6.1.1" + }, + "engines": { + "node": ">=12.0" + }, + "peerDependencies": { + "postcss": "^8.2.14" + } + }, + "node_modules/postcss-selector-parser": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", + "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/postcss-value-parser": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", + "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/prelude-ls": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", + "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/react": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", + "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", + "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", + "license": "MIT", + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.6" + } + }, + "node_modules/read-cache": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", + "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "pify": "^2.3.0" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/readdirp/node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/resolve": { + "version": "1.22.12", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.12.tgz", + "integrity": "sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "is-core-module": "^2.16.1", + "path-parse": "^1.0.7", + "supports-preserve-symlinks-flag": "^1.0.0" + }, + "bin": { + "resolve": "bin/resolve" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rolldown": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.1.tgz", + "integrity": "sha512-X0KQHljNnEkWNqqiz9zJrGunh1B0HgOxLXvnFpCOcadzcy5qohZ3tqMEUg00vncoRovXuK3ZqCT9KnnKzoInFQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@oxc-project/types": "=0.130.0", + "@rolldown/pluginutils": "^1.0.0" + }, + "bin": { + "rolldown": "bin/cli.mjs" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "optionalDependencies": { + "@rolldown/binding-android-arm64": "1.0.1", + "@rolldown/binding-darwin-arm64": "1.0.1", + "@rolldown/binding-darwin-x64": "1.0.1", + "@rolldown/binding-freebsd-x64": "1.0.1", + "@rolldown/binding-linux-arm-gnueabihf": "1.0.1", + "@rolldown/binding-linux-arm64-gnu": "1.0.1", + "@rolldown/binding-linux-arm64-musl": "1.0.1", + "@rolldown/binding-linux-ppc64-gnu": "1.0.1", + "@rolldown/binding-linux-s390x-gnu": "1.0.1", + "@rolldown/binding-linux-x64-gnu": "1.0.1", + "@rolldown/binding-linux-x64-musl": "1.0.1", + "@rolldown/binding-openharmony-arm64": "1.0.1", + "@rolldown/binding-wasm32-wasi": "1.0.1", + "@rolldown/binding-win32-arm64-msvc": "1.0.1", + "@rolldown/binding-win32-x64-msvc": "1.0.1" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT" + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sucrase": { + "version": "3.35.1", + "resolved": "https://registry.npmjs.org/sucrase/-/sucrase-3.35.1.tgz", + "integrity": "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.2", + "commander": "^4.0.0", + "lines-and-columns": "^1.1.6", + "mz": "^2.7.0", + "pirates": "^4.0.1", + "tinyglobby": "^0.2.11", + "ts-interface-checker": "^0.1.9" + }, + "bin": { + "sucrase": "bin/sucrase", + "sucrase-node": "bin/sucrase-node" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/supports-preserve-symlinks-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", + "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/tailwind-merge": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/tailwind-merge/-/tailwind-merge-3.6.0.tgz", + "integrity": "sha512-uxL7qAVQriqRQPAyK3pj66VqskWqoZ37PW94jwOTwNfq/z9oyu1V+eqrZqtR2+fCiXdYOZe/Modt8GtvqNzu+w==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/dcastil" + } + }, + "node_modules/tailwindcss": { + "version": "3.4.19", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.19.tgz", + "integrity": "sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@alloc/quick-lru": "^5.2.0", + "arg": "^5.0.2", + "chokidar": "^3.6.0", + "didyoumean": "^1.2.2", + "dlv": "^1.1.3", + "fast-glob": "^3.3.2", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "jiti": "^1.21.7", + "lilconfig": "^3.1.3", + "micromatch": "^4.0.8", + "normalize-path": "^3.0.0", + "object-hash": "^3.0.0", + "picocolors": "^1.1.1", + "postcss": "^8.4.47", + "postcss-import": "^15.1.0", + "postcss-js": "^4.0.1", + "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", + "postcss-nested": "^6.2.0", + "postcss-selector-parser": "^6.1.2", + "resolve": "^1.22.8", + "sucrase": "^3.35.0" + }, + "bin": { + "tailwind": "lib/cli.js", + "tailwindcss": "lib/cli.js" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/thenify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", + "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "any-promise": "^1.0.0" + } + }, + "node_modules/thenify-all": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", + "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "thenify": ">= 3.1.0 < 4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.16", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.16.tgz", + "integrity": "sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/ts-api-utils": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.5.0.tgz", + "integrity": "sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/ts-interface-checker": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", + "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD", + "optional": true + }, + "node_modules/type-check": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", + "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", + "dev": true, + "license": "MIT", + "dependencies": { + "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/typescript": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-6.0.3.tgz", + "integrity": "sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typescript-eslint": { + "version": "8.59.3", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.3.tgz", + "integrity": "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/eslint-plugin": "8.59.3", + "@typescript-eslint/parser": "8.59.3", + "@typescript-eslint/typescript-estree": "8.59.3", + "@typescript-eslint/utils": "8.59.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", + "typescript": ">=4.8.4 <6.1.0" + } + }, + "node_modules/undici-types": { + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "8.0.13", + "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.13.tgz", + "integrity": "sha512-MFtjBYgzmSxmgA4RAfjIyXWpGe1oALnjgUTzzV7QLx/TKxCzjtMH6Fd9/eVK+5Fg1qNoz5VAwsmMs/NofrmJvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "lightningcss": "^1.32.0", + "picomatch": "^4.0.4", + "postcss": "^8.5.14", + "rolldown": "1.0.1", + "tinyglobby": "^0.2.16" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "@vitejs/devtools": "^0.1.18", + "esbuild": "^0.27.0 || ^0.28.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "@vitejs/devtools": { + "optional": true + }, + "esbuild": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "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", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/zod": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.4.3.tgz", + "integrity": "sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-validation-error": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-4.0.2.tgz", + "integrity": "sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } + } + } +} diff --git a/frontend/package.json b/frontend/package.json new file mode 100644 index 0000000000..12a1b5494c --- /dev/null +++ b/frontend/package.json @@ -0,0 +1,36 @@ +{ + "name": "frontend", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "tsc -b && vite build", + "lint": "eslint .", + "preview": "vite preview" + }, + "dependencies": { + "clsx": "^2.1.1", + "lucide-react": "^1.16.0", + "react": "^19.2.6", + "react-dom": "^19.2.6", + "tailwind-merge": "^3.6.0" + }, + "devDependencies": { + "@eslint/js": "^10.0.1", + "@types/node": "^24.12.3", + "@types/react": "^19.2.14", + "@types/react-dom": "^19.2.3", + "@vitejs/plugin-react": "^6.0.1", + "autoprefixer": "^10.5.0", + "eslint": "^10.3.0", + "eslint-plugin-react-hooks": "^7.1.1", + "eslint-plugin-react-refresh": "^0.5.2", + "globals": "^17.6.0", + "postcss": "^8.5.14", + "tailwindcss": "^3.4.19", + "typescript": "~6.0.2", + "typescript-eslint": "^8.59.2", + "vite": "^8.0.12" + } +} diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js new file mode 100644 index 0000000000..2e7af2b7f1 --- /dev/null +++ b/frontend/postcss.config.js @@ -0,0 +1,6 @@ +export default { + plugins: { + tailwindcss: {}, + autoprefixer: {}, + }, +} diff --git a/frontend/public/coins/Bitcoin-filled.svg b/frontend/public/coins/Bitcoin-filled.svg new file mode 100644 index 0000000000..b684b9af11 --- /dev/null +++ b/frontend/public/coins/Bitcoin-filled.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/public/coins/Carbon-filled.svg b/frontend/public/coins/Carbon-filled.svg new file mode 100644 index 0000000000..1e005956a1 --- /dev/null +++ b/frontend/public/coins/Carbon-filled.svg @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/coins/Cosmos-Hub-filled.svg b/frontend/public/coins/Cosmos-Hub-filled.svg new file mode 100644 index 0000000000..e166fe4abe --- /dev/null +++ b/frontend/public/coins/Cosmos-Hub-filled.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/public/coins/Ethereum-filled.svg b/frontend/public/coins/Ethereum-filled.svg new file mode 100644 index 0000000000..35273eac70 --- /dev/null +++ b/frontend/public/coins/Ethereum-filled.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/public/coins/Evmos-filled.svg b/frontend/public/coins/Evmos-filled.svg new file mode 100644 index 0000000000..3637d7936d --- /dev/null +++ b/frontend/public/coins/Evmos-filled.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/frontend/public/coins/Iris-Hub-filled.svg b/frontend/public/coins/Iris-Hub-filled.svg new file mode 100644 index 0000000000..250972db31 --- /dev/null +++ b/frontend/public/coins/Iris-Hub-filled.svg @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/coins/Kujira-filled.svg b/frontend/public/coins/Kujira-filled.svg new file mode 100644 index 0000000000..2dc887cdd7 --- /dev/null +++ b/frontend/public/coins/Kujira-filled.svg @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/coins/Neo-filled.svg b/frontend/public/coins/Neo-filled.svg new file mode 100644 index 0000000000..36a4a35648 --- /dev/null +++ b/frontend/public/coins/Neo-filled.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/frontend/public/coins/OKC-filled.svg b/frontend/public/coins/OKC-filled.svg new file mode 100644 index 0000000000..2265452507 --- /dev/null +++ b/frontend/public/coins/OKC-filled.svg @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/coins/Osmosis-filled.svg b/frontend/public/coins/Osmosis-filled.svg new file mode 100644 index 0000000000..943e27f958 --- /dev/null +++ b/frontend/public/coins/Osmosis-filled.svg @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/coins/Stride-filled.svg b/frontend/public/coins/Stride-filled.svg new file mode 100644 index 0000000000..a5fd57eaf3 --- /dev/null +++ b/frontend/public/coins/Stride-filled.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/frontend/public/coins/Terra-filled.svg b/frontend/public/coins/Terra-filled.svg new file mode 100644 index 0000000000..0b5f182fdb --- /dev/null +++ b/frontend/public/coins/Terra-filled.svg @@ -0,0 +1,51 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/public/coins/Zilliqa-filled.svg b/frontend/public/coins/Zilliqa-filled.svg new file mode 100644 index 0000000000..268fb77ed1 --- /dev/null +++ b/frontend/public/coins/Zilliqa-filled.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/frontend/public/favicon.svg b/frontend/public/favicon.svg new file mode 100644 index 0000000000..6893eb1323 --- /dev/null +++ b/frontend/public/favicon.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/public/icons.svg b/frontend/public/icons.svg new file mode 100644 index 0000000000..e9522193d9 --- /dev/null +++ b/frontend/public/icons.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/frontend/src/App.css b/frontend/src/App.css new file mode 100644 index 0000000000..f90339d8f7 --- /dev/null +++ b/frontend/src/App.css @@ -0,0 +1,184 @@ +.counter { + font-size: 16px; + padding: 5px 10px; + border-radius: 5px; + color: var(--accent); + background: var(--accent-bg); + border: 2px solid transparent; + transition: border-color 0.3s; + margin-bottom: 24px; + + &:hover { + border-color: var(--accent-border); + } + &:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; + } +} + +.hero { + position: relative; + + .base, + .framework, + .vite { + inset-inline: 0; + margin: 0 auto; + } + + .base { + width: 170px; + position: relative; + z-index: 0; + } + + .framework, + .vite { + position: absolute; + } + + .framework { + z-index: 1; + top: 34px; + height: 28px; + transform: perspective(2000px) rotateZ(300deg) rotateX(44deg) rotateY(39deg) + scale(1.4); + } + + .vite { + z-index: 0; + top: 107px; + height: 26px; + width: auto; + transform: perspective(2000px) rotateZ(300deg) rotateX(40deg) rotateY(39deg) + scale(0.8); + } +} + +#center { + display: flex; + flex-direction: column; + gap: 25px; + place-content: center; + place-items: center; + flex-grow: 1; + + @media (max-width: 1024px) { + padding: 32px 20px 24px; + gap: 18px; + } +} + +#next-steps { + display: flex; + border-top: 1px solid var(--border); + text-align: left; + + & > div { + flex: 1 1 0; + padding: 32px; + @media (max-width: 1024px) { + padding: 24px 20px; + } + } + + .icon { + margin-bottom: 16px; + width: 22px; + height: 22px; + } + + @media (max-width: 1024px) { + flex-direction: column; + text-align: center; + } +} + +#docs { + border-right: 1px solid var(--border); + + @media (max-width: 1024px) { + border-right: none; + border-bottom: 1px solid var(--border); + } +} + +#next-steps ul { + list-style: none; + padding: 0; + display: flex; + gap: 8px; + margin: 32px 0 0; + + .logo { + height: 18px; + } + + a { + color: var(--text-h); + font-size: 16px; + border-radius: 6px; + background: var(--social-bg); + display: flex; + padding: 6px 12px; + align-items: center; + gap: 8px; + text-decoration: none; + transition: box-shadow 0.3s; + + &:hover { + box-shadow: var(--shadow); + } + .button-icon { + height: 18px; + width: 18px; + } + } + + @media (max-width: 1024px) { + margin-top: 20px; + flex-wrap: wrap; + justify-content: center; + + li { + flex: 1 1 calc(50% - 8px); + } + + a { + width: 100%; + justify-content: center; + box-sizing: border-box; + } + } +} + +#spacer { + height: 88px; + border-top: 1px solid var(--border); + @media (max-width: 1024px) { + height: 48px; + } +} + +.ticks { + position: relative; + width: 100%; + + &::before, + &::after { + content: ''; + position: absolute; + top: -4.5px; + border: 5px solid transparent; + } + + &::before { + left: 0; + border-left-color: var(--border); + } + &::after { + right: 0; + border-right-color: var(--border); + } +} diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx new file mode 100644 index 0000000000..218b02a00f --- /dev/null +++ b/frontend/src/App.tsx @@ -0,0 +1,108 @@ +import { useState, useEffect } from 'react'; +import type { AuthSession } from './types'; +import { Layout } from './components/Layout'; +import { SwapPage } from './pages/SwapPage'; +import { WalletPage } from './pages/WalletPage'; +import { ScoreboardPage } from './pages/ScoreboardPage'; +import { CoinsPage } from './pages/CoinsPage'; +import { useCoins } from './hooks/useCoins'; +import { useWebSocket } from './hooks/useWebSocket'; + +function App() { + const [activeTab, setActiveTab] = useState<'swap' | 'wallet' | 'scoreboard' | 'coins'>('swap'); + const [session, setSession] = useState(null); + // localScore is always fetched from the server — never persisted in localStorage + const [localScore, setLocalScore] = useState(0); + + const { coins, loading, refetch } = useCoins(); + const { scores } = useWebSocket(); + + const fetchMyScore = (token: string) => { + fetch('http://localhost:3001/api/scores/me', { + headers: { Authorization: `Bearer ${token}` } + }) + .then(res => { + if (!res.ok) throw new Error('Invalid session'); + return res.json(); + }) + .then(data => setLocalScore(data.score)) + .catch(() => { + // Token is invalid (e.g. DB was wiped). Clear and start fresh. + localStorage.removeItem('crypto_dash_session'); + createNewSession(); + }); + }; + + const createNewSession = () => { + fetch('http://localhost:3001/api/scores/session', { method: 'POST' }) + .then(res => res.json()) + .then(data => { + // Only store the token + username — score always comes from server + localStorage.setItem('crypto_dash_session', JSON.stringify({ username: data.username, token: data.token })); + setSession({ username: data.username, token: data.token }); + setLocalScore(data.score); // new sessions start at 40 + }) + .catch(console.error); + }; + + // On mount: restore or create session, then fetch current score from server + useEffect(() => { + const saved = localStorage.getItem('crypto_dash_session'); + if (saved) { + const parsed = JSON.parse(saved) as AuthSession; + setSession(parsed); + fetchMyScore(parsed.token); // always pull fresh score from server + } else { + createNewSession(); + } + }, []); + + // Sync localScore if user breaks into the WebSocket top-10 broadcast + useEffect(() => { + if (!session?.username) return; + const top10Entry = scores.find(s => s.username === session.username); + if (top10Entry) { + setLocalScore(top10Entry.score); + } + }, [scores, session?.username]); + + // Called by SwapPage when the swap response returns a fresh server score + const handleScoreUpdate = (newScore: number) => { + setLocalScore(newScore); + }; + + return ( + + {loading ? ( +
+ Loading dashboard... +
+ ) : ( + <> +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ + )} +
+ ); +} + +export default App; diff --git a/frontend/src/assets/hero.png b/frontend/src/assets/hero.png new file mode 100644 index 0000000000000000000000000000000000000000..02251f4b956c55af2d76fd0788124d7eee2b45eb GIT binary patch literal 13057 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 P00000NkvXXu0mjft=yBf literal 0 HcmV?d00001 diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg new file mode 100644 index 0000000000..6c87de9bb3 --- /dev/null +++ b/frontend/src/assets/react.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/frontend/src/assets/vite.svg b/frontend/src/assets/vite.svg new file mode 100644 index 0000000000..5101b674df --- /dev/null +++ b/frontend/src/assets/vite.svg @@ -0,0 +1 @@ +Vite diff --git a/frontend/src/components/Layout.tsx b/frontend/src/components/Layout.tsx new file mode 100644 index 0000000000..6bc7e63cad --- /dev/null +++ b/frontend/src/components/Layout.tsx @@ -0,0 +1,93 @@ +import type { FC, ReactNode } from 'react'; +import { RefreshCw, Wallet, Trophy, Database } from 'lucide-react'; + +interface LayoutProps { + children: ReactNode; + activeTab: 'swap' | 'wallet' | 'scoreboard' | 'coins'; + onTabChange: (tab: 'swap' | 'wallet' | 'scoreboard' | 'coins') => void; + username?: string; + score?: number; +} + +export const Layout: FC = ({ children, activeTab, onTabChange, username, score }) => { + const tabs = [ + { id: 'swap', label: 'Swap', icon: RefreshCw }, + { id: 'wallet', label: 'Wallet', icon: Wallet }, + { id: 'scoreboard', label: 'Scoreboard', icon: Trophy }, + { id: 'coins', label: 'Coins', icon: Database }, + ] as const; + + return ( +
+ {/* Background gradients */} +
+
+ +
+
+
+ + + CryptoDash + +
+ +
+ {tabs.map(tab => { + const Icon = tab.icon; + return ( + + ); + })} +
+ +
+ {username && ( +
+
+ {username} + {score} pts +
+ )} +
+
+
+ +
+ {children} +
+ + {/* Mobile nav */} +
+
+ {tabs.map(tab => { + const Icon = tab.icon; + return ( + + ); + })} +
+
+
+ ); +}; diff --git a/frontend/src/components/SumCalculator.tsx b/frontend/src/components/SumCalculator.tsx new file mode 100644 index 0000000000..6a6cdc57da --- /dev/null +++ b/frontend/src/components/SumCalculator.tsx @@ -0,0 +1,86 @@ +import { useState, useEffect } from 'react'; +import type { FC } from 'react'; +import { Calculator, X } from 'lucide-react'; +import { sum_to_n_a, sum_to_n_b, sum_to_n_c } from '../utils/sum'; + +interface SumCalculatorProps { + onSelectResult: (result: number) => void; + onClose: () => void; +} + +export const SumCalculator: FC = ({ onSelectResult, onClose }) => { + const [inputVal, setInputVal] = useState(''); + const [results, setResults] = useState<{a: number|null, b: number|null, c: number|null}>({a: null, b: null, c: null}); + + useEffect(() => { + const n = parseInt(inputVal); + if (!isNaN(n) && n > 0 && n < 10000) { // arbitrary limit to prevent max call stack + setResults({ + a: sum_to_n_a(n), + b: sum_to_n_b(n), + c: sum_to_n_c(n) + }); + } else { + setResults({a: null, b: null, c: null}); + } + }, [inputVal]); + + const bestResult = results.a; + + return ( +
+
+
+ + Sum to N Calculator +
+ +
+ +
+
+ + setInputVal(e.target.value)} + className="w-full bg-slate-900 border border-slate-700 rounded-lg px-3 py-2 text-white focus:outline-none focus:border-cyan-500 text-sm" + placeholder="e.g. 5" + /> +
+ + {bestResult !== null && ( +
+ + + + + +
+ )} +
+
+ ); +}; + +const ResultRow = ({ label, result, badge, badgeColor }: { label: string, result: number | null, badge: string, badgeColor: string }) => ( +
+
+ {label} + + {badge} + +
+ {result} +
+); diff --git a/frontend/src/hooks/useCoins.ts b/frontend/src/hooks/useCoins.ts new file mode 100644 index 0000000000..8363460166 --- /dev/null +++ b/frontend/src/hooks/useCoins.ts @@ -0,0 +1,27 @@ +import { useState, useEffect } from 'react'; +import type { Coin } from '../types'; + +export const useCoins = () => { + const [coins, setCoins] = useState([]); + const [loading, setLoading] = useState(true); + + const fetchCoins = () => { + setLoading(true); + fetch('http://localhost:3001/api/coins') + .then(res => res.json()) + .then(data => { + setCoins(data); + setLoading(false); + }) + .catch(err => { + console.error('Error fetching coins', err); + setLoading(false); + }); + }; + + useEffect(() => { + fetchCoins(); + }, []); + + return { coins, loading, refetch: fetchCoins }; +}; diff --git a/frontend/src/hooks/useWebSocket.ts b/frontend/src/hooks/useWebSocket.ts new file mode 100644 index 0000000000..6aee62a40b --- /dev/null +++ b/frontend/src/hooks/useWebSocket.ts @@ -0,0 +1,31 @@ +import { useState, useEffect } from 'react'; +import type { Score } from '../types'; + +export const useWebSocket = () => { + const [scores, setScores] = useState([]); + + useEffect(() => { + const ws = new WebSocket('ws://localhost:3001'); + + ws.onmessage = (event) => { + try { + const message = JSON.parse(event.data); + if (message.type === 'SCORE_UPDATE') { + setScores(message.data); + } + } catch (e) { + console.error('Error parsing websocket message', e); + } + }; + + return () => { + if (ws.readyState === WebSocket.OPEN) { + ws.close(); + } else { + ws.onopen = () => ws.close(); + } + }; + }, []); + + return { scores }; +}; diff --git a/frontend/src/index.css b/frontend/src/index.css new file mode 100644 index 0000000000..415c56d820 --- /dev/null +++ b/frontend/src/index.css @@ -0,0 +1,21 @@ +@tailwind base; +@tailwind components; +@tailwind utilities; + +@layer base { + body { + @apply bg-slate-900 text-slate-100 font-sans antialiased; + } +} + +@layer components { + .glass-card { + @apply bg-white/5 backdrop-blur-md border border-white/10 shadow-xl rounded-2xl; + } + .input-field { + @apply w-full bg-slate-800/50 border border-slate-700 rounded-xl px-4 py-3 text-white placeholder-slate-400 focus:outline-none focus:ring-2 focus:ring-cyan-500 transition-all duration-200; + } + .btn-primary { + @apply w-full bg-gradient-to-r from-cyan-500 to-blue-500 hover:from-cyan-400 hover:to-blue-400 text-white font-semibold rounded-xl px-4 py-3 shadow-lg shadow-cyan-500/20 active:scale-[0.98] transition-all duration-200 disabled:opacity-50 disabled:cursor-not-allowed; + } +} diff --git a/frontend/src/main.tsx b/frontend/src/main.tsx new file mode 100644 index 0000000000..bef5202a32 --- /dev/null +++ b/frontend/src/main.tsx @@ -0,0 +1,10 @@ +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import './index.css' +import App from './App.tsx' + +createRoot(document.getElementById('root')!).render( + + + , +) diff --git a/frontend/src/pages/CoinsPage.tsx b/frontend/src/pages/CoinsPage.tsx new file mode 100644 index 0000000000..9f6cac1438 --- /dev/null +++ b/frontend/src/pages/CoinsPage.tsx @@ -0,0 +1,314 @@ +import { useState } from 'react'; +import type { FC } from 'react'; +import { Plus, Edit2, Trash2, X, Loader2, AlertTriangle } from 'lucide-react'; +import type { Coin } from '../types'; + +interface CoinsPageProps { + coins: Coin[]; + refetch: () => void; +} + +export const CoinsPage: FC = ({ coins, refetch }) => { + const [isEditModalOpen, setIsEditModalOpen] = useState(false); + const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false); + const [selectedCoin, setSelectedCoin] = useState(null); + + const handleEdit = (coin: Coin) => { + setSelectedCoin(coin); + setIsEditModalOpen(true); + }; + + const handleCreate = () => { + setSelectedCoin(null); + setIsEditModalOpen(true); + }; + + const handleDelete = (coin: Coin) => { + setSelectedCoin(coin); + setIsDeleteModalOpen(true); + }; + + return ( +
+
+
+

Coin Management

+

Create, edit, or delete coins from the database.

+
+ +
+ +
+
+
Currency
+
Blockchain
+
Price (USD)
+
Actions
+
+ +
+ {coins.map((coin) => ( +
+
+ {coin.iconFilename ? ( + {coin.currency} + ) : ( +
+ {coin.currency[0]} +
+ )} + {coin.currency} +
+
+ + {coin.blockchain} + +
+
+ ${coin.price.toFixed(4)} +
+
+ + +
+
+ ))} + {coins.length === 0 && ( +
No coins found in database.
+ )} +
+
+ + {isEditModalOpen && ( + setIsEditModalOpen(false)} + onSuccess={() => { + setIsEditModalOpen(false); + refetch(); + }} + /> + )} + + {isDeleteModalOpen && selectedCoin && ( + setIsDeleteModalOpen(false)} + onSuccess={() => { + setIsDeleteModalOpen(false); + refetch(); + }} + /> + )} +
+ ); +}; + +interface EditModalProps { + coin: Coin | null; + onClose: () => void; + onSuccess: () => void; +} + +const EditModal: FC = ({ coin, onClose, onSuccess }) => { + const [formData, setFormData] = useState({ + currency: coin?.currency || '', + blockchain: coin?.blockchain || '', + price: coin?.price?.toString() || '', + iconFilename: coin?.iconFilename || '' + }); + const [loading, setLoading] = useState(false); + const [error, setError] = useState(''); + + const isEdit = !!coin; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + if (!formData.currency || !formData.blockchain || !formData.price) { + setError('Please fill all required fields'); + return; + } + + setLoading(true); + setError(''); + + const payload = { + ...formData, + price: parseFloat(formData.price), + date: coin?.date || new Date().toISOString() + }; + + try { + const url = isEdit ? `http://localhost:3001/api/coins/${coin.id}` : 'http://localhost:3001/api/coins'; + const method = isEdit ? 'PUT' : 'POST'; + + const res = await fetch(url, { + method, + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(payload) + }); + + if (!res.ok) throw new Error('Failed to save coin'); + onSuccess(); + } catch (err: any) { + setError(err.message || 'An error occurred'); + } finally { + setLoading(false); + } + }; + + return ( +
+
+
+

{isEdit ? 'Edit Coin' : 'Add New Coin'}

+ +
+ +
+ {error &&
{error}
} + +
+ + setFormData({...formData, currency: e.target.value.toUpperCase()})} + placeholder="e.g. ETH" + className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-2.5 text-white focus:outline-none focus:border-cyan-500 transition-colors" + /> +
+ +
+ + setFormData({...formData, blockchain: e.target.value})} + placeholder="e.g. Ethereum" + className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-2.5 text-white focus:outline-none focus:border-cyan-500 transition-colors" + /> +
+ +
+ + setFormData({...formData, price: e.target.value})} + placeholder="0.00" + className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-2.5 text-white focus:outline-none focus:border-cyan-500 transition-colors" + /> +
+ +
+ + setFormData({...formData, iconFilename: e.target.value})} + placeholder="e.g. Ethereum.svg" + className="w-full bg-slate-800 border border-slate-700 rounded-lg px-4 py-2.5 text-white focus:outline-none focus:border-cyan-500 transition-colors" + /> +

Must match an SVG file in public/coins directory.

+
+ +
+ + +
+
+
+
+ ); +}; + +interface DeleteModalProps { + coin: Coin; + onClose: () => void; + onSuccess: () => void; +} + +const DeleteModal: FC = ({ coin, onClose, onSuccess }) => { + const [loading, setLoading] = useState(false); + + const handleDelete = async () => { + setLoading(true); + try { + const res = await fetch(`http://localhost:3001/api/coins/${coin.id}`, { + method: 'DELETE' + }); + if (!res.ok) throw new Error('Failed to delete'); + onSuccess(); + } catch (err) { + console.error(err); + setLoading(false); + } + }; + + return ( +
+
+
+
+ +
+
+

Delete Coin?

+

+ Are you sure you want to delete {coin.currency}? This action cannot be undone. +

+
+ + +
+
+
+ ); +}; diff --git a/frontend/src/pages/ScoreboardPage.tsx b/frontend/src/pages/ScoreboardPage.tsx new file mode 100644 index 0000000000..72658759db --- /dev/null +++ b/frontend/src/pages/ScoreboardPage.tsx @@ -0,0 +1,70 @@ +import type { FC } from 'react'; +import type { Score } from '../types'; +import { Trophy, Medal } from 'lucide-react'; + +interface ScoreboardPageProps { + scores: Score[]; + currentUsername?: string; +} + +export const ScoreboardPage: FC = ({ scores, currentUsername }) => { + return ( +
+
+
+ +
+

Top Swappers

+

Compete with others by making swaps to earn points!

+
+ +
+
+
Rank
+
User
+
Score
+
+ +
+ {scores.length === 0 ? ( +
Loading scores...
+ ) : ( + scores.map((score, index) => { + const isCurrentUser = score.username === currentUsername; + return ( +
+ {isCurrentUser && ( +
+ )} + +
+ {index === 0 ? : + index === 1 ? : + index === 2 ? : + {index + 1}} +
+ +
+ + {score.username} + + {isCurrentUser && You} +
+ +
+ {score.score} +
+
+ ); + }) + )} +
+
+
+ ); +}; diff --git a/frontend/src/pages/SwapPage.tsx b/frontend/src/pages/SwapPage.tsx new file mode 100644 index 0000000000..381c475c3e --- /dev/null +++ b/frontend/src/pages/SwapPage.tsx @@ -0,0 +1,253 @@ +import { useState, useEffect, useRef } from 'react'; +import type { FC } from 'react'; +import { Loader2 } from 'lucide-react'; +import type { Coin } from '../types'; +import '../swap.css'; + +interface SwapPageProps { + coins: Coin[]; + sessionToken?: string; + onScoreUpdate?: (newScore: number) => void; +} + +export const SwapPage: FC = ({ coins, sessionToken, onScoreUpdate }) => { + const [isDark, setIsDark] = useState(true); // Default to dark mode for overall app match + + const [fromCoin, setFromCoin] = useState(null); + const [toCoin, setToCoin] = useState(null); + const [amount, setAmount] = useState(''); + const [rate, setRate] = useState(null); + const [loadingRate, setLoadingRate] = useState(false); + const [isSwapping, setIsSwapping] = useState(false); + const [toast, setToast] = useState<{msg: string, type: 'success'|'error'} | null>(null); + + useEffect(() => { + if (coins.length > 1 && !fromCoin && !toCoin) { + setFromCoin(coins.find(c => c.currency === 'ETH') || coins[0]); + setToCoin(coins.find(c => c.currency === 'USDC') || coins[1]); + } + }, [coins]); + + useEffect(() => { + if (fromCoin && toCoin) { + setLoadingRate(true); + fetch(`http://localhost:3001/api/exchange-rate?from=${fromCoin.currency}&to=${toCoin.currency}`) + .then(res => res.json()) + .then(data => { + setRate(data.rate); + setLoadingRate(false); + }) + .catch(() => { + setRate(null); + setLoadingRate(false); + }); + } + }, [fromCoin, toCoin]); + + const handleSwap = async () => { + if (!amount || isNaN(Number(amount)) || Number(amount) <= 0) { + setToast({ msg: 'Please enter a valid amount', type: 'error' }); + return; + } + if (!fromCoin || !toCoin) return; + + setIsSwapping(true); + setToast(null); + + try { + const res = await fetch('http://localhost:3001/api/swap', { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ + fromCoin: fromCoin.currency, + toCoin: toCoin.currency, + amount: Number(amount), + sessionToken + }) + }); + const data = await res.json(); + + if (data.success) { + setToast({ msg: data.message, type: 'success' }); + setAmount(''); + // Propagate server-returned score to parent (App.tsx) + if (data.newScore !== null && data.newScore !== undefined) { + onScoreUpdate?.(data.newScore); + } + } else { + setToast({ msg: data.error || 'Swap failed', type: 'error' }); + } + } catch (e) { + setToast({ msg: 'Network error', type: 'error' }); + } finally { + setIsSwapping(false); + } + }; + + const receivedAmount = rate && amount && !isNaN(Number(amount)) ? (Number(amount) * rate).toFixed(6) : '0.00'; + + return ( +
+ {toast && ( +
+ {toast.msg} +
+ )} + +
+ +
+ +
+ +

Swap Assets

+ +
+
+
You pay
+
+ setAmount(e.target.value)} + /> + + { + if (c.currency === toCoin?.currency) setToCoin(fromCoin); + setFromCoin(c); + }} + /> +
+
+ +
+
{ + setFromCoin(toCoin); + setToCoin(fromCoin); + }} + > + ⇌ +
+
+ +
+
You receive
+
+ + + { + if (c.currency === fromCoin?.currency) setFromCoin(toCoin); + setToCoin(c); + }} + /> +
+
+
+ +
+ Exchange Rate + + {loadingRate ? : null} + {rate && fromCoin && toCoin ? `1 ${fromCoin.currency} = ${rate.toFixed(6)} ${toCoin.currency}` : 'Unavailable'} + +
+ + + +
+
+ ); +}; + +const TokenSelector = ({ coins, selected, onSelect }: { coins: Coin[], selected: Coin | null, onSelect: (c: Coin) => void }) => { + const [open, setOpen] = useState(false); + const dropdownRef = useRef(null); + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + setOpen(false); + } + }; + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, []); + + return ( +
+ + + {open && ( +
+ {coins.map(c => ( + + ))} +
+ )} +
+ ); +}; diff --git a/frontend/src/pages/WalletPage.tsx b/frontend/src/pages/WalletPage.tsx new file mode 100644 index 0000000000..7d2b15a57c --- /dev/null +++ b/frontend/src/pages/WalletPage.tsx @@ -0,0 +1,204 @@ +import { useState, useMemo } from 'react'; +import type { FC } from 'react'; +import type { Coin } from '../types'; + +interface WalletPageProps { + coins: Coin[]; +} + +// Problem 3 Fix: Use a HashMap for O(1) lookup instead of O(n) switch +const BLOCKCHAIN_PRIORITY: Record = { + 'Osmosis': 100, + 'Ethereum': 50, + 'Arbitrum': 30, + 'Zilliqa': 20, + 'Neo': 20 +}; + +// Mock balances since we don't have a real wallet connection +const generateMockBalances = (coins: Coin[]) => { + return coins.map((c, i) => ({ + currency: c.currency, + blockchain: c.blockchain, + amount: i % 3 === 0 ? 0 : Math.random() * 100 + 10 // Every 3rd coin is empty + })); +}; + +export const WalletPage: FC = ({ coins }) => { + const [showComparison, setShowComparison] = useState(false); + + // Create mock balances only once + const balances = useMemo(() => generateMockBalances(coins), [coins]); + + // Problem 3 Fix: Single pass filter, sort, and map using useMemo + const processedBalances = useMemo(() => { + return balances + // 1. Filter: Fix inverted logic (amount > 0) + .filter(balance => { + const priority = BLOCKCHAIN_PRIORITY[balance.blockchain] ?? -99; + return priority > -99 && balance.amount > 0; + }) + // 2. Sort: Fix missing return 0 + .sort((a, b) => { + const priorityA = BLOCKCHAIN_PRIORITY[a.blockchain] ?? -99; + const priorityB = BLOCKCHAIN_PRIORITY[b.blockchain] ?? -99; + if (priorityA > priorityB) return -1; + if (priorityA < priorityB) return 1; + return 0; + }) + // 3. Map to include formatted data + USD value (since we have coins array with prices) + .map(balance => { + const coinInfo = coins.find(c => c.currency === balance.currency); + const price = coinInfo ? coinInfo.price : 0; + const usdValue = price * balance.amount; + + return { + ...balance, + formatted: balance.amount.toFixed(4), + usdValue, + iconFilename: coinInfo?.iconFilename + }; + }); + }, [balances, coins]); // Removed 'prices' dependency as they are inside 'coins' + + return ( +
+
+
+

Your Wallet

+

Refactored version of the Problem 3 component

+
+ +
+ + {showComparison ? ( + + ) : ( +
+ {processedBalances.map((balance) => ( +
+
+ {balance.iconFilename ? ( + {balance.currency} + ) : ( +
+ {balance.currency[0]} +
+ )} +
+

{balance.currency}

+ + {balance.blockchain} + +
+
+
+
{balance.formatted}
+
${balance.usdValue.toFixed(2)}
+
+
+ ))} + {processedBalances.length === 0 && ( +
+ No balances found matching priority criteria. +
+ )} +
+ )} +
+ ); +}; + +const WalletComparison = () => { + return ( +
+
+
+

Before (Original Code)

+
+
{`// 1. O(n) switch statement recreated every render
+const getPriority = (blockchain: any): number => {
+  switch (blockchain) {
+    case 'Osmosis': return 100
+    // ...
+  }
+}
+
+const sortedBalances = useMemo(() => {
+  return balances.filter((balance: WalletBalance) => {
+    // 2. lhsPriority is undefined (should be balancePriority)
+    const balancePriority = getPriority(balance.blockchain);
+    if (lhsPriority > -99) {
+       // 3. Inverted logic: keeps empty balances!
+       if (balance.amount <= 0) {
+         return true;
+       }
+    }
+    return false
+  }).sort((lhs: WalletBalance, rhs: WalletBalance) => {
+    // 4. Missing return 0 for equal cases
+    if (leftPriority > rightPriority) return -1;
+    else if (rightPriority > leftPriority) return 1;
+  });
+// 5. prices is a dependency but not used in the block
+}, [balances, prices]); 
+
+// 6. formattedBalances computed but not used for rows
+const formattedBalances = sortedBalances.map(...)
+
+// 7. key={index} is an anti-pattern
+const rows = sortedBalances.map((balance, index) => {
+  
+})`}
+
+
+ +
+

After (Refactored)

+
+
{`// 1. O(1) HashMap lookup, defined outside component
+const PRIORITY: Record = {
+  'Osmosis': 100, /* ... */
+};
+const getPriority = (chain: string) => PRIORITY[chain] ?? -99;
+
+// 5. Removed unused 'prices' dependency
+// 6. Combined filter, sort, and format into one pass
+const processedBalances = useMemo(() => {
+  return balances
+    .filter(balance => {
+      // 2. Fixed variable name
+      const priority = getPriority(balance.blockchain);
+      // 3. Fixed logic to keep non-empty balances
+      return priority > -99 && balance.amount > 0;
+    })
+    .sort((a, b) => {
+      const pA = getPriority(a.blockchain);
+      const pB = getPriority(b.blockchain);
+      if (pA > pB) return -1;
+      if (pA < pB) return 1;
+      // 4. Added missing return 0
+      return 0;
+    })
+    .map(balance => ({
+      ...balance,
+      formatted: balance.amount.toFixed(4),
+      usdValue: prices[balance.currency] * balance.amount
+    }));
+}, [balances, prices]);
+
+// 7. Used currency as unique key
+const rows = processedBalances.map((balance) => {
+  
+})`}
+
+
+
+
+ ); +}; diff --git a/frontend/src/swap.css b/frontend/src/swap.css new file mode 100644 index 0000000000..4870910f77 --- /dev/null +++ b/frontend/src/swap.css @@ -0,0 +1,238 @@ +/* d:\Test\99tech\frontend\src\swap.css */ + +/* Light Mode Defaults */ +.swap-card { + width: 100%; + max-width: 460px; + background-color: #f6f7f9; + border-radius: 24px; + padding: 30px; + box-shadow: 0 10px 40px rgba(0, 0, 0, 0.4); + position: relative; + transition: all 0.3s ease; + font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; +} + +.card-title { + color: #1a1e29; + font-size: 24px; + font-weight: 600; + margin-bottom: 24px; +} + +.input-section { + background: transparent; + border: 1px solid rgba(0, 0, 0, 0.05); + border-radius: 16px; + padding: 16px 20px; + transition: all 0.3s ease; +} + +.you-pay { margin-bottom: 6px; } +.you-receive { margin-top: 6px; } + +.input-header { + color: #8c909a; + font-size: 14px; + font-weight: 400; + margin-bottom: 8px; +} + +.input-row { + display: flex; + justify-content: space-between; + align-items: center; +} + +.amount-input { + background: transparent; + border: none; + color: #1a1e29; + font-size: 32px; + font-weight: 500; + width: 60%; + outline: none; +} + +.amount-input::placeholder { + color: rgba(0, 0, 0, 0.3); +} + +.token-selector { + background-color: rgba(255, 255, 255, 0.9); + color: #1a1e29; + border-radius: 30px; + padding: 8px 16px; + border: 1px solid rgba(0, 0, 0, 0.1); + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.05); + display: flex; + align-items: center; + cursor: pointer; + font-size: 16px; + font-weight: 500; +} + +.token-icon { + width: 24px; + height: 24px; + border-radius: 50%; + margin-right: 8px; +} + +.token-symbol { + margin-right: 12px; +} + +.dropdown-icon { + font-size: 10px; + color: rgba(0, 0, 0, 0.5); +} + +.swap-icon-container { + display: flex; + justify-content: center; + position: absolute; + top: calc(50% - 24px); + left: 50%; + transform: translateX(-50%); + z-index: 10; +} + +.swap-icon-button { + width: 48px; + height: 48px; + background: #e9ebee; + border: 3px solid #f6f7f9; + border-radius: 50%; + display: flex; + justify-content: center; + align-items: center; + color: #1a1e29; + font-size: 20px; + cursor: pointer; + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1); + transition: transform 0.2s, background 0.3s; +} +.swap-icon-button:hover { + transform: rotate(180deg); +} + +.info-row { + display: flex; + justify-content: space-between; + margin: 20px 0 24px; + padding: 0 4px; + font-size: 14px; + color: #8c909a; +} +.info-value { color: #1a1e29; font-weight: 500; } + +/* Dark Mode Overrides */ +.theme-dark .swap-card { + background: rgba(255, 255, 255, 0.05); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border: 1px solid rgba(255, 255, 255, 0.1); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); +} +.theme-dark .card-title { color: #ffffff; } +.theme-dark .input-section { + background: rgba(0, 0, 0, 0.2); + border: 1px solid rgba(255, 255, 255, 0.05); +} +.theme-dark .input-header { color: rgba(255, 255, 255, 0.6); } +.theme-dark .amount-input { color: #ffffff; } +.theme-dark .amount-input::placeholder { color: rgba(255, 255, 255, 0.3); } +.theme-dark .token-selector { + background: rgba(255, 255, 255, 0.1); + border: 1px solid rgba(255, 255, 255, 0.1); + color: #ffffff; +} +.theme-dark .token-selector:hover { background: rgba(255, 255, 255, 0.15); } +.theme-dark .dropdown-icon { color: rgba(255, 255, 255, 0.5); } +.theme-dark .swap-icon-button { + background: #1a1e27; + border: 3px solid #0d1117; + color: rgba(255, 255, 255, 0.8); + box-shadow: 0 4px 10px rgba(0, 0, 0, 0.5); +} +.theme-dark .info-row { color: rgba(255, 255, 255, 0.6); } +.theme-dark .info-value { color: #ffffff; } + +/* Confirm swap btn styles */ +.confirm-swap-btn { + width: 100%; + height: 56px; + border: none; + border-radius: 16px; + background: linear-gradient(90deg, #37d3e4 0%, #1c8bff 100%); + color: #ffffff; + font-size: 18px; + font-weight: 600; + cursor: pointer; + transition: opacity 0.2s, transform 0.1s; + outline: none; + position: relative; + overflow: hidden; + box-shadow: 0 4px 15px rgba(23, 196, 229, 0.25); +} +.confirm-swap-btn:hover { + opacity: 0.95; + box-shadow: 0 6px 20px rgba(23, 196, 229, 0.35); +} +.confirm-swap-btn:active { + transform: scale(0.98); + box-shadow: 0 2px 10px rgba(23, 196, 229, 0.2); +} +.confirm-swap-btn::before { + content: ''; + position: absolute; + bottom: -30px; + left: -20px; + width: 120px; + height: 120px; + background: radial-gradient(circle, rgba(255, 255, 255, 0.3) 0%, rgba(255, 255, 255, 0) 70%); + border-radius: 50%; + opacity: 0.8; +} +.confirm-swap-btn::after { + content: ''; + position: absolute; + inset: 0; + border-radius: 16px; + pointer-events: none; + box-shadow: inset 2px 2px 5px rgba(11, 89, 102, 0.2), inset -1px -1px 3px rgba(255, 255, 255, 0.1); +} + +/* Theme Toggle */ +.theme-toggle-container { + display: flex; + justify-content: flex-end; + margin-bottom: -15px; + position: relative; + z-index: 20; +} + +.theme-toggle-btn { + background: rgba(0, 0, 0, 0.05); + border: 1px solid rgba(0, 0, 0, 0.08); + padding: 8px; + border-radius: 50%; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + color: #1a1e29; + transition: all 0.2s ease; +} + +.theme-toggle-btn .icon-moon { display: none; } +.theme-toggle-btn .icon-sun { display: block; } + +.theme-dark .theme-toggle-btn { + background: rgba(255, 255, 255, 0.1); + border-color: rgba(255, 255, 255, 0.1); + color: #fff; +} +.theme-dark .theme-toggle-btn .icon-sun { display: none; } +.theme-dark .theme-toggle-btn .icon-moon { display: block; } diff --git a/frontend/src/types/index.ts b/frontend/src/types/index.ts new file mode 100644 index 0000000000..a7342c114c --- /dev/null +++ b/frontend/src/types/index.ts @@ -0,0 +1,18 @@ +export interface Coin { + id: number; + currency: string; + blockchain: string; + price: number; + date: string; + iconFilename: string; +} + +export interface Score { + username: string; + score: number; +} + +export interface AuthSession { + username: string; + token: string; +} diff --git a/frontend/src/utils/sum.ts b/frontend/src/utils/sum.ts new file mode 100644 index 0000000000..105568010a --- /dev/null +++ b/frontend/src/utils/sum.ts @@ -0,0 +1,35 @@ +/** + * Problem 1: Three ways to sum to n + */ + +/** + * Implementation A: Mathematical formula (Gauss) + * Time Complexity: O(1) - Constant time arithmetic operations + * Space Complexity: O(1) - Constant space + */ +export const sum_to_n_a = (n: number): number => { + return (n * (n + 1)) / 2; +}; + +/** + * Implementation B: Iterative approach + * Time Complexity: O(n) - Loops n times + * Space Complexity: O(1) - Constant space for sum variable + */ +export const sum_to_n_b = (n: number): number => { + let sum = 0; + for (let i = 1; i <= n; i++) { + sum += i; + } + return sum; +}; + +/** + * Implementation C: Recursive approach + * Time Complexity: O(n) - n recursive calls + * Space Complexity: O(n) - Call stack grows to n + */ +export const sum_to_n_c = (n: number): number => { + if (n <= 1) return n; + return n + sum_to_n_c(n - 1); +}; diff --git a/frontend/tailwind.config.js b/frontend/tailwind.config.js new file mode 100644 index 0000000000..d37737fc01 --- /dev/null +++ b/frontend/tailwind.config.js @@ -0,0 +1,12 @@ +/** @type {import('tailwindcss').Config} */ +export default { + content: [ + "./index.html", + "./src/**/*.{js,ts,jsx,tsx}", + ], + theme: { + extend: {}, + }, + plugins: [], +} + diff --git a/frontend/tsconfig.app.json b/frontend/tsconfig.app.json new file mode 100644 index 0000000000..3199abe671 --- /dev/null +++ b/frontend/tsconfig.app.json @@ -0,0 +1,25 @@ +{ + "compilerOptions": { + "incremental": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", + "target": "es2022", + "lib": ["ES2023", "DOM"], + "module": "esnext", + "types": ["vite/client"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": false, + "moduleDetection": "force", + "noEmit": true, + "jsx": "react-jsx", + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["src"] +} diff --git a/frontend/tsconfig.json b/frontend/tsconfig.json new file mode 100644 index 0000000000..1ffef600d9 --- /dev/null +++ b/frontend/tsconfig.json @@ -0,0 +1,7 @@ +{ + "files": [], + "references": [ + { "path": "./tsconfig.app.json" }, + { "path": "./tsconfig.node.json" } + ] +} diff --git a/frontend/tsconfig.node.json b/frontend/tsconfig.node.json new file mode 100644 index 0000000000..1b76d9006b --- /dev/null +++ b/frontend/tsconfig.node.json @@ -0,0 +1,24 @@ +{ + "compilerOptions": { + "incremental": true, + "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", + "target": "es2022", + "lib": ["ES2023"], + "module": "esnext", + "types": ["node"], + "skipLibCheck": true, + + /* Bundler mode */ + "moduleResolution": "bundler", + "allowImportingTsExtensions": true, + "verbatimModuleSyntax": true, + "moduleDetection": "force", + "noEmit": true, + + /* Linting */ + "noUnusedLocals": true, + "noUnusedParameters": true, + "noFallthroughCasesInSwitch": true + }, + "include": ["vite.config.ts"] +} diff --git a/frontend/vite.config.ts b/frontend/vite.config.ts new file mode 100644 index 0000000000..8b0f57b91a --- /dev/null +++ b/frontend/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from 'vite' +import react from '@vitejs/plugin-react' + +// https://vite.dev/config/ +export default defineConfig({ + plugins: [react()], +}) diff --git a/readme.md b/readme.md index 1ff4bc95b4..ca967b06d4 100644 --- a/readme.md +++ b/readme.md @@ -1,10 +1,26 @@ -# 99Tech Code Challenge #1 # +This is a PR for the 99tech coding tasks. -Note that if you fork this repository, your responses may be publicly linked to this repo. -Please submit your application along with the solutions attached or linked. +Even though I applied as a frontend developer, I wanted to do the fullstack code challenge as i have experience in both frontend and backend, so I made a minimal but complete project to show the result of the function, UI, and use mockup api in the backend folder to show the result of the function following the problem requirement as I combine the spec together to make a complete project. +If you wanted just to see the minimal solution to each problem you can check the docs folder for each problem and see the readme.md file. +If you wanted to view the full project you can check the frontend and backend folder and run the project locally by using the run.sh. +I also grouped problem1-4 together as they are the same as they have same requirement. -It is important that you minimally attempt the problems, even if you do not arrive at a working solution. +Docs organization: -## Submission ## -You can either provide a link to an online repository, attach the solution in your application, or whichever method you prefer. -We're cool as long as we can view your solution without any pain. +- [docs/problem1and4/](./docs/problem1and4/) + - [readme.md](./docs/problem1and4/readme.md) + - [detail.md](./docs/problem1and4/detail.md) +- [docs/problem2/](./docs/problem2/) + - [readme.md](./docs/problem2/readme.md) + - [detail.md](./docs/problem2/detail.md) +- [docs/problem3/](./docs/problem3/) + - [readme.md](./docs/problem3/readme.md) + - [detail.md](./docs/problem3/detail.md) +- [docs/problem5/](./docs/problem5/) + - [readme.md](./docs/problem5/readme.md) + - [detail.md](./docs/problem5/detail.md) +- [docs/problem6/](./docs/problem6/) + - [readme.md](./docs/problem6/readme.md) + - [detail.md](./docs/problem6/detail.md) +- [frontend/](./frontend/) +- [backend/](./backend/) diff --git a/run.sh b/run.sh new file mode 100644 index 0000000000..15f6cdf403 --- /dev/null +++ b/run.sh @@ -0,0 +1,22 @@ +#!/bin/bash + +# Navigate to the project root +cd "$(dirname "$0")" + +echo "Starting Backend Server..." +(cd backend && npm run dev) & +BACKEND_PID=$! + +echo "Starting Frontend Server..." +(cd frontend && npm run dev) & +FRONTEND_PID=$! + +echo "Both servers are starting up!" +echo "Press [CTRL+C] to stop both servers." + +# Catch CTRL+C and kill both background processes +trap "echo 'Stopping servers...'; kill $BACKEND_PID $FRONTEND_PID; exit" SIGINT + +# Wait for both processes +wait $BACKEND_PID +wait $FRONTEND_PID diff --git a/src/problem1/.keep b/src/problem1/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/problem2/index.html b/src/problem2/index.html deleted file mode 100644 index 4058a68bff..0000000000 --- a/src/problem2/index.html +++ /dev/null @@ -1,27 +0,0 @@ - - - - - Fancy Form - - - - - - - - -
-
Swap
- - - - - - - -
- - - - diff --git a/src/problem2/script.js b/src/problem2/script.js deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/problem2/style.css b/src/problem2/style.css deleted file mode 100644 index 915af91c72..0000000000 --- a/src/problem2/style.css +++ /dev/null @@ -1,8 +0,0 @@ -body { - display: flex; - flex-direction: row; - align-items: center; - justify-content: center; - min-width: 360px; - font-family: Arial, Helvetica, sans-serif; -} diff --git a/src/problem3/.keep b/src/problem3/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/problem4/.keep b/src/problem4/.keep deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/problem5/.keep b/src/problem5/.keep deleted file mode 100644 index e69de29bb2..0000000000