├── waterctl.jpg ├── 终极黑阴宝典.wuff ├── public ├── favicon.ico ├── logo192.png ├── logo512.png ├── service-worker.js ├── manifest.json └── serviceworker.js ├── .prettierrc.json ├── postcss.config.js ├── src ├── logger.ts ├── styles.css ├── vite-env.d.ts ├── writeValueLogging.ts ├── utils.ts ├── utils.spec.ts ├── algorithms.ts ├── algorithms.spec.ts ├── pwaHelper.ts ├── index.ts ├── payloads.ts ├── errors.ts ├── solvers.ts ├── solvers.spec.ts ├── bluetooth.ts └── deputy.wat ├── .gitignore ├── vite.config.js ├── tsconfig.json ├── .github └── workflows │ └── deploy.yml ├── package.json ├── LICENSE ├── README.md ├── FAQ.md ├── index.html └── pnpm-lock.yaml /waterctl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celesWuff/waterctl/HEAD/waterctl.jpg -------------------------------------------------------------------------------- /终极黑阴宝典.wuff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celesWuff/waterctl/HEAD/终极黑阴宝典.wuff -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celesWuff/waterctl/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celesWuff/waterctl/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/celesWuff/waterctl/HEAD/public/logo512.png -------------------------------------------------------------------------------- /.prettierrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "trailingComma": "all", 3 | "printWidth": 160, 4 | "tabWidth": 2, 5 | "semi": true, 6 | "singleQuote": false 7 | } 8 | -------------------------------------------------------------------------------- /postcss.config.js: -------------------------------------------------------------------------------- 1 | import purgecss from "@fullhuman/postcss-purgecss"; 2 | 3 | export default { 4 | plugins: [ 5 | purgecss({ 6 | content: ["./index.html"], 7 | }), 8 | ], 9 | }; 10 | -------------------------------------------------------------------------------- /src/logger.ts: -------------------------------------------------------------------------------- 1 | const logs: string[] = []; 2 | 3 | export function log(message: string) { 4 | console.log(message); 5 | logs.push(message); 6 | } 7 | 8 | export function clearLogs() { 9 | logs.length = 0; 10 | } 11 | 12 | export function isLogEmpty() { 13 | return logs.length === 0; 14 | } 15 | 16 | export function getLogs() { 17 | return Object.freeze(logs); 18 | } 19 | -------------------------------------------------------------------------------- /public/service-worker.js: -------------------------------------------------------------------------------- 1 | // this is the kill-switch for service workers of the old versions 2 | // this file won't be accessed by the current version 3 | 4 | if ("serviceWorker" in navigator) { 5 | navigator.serviceWorker.getRegistrations().then(function (registrations) { 6 | for (let registration of registrations) { 7 | registration.unregister(); 8 | } 9 | }); 10 | } 11 | -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | body { 2 | background-color: #fafafa; 3 | } 4 | 5 | .main { 6 | position: absolute; 7 | left: 50%; 8 | top: 50%; 9 | -webkit-transform: translate(-50%, -50%); 10 | transform: translate(-50%, -50%); 11 | display: flex; 12 | align-items: center; 13 | flex-direction: column; 14 | } 15 | 16 | .misc { 17 | position: absolute; 18 | bottom: 0; 19 | left: 0; 20 | margin: 10px; 21 | } 22 | 23 | .unsupported { 24 | display: none; 25 | } 26 | 27 | .csdn-warning { 28 | display: none; 29 | } 30 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | 25 | # cloudflare workers 26 | /dist 27 | /workers-site 28 | wrangler.toml 29 | 30 | # deputy 31 | /src/deputy.wasm 32 | -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import { viteSingleFile } from "vite-plugin-singlefile"; 3 | import { createHtmlPlugin } from "vite-plugin-html"; 4 | 5 | export default defineConfig({ 6 | define: { 7 | VERSION: JSON.stringify(process.env.npm_package_version), 8 | }, 9 | build: { 10 | sourcemap: true, 11 | rollupOptions: { 12 | output: { 13 | entryFileNames: `[name].js`, 14 | chunkFileNames: `[name].js`, 15 | assetFileNames: `[name].[ext]`, 16 | }, 17 | }, 18 | }, 19 | plugins: [viteSingleFile(), createHtmlPlugin({ minify: true })], 20 | }); 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "useDefineForClassFields": true, 5 | "module": "ESNext", 6 | "lib": ["ES2020", "DOM", "DOM.Iterable"], 7 | "skipLibCheck": true, 8 | 9 | /* Bundler mode */ 10 | "moduleResolution": "bundler", 11 | "allowImportingTsExtensions": true, 12 | "resolveJsonModule": true, 13 | "isolatedModules": true, 14 | "moduleDetection": "force", 15 | "noEmit": true, 16 | 17 | /* Linting */ 18 | "strict": true, 19 | "noUnusedLocals": true, 20 | "noUnusedParameters": true, 21 | "noFallthroughCasesInSwitch": true 22 | }, 23 | "include": ["src"] 24 | } 25 | -------------------------------------------------------------------------------- /src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare const VERSION: string; 4 | 5 | declare interface Window { 6 | deferredPrompt: BeforeInstallPromptEvent | null; 7 | } 8 | 9 | interface BeforeInstallPromptEvent extends Event { 10 | readonly platforms: string[]; 11 | readonly userChoice: Promise<{ 12 | outcome: "accepted" | "dismissed"; 13 | platform: string; 14 | }>; 15 | prompt(): Promise; 16 | } 17 | 18 | interface WindowEventMap { 19 | beforeinstallprompt: BeforeInstallPromptEvent; 20 | } 21 | 22 | // see solvers.ts 23 | declare module "node:fs" { 24 | export function readFileSync(path: string | URL, options?: { encoding?: null; flag?: string; }): Buffer; 25 | } 26 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "蓝牙水控器", 3 | "name": "蓝牙水控器 FOSS", 4 | "categories": ["utilities"], 5 | "icons": [ 6 | { 7 | "src": "favicon.ico", 8 | "sizes": "32x32 24x24 16x16", 9 | "type": "image/x-icon" 10 | }, 11 | { 12 | "src": "logo192.png", 13 | "type": "image/png", 14 | "sizes": "192x192", 15 | "purpose": "any maskable" 16 | }, 17 | { 18 | "src": "logo512.png", 19 | "type": "image/png", 20 | "sizes": "512x512", 21 | "purpose": "any maskable" 22 | } 23 | ], 24 | "start_url": ".", 25 | "display": "standalone", 26 | "background_color": "#fafafa", 27 | "theme_color": "#fafafa", 28 | "orientation": "any" 29 | } 30 | -------------------------------------------------------------------------------- /src/writeValueLogging.ts: -------------------------------------------------------------------------------- 1 | import { log } from "./logger"; 2 | import { bufferToHexString } from "./utils"; 3 | 4 | // .writeValue logging 5 | // @ts-expect-error: BluetoothRemoteGATTCharacteristic is a function in browsers, but TypeScript has its own thought... 6 | if (typeof BluetoothRemoteGATTCharacteristic !== "undefined") { 7 | // @ts-expect-error: same as above 8 | const originalWriteValue = BluetoothRemoteGATTCharacteristic.prototype.writeValue; 9 | // @ts-expect-error: same as above 10 | BluetoothRemoteGATTCharacteristic.prototype.writeValue = function (value: ArrayBuffer) { 11 | const msg = "TXD: " + bufferToHexString(value); 12 | log(msg); 13 | return originalWriteValue.apply(this, arguments); 14 | }; 15 | } 16 | -------------------------------------------------------------------------------- /public/serviceworker.js: -------------------------------------------------------------------------------- 1 | const cacheName = 'files'; 2 | 3 | addEventListener('fetch', fetchEvent => { 4 | const request = fetchEvent.request; 5 | if (request.method !== 'GET' || !request.url.startsWith(self.location.origin)) { 6 | return; 7 | } 8 | fetchEvent.respondWith(async function () { 9 | const responseFromFetch = fetch(request); 10 | fetchEvent.waitUntil(async function () { 11 | const responseCopy = (await responseFromFetch).clone(); 12 | const myCache = await caches.open(cacheName); 13 | await myCache.put(request, responseCopy); 14 | }()); 15 | const responseFromCache = await caches.match(request); 16 | return responseFromCache || responseFromFetch; 17 | }()); 18 | }); -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | export function bufferToHexString(array: ArrayBuffer): string { 2 | // explanation: [0x12, 0x34, 0xAB, 0xCD] => "1234ABCD" 3 | return Array.from(new Uint8Array(array)) 4 | .map((x) => x.toString(16).padStart(2, "0")) 5 | .join("") 6 | .toUpperCase(); 7 | } 8 | 9 | export function decAsHex(n: number): number { 10 | // explanation: 42 => 0x42 11 | return n <= 0 ? 0 : n % 10 | (decAsHex((n / 10) | 0) << 4); 12 | } 13 | 14 | export function isCsdn() { 15 | const userAgent = navigator.userAgent.toLowerCase(); 16 | const referer = document.referrer.toLowerCase(); 17 | return /csdnapp/i.test(userAgent) || /csdn.net/i.test(referer) || /gitcode.com/i.test(referer); 18 | } 19 | 20 | // だって思考と錯誤のモンスター 21 | // それ僕のこと? いや皆のこと! 22 | // 一緒になって踊ろうよ、さ! 23 | -------------------------------------------------------------------------------- /src/utils.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import { bufferToHexString, decAsHex } from "./utils"; 3 | 4 | test("bufferToHexString", () => { 5 | expect(bufferToHexString(new Uint8Array([0x00, 0x01, 0x02, 0x03]).buffer)).toBe("00010203"); 6 | expect(bufferToHexString(new Uint8Array([0x12, 0x34, 0xab, 0xcd]).buffer)).toBe("1234ABCD"); 7 | expect(bufferToHexString(new Uint8Array([0xfe, 0xfe, 0x09, 0xaf]).buffer)).toBe("FEFE09AF"); 8 | expect(bufferToHexString(new Uint8Array([0xcf, 0x75, 0x1b, 0x42]).buffer)).toBe("CF751B42"); 9 | }); 10 | 11 | test("decAsHex", () => { 12 | expect(decAsHex(0)).toBe(0x00); 13 | expect(decAsHex(1)).toBe(0x01); 14 | expect(decAsHex(9)).toBe(0x09); 15 | expect(decAsHex(10)).toBe(0x10); 16 | expect(decAsHex(11)).toBe(0x11); 17 | expect(decAsHex(19)).toBe(0x19); 18 | expect(decAsHex(99)).toBe(0x99); 19 | }); 20 | -------------------------------------------------------------------------------- /.github/workflows/deploy.yml: -------------------------------------------------------------------------------- 1 | name: Deploy 2 | on: 3 | push: 4 | branches: 5 | - 2.x 6 | workflow_dispatch: 7 | 8 | jobs: 9 | build: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - name: Checkout 13 | uses: actions/checkout@v4 14 | 15 | - name: Install Node.js 16 | uses: actions/setup-node@v4 17 | with: 18 | node-version: 22 19 | 20 | - name: Build 21 | run: | 22 | npm install 23 | npm run build 24 | 25 | - name: Upload 26 | uses: actions/upload-pages-artifact@v3 27 | with: 28 | path: dist 29 | 30 | deploy: 31 | runs-on: ubuntu-latest 32 | needs: build 33 | permissions: 34 | pages: write 35 | id-token: write 36 | environment: 37 | name: github-pages 38 | url: ${{ steps.deployment.outputs.page_url }} 39 | steps: 40 | - name: Deploy 41 | id: deployment 42 | uses: actions/deploy-pages@v4 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "waterctl", 3 | "private": true, 4 | "version": "2.1.4", 5 | "type": "module", 6 | "scripts": { 7 | "compile-wasm": "wat2wasm ./src/deputy.wat -o ./src/deputy.wasm", 8 | "dev": "npm run compile-wasm && vite", 9 | "build": "npm run compile-wasm && tsc && vite build", 10 | "preview": "vite preview", 11 | "test": "vitest" 12 | }, 13 | "devDependencies": { 14 | "@fullhuman/postcss-purgecss": "^6.0.0", 15 | "@types/web-bluetooth": "^0.0.21", 16 | "typescript": "^5.8.3", 17 | "vite": "^6.3.2", 18 | "vite-plugin-html": "^3.2.2", 19 | "vite-plugin-singlefile": "^2.2.0", 20 | "vitest": "^3.1.1", 21 | "wabt": "^1.0.37" 22 | }, 23 | "dependencies": { 24 | "@sentry/browser": "^9.13.0", 25 | "water.css": "^2.1.1" 26 | }, 27 | "packageManager": "pnpm@10.8.1+sha512.c50088ba998c67b8ca8c99df8a5e02fd2ae2e2b29aaf238feaa9e124248d3f48f9fb6db2424949ff901cffbb5e0f0cc1ad6aedb602cd29450751d11c35023677" 28 | } 29 | -------------------------------------------------------------------------------- /src/algorithms.ts: -------------------------------------------------------------------------------- 1 | // CRC-16/ChangGong 2 | // width=16 poly=0x8005 init=0xe808 refin=true refout=true xorout=0x0000 3 | export function crc16changgong(str: string): number { 4 | let crc = 0x1017; 5 | for (let i = 0; i < str.length; i++) { 6 | crc ^= str.charCodeAt(i); 7 | for (let j = 0; j < 8; j++) { 8 | if ((crc & 0x0001) == 1) { 9 | crc >>= 1; 10 | crc ^= 0xa001; 11 | } else crc >>= 1; 12 | } 13 | } 14 | return crc; 15 | } 16 | 17 | // CRC-16/CGAEAF (abbrev for ChangGong AE/AF) 18 | // width=16 poly=0x8005 init=0xf856 refin=true refout=true xorout=0x0075 19 | // truncated to lower 8 bits 20 | export function crc16cgaeaf(array: Uint8Array): number { 21 | let crc = 0x6a1f; 22 | for (let i = 0; i < array.length; i++) { 23 | crc ^= array[i]; 24 | for (let j = 0; j < 8; j++) { 25 | if ((crc & 0x0001) == 1) { 26 | crc >>= 1; 27 | crc ^= 0xa001; 28 | } else crc >>= 1; 29 | } 30 | } 31 | crc = (crc ^ 0x75) & 0xff; 32 | return crc; 33 | } 34 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 celesWuff 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/algorithms.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import { crc16changgong, crc16cgaeaf } from "./algorithms"; 3 | 4 | test("crc16changgong", () => { 5 | expect(crc16changgong("11070")).toBe(0xd015); 6 | expect(crc16changgong("25333")).toBe(0x21e2); 7 | expect(crc16changgong("33982")).toBe(0x9b39); 8 | expect(crc16changgong("45130")).toBe(0xe08b); 9 | expect(crc16changgong("50312")).toBe(0x8d97); 10 | }); 11 | 12 | test("crc16cgaeaf", () => { 13 | expect(crc16cgaeaf(new Uint8Array([0x00, 0x00, 0x05, 0x95, 0x27, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12]))).toBe(0x38); 14 | expect(crc16cgaeaf(new Uint8Array([0x00, 0x9b, 0x05, 0x95, 0x27, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12]))).toBe(0xa2); 15 | expect(crc16cgaeaf(new Uint8Array([0x39, 0x9b, 0x05, 0x95, 0x27, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12]))).toBe(0x5b); 16 | expect(crc16cgaeaf(new Uint8Array([0x00, 0xff, 0xff, 0x95, 0x27, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12]))).toBe(0x9f); 17 | expect(crc16cgaeaf(new Uint8Array([0x00, 0x00, 0x01, 0xcf, 0x75, 0x31, 0xd4, 0xfe, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]))).toBe(0x69); 18 | }); 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 🛀 蓝牙水控器 FOSS 2 | 3 | 深圳市常工电子“蓝牙水控器”控制程序的开源实现。适用于国内各大高校宿舍热水器。 4 | 5 | ![waterctl](waterctl.jpg) 6 | 7 | ## 限时活动:waterctl KOTH Challenge 8 | 9 | https://github.com/celesWuff/waterctl/issues/41 10 | 11 | ## 🏃 使用 12 | 13 | - 🌎 开始使用: https://celeswuff.github.io/waterctl/ 14 | - 不能用?请先看看”疑难解答“: https://github.com/celesWuff/waterctl/blob/2.x/FAQ.md 15 | 16 | ## ✨ 特性 17 | 18 | - 🌐 真正离线使用,不依赖互联网连接(你可以在离线状态下打开本应用的链接) 19 | - 🖕 完全脱离“微信”,夺回对科技的控制权 20 | - ⚛️ 使用开放的 Web 技术构建 21 | - 💡 简洁,明确,美观的操作界面 22 | - ⚡ 响应速度极快 23 | - 🔥 简化的交互逻辑 24 | - 🖥️ 支持 PWA,可在 Chrome/Edge 里点击地址栏右边的 ➕ 安装到电脑或手机 25 | - 📱 支持 Windows/Linux/macOS/Android/iOS/ChromeOS 26 | - 👍 开放源代码 27 | - 🛠 更多特性开发中 28 | 29 | ## ♿ 引流位 30 | 31 | - [celesWuff/wateremu](https://github.com/celesWuff/wateremu) 蓝牙水控器 模拟器 32 | - [FudanDeDRM](https://gist.github.com/celesWuff/f54c02c2d73c40f9250c21fdc6fb4630) Demo Code for Retrieving Later Publicly Published, Internally Accessible Dissertations and Theses from Fudan University 33 | - [celesWuff/drinkctl](https://github.com/celesWuff/drinkctl) “点点” app 扫码饮水的开源实现 34 | - [celesWuff/ktpWarp](https://github.com/celesWuff/ktpwarp-server) 课堂派自动签到 35 | 36 | ## 📜 开源许可 37 | 38 | 基于 [MIT license](https://opensource.org/licenses/MIT) 许可进行开源。 39 | -------------------------------------------------------------------------------- /src/pwaHelper.ts: -------------------------------------------------------------------------------- 1 | export const registerServiceWorker = () => { 2 | if (navigator.serviceWorker && !navigator.serviceWorker.controller && location.protocol === "https:") { 3 | navigator.serviceWorker.register("serviceworker.js"); 4 | } 5 | }; 6 | 7 | // pwa install prompt 8 | export const setupInstallButton = () => { 9 | const installButton = document.getElementById("install-button")!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! as HTMLButtonElement; 10 | 11 | window.addEventListener("beforeinstallprompt", (event) => { 12 | event.preventDefault(); 13 | window.deferredPrompt = event; 14 | installButton.hidden = false; 15 | }); 16 | 17 | installButton.addEventListener("click", async () => { 18 | const promptEvent = window.deferredPrompt; 19 | if (!promptEvent) { 20 | return; 21 | } 22 | promptEvent.prompt(); 23 | // @ts-expect-error: we don't care about the result 24 | const result = await promptEvent.userChoice; 25 | window.deferredPrompt = null; 26 | installButton.hidden = true; 27 | }); 28 | 29 | window.addEventListener("appinstalled", () => { 30 | window.deferredPrompt = null; 31 | }); 32 | }; 33 | 34 | // auto resize for desktop 35 | export const resizeWindow = () => { 36 | window.resizeTo(538, 334); 37 | }; 38 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import "water.css/out/light.min.css"; 2 | import "./styles.css"; 3 | import "./writeValueLogging"; 4 | import { handleButtonClick } from "./bluetooth"; 5 | import { registerServiceWorker, resizeWindow, setupInstallButton } from "./pwaHelper"; 6 | import * as Sentry from "@sentry/browser"; 7 | import { isCsdn } from "./utils"; 8 | 9 | Sentry.init({ 10 | dsn: "https://17d03841e2244d53abdbe587434efd5c@glitchtip.celeswuff.science/1", 11 | }); 12 | 13 | (document.getElementById("version") as HTMLSpanElement).innerText = " · v" + VERSION; 14 | 15 | if (!navigator.bluetooth) { 16 | (document.querySelector(".supported") as HTMLElement).style.display = "none"; 17 | (document.querySelector(".unsupported") as HTMLElement).style.display = "block"; 18 | } 19 | 20 | if (isCsdn()) { 21 | (document.querySelector(".supported") as HTMLElement).style.display = "none"; 22 | (document.querySelector(".unsupported") as HTMLElement).style.display = "none"; 23 | (document.querySelector(".csdn-warning") as HTMLElement).style.display = "block"; 24 | } 25 | 26 | document.addEventListener("DOMContentLoaded", () => { 27 | const mainButton = document.getElementById("main-button") as HTMLButtonElement; 28 | mainButton.addEventListener("click", handleButtonClick); 29 | }); 30 | 31 | // PWA 32 | registerServiceWorker(); 33 | setupInstallButton(); 34 | resizeWindow(); 35 | -------------------------------------------------------------------------------- /src/payloads.ts: -------------------------------------------------------------------------------- 1 | // send this first, then wait for B0, B1, or AE 2 | // B0 and B1 contain the status of the previous session, AE is a key authentication request 3 | // AE will only show up in new firmwares 4 | // prettier-ignore 5 | export const startPrologue = new Uint8Array([ 6 | 0xFE, 0xFE, 0x09, 0xB0, 7 | 0x01, 0x01, 0x00, 0x00 8 | ]); 9 | 10 | // when ending the session, send this then wait for B3 11 | // B3 contains the status of this session 12 | // prettier-ignore 13 | export const endPrologue = new Uint8Array([ 14 | 0xFE, 0xFE, 0x09, 0xB3, 15 | 0x00, 0x00 16 | ]); 17 | 18 | // after receiving B3, send this then disconnect 19 | // prettier-ignore 20 | export const endEpilogue = new Uint8Array([ 21 | 0xFE, 0xFE, 0x09, 0xB4, 22 | 0x00, 0x00 23 | ]); 24 | 25 | // send this before startEpilogueOfflinebomb and before endEpilogue, or if BC is received 26 | // used to clear the previous unfinished offline (BB) session 27 | // receiving BC is a strong signal that sending this is necessary, 28 | // but if Offlinebomb will be used it will be safer to send this before any other command 29 | // (deprecated, for reference only) 30 | // prettier-ignore 31 | export const offlinebombFix = new Uint8Array([ 32 | 0xFE, 0xFE, 0x09, 0xBC, 33 | 0x00, 0x00 34 | ]); 35 | 36 | // send this if BA is received 37 | // BA is related to user info uploading, but we will never actually upload anything 38 | // prettier-ignore 39 | export const baAck = new Uint8Array([ 40 | 0xFE, 0xFE, 0x09, 0xBA, 41 | 0x00, 0x00 42 | ]); 43 | -------------------------------------------------------------------------------- /FAQ.md: -------------------------------------------------------------------------------- 1 | # 疑难解答 2 | 3 | ## “不支持的浏览器” 4 | 5 | 我们推荐使用**最新的** Google Chrome 或 Microsoft Edge 浏览器。您也可以自行试验其他您常用的浏览器。 6 | 7 | 如果您在意隐私或广告,可以选择 Cromite 或 Vivaldi。 8 | 9 | 注意,受 Apple 限制, iOS 上的 Chrome 仍无法使用蓝牙功能。 10 | 您需要前往 App Store 下载 [Bluefy](https://apps.apple.com/us/app/id1492822055) 浏览器以使用蓝牙水控器 FOSS。 11 | 12 | ### 已测试可用的浏览器 13 | 14 | | 桌面(Windows/Linux/macOS) | Android | iOS | ChromeOS | 15 | | --------------------------- | -------------- | ------ | ------------ | 16 | | Google Chrome | Google Chrome | Bluefy | 您有得选吗? | 17 | | Microsoft Edge | Microsoft Edge | | | 18 | | Chromium | Cromite | | | 19 | | Vivaldi | Vivaldi | | | 20 | | Arc | 三星浏览器 | | | 21 | | | Ecosia | | | 22 | | | Kiwi Browser | | | 23 | 24 | **强烈不建议使用国产浏览器。** 25 | 26 | ## “未授予蓝牙权限。” 27 | 28 | ### 桌面(Windows/Linux/macOS) 29 | 30 | 请按顺序检查以下项目: 31 | - 电脑已安装蓝牙硬件。 32 | - 蓝牙驱动工作正常。 33 | - 已在系统设置中开启蓝牙。 34 | - 已在浏览器中为蓝牙水控器 FOSS 授权蓝牙权限。 35 | 36 | ### Android 37 | 38 | 请在手机设置中,为浏览器授予蓝牙权限。 39 | 40 | 在 Android 系统中,该权限被称作“附近设备”权限。 41 | 42 | > [!NOTE] 43 | > 注意:在 Android 11 及更低版本中,访问蓝牙设备的权限被归类在“位置信息”权限当中。因此,在旧版本 Android 我们可能需要您授予这一权限。 44 | 45 | 默认情况下,浏览器会提示用户授权附近设备(或位置信息)权限。但在一部分定制的 Android 系统中,这一权限默认是“否”,需要用户手动到系统设置中授权。 46 | 47 | 我们承诺不会利用这一权限对您进行跟踪或定位。 48 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 蓝牙水控器 FOSS 12 | 13 | 14 | 15 |
16 | 17 | 18 |

未连接

19 |
20 |
21 |

不支持的浏览器

22 |

需要支持蓝牙的浏览器,如 Chrome 或 Edge。

23 |

24 | 25 |

26 |
27 |
28 |

您正在从假冒网站访问

29 |

任何 CSDN 或 GitCode 上的“蓝牙水控器 FOSS”均为假冒。

30 |

蓝牙水控器 FOSS 已停止运作。若要开始使用,请从我们的真实 GitHub 仓库重新进入:

31 |

32 | https://github.com/celesWuff/waterctl 33 |

34 |
35 |
36 | 37 |

38 | 源代码 · 39 | 疑难解答 40 | 41 |
42 | copyright (c) 2025 celesWuff, licensed under MIT License 43 |

44 |
45 | 46 |
出现错误
47 |
48 |

49 |
50 | 51 | 52 | 53 |
54 |
55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/errors.ts: -------------------------------------------------------------------------------- 1 | interface ErrorCase { 2 | isHit: (msg: string) => boolean; 3 | output: (container: HTMLElement, error: unknown) => void; 4 | isFatal: boolean; 5 | showLogs: boolean; 6 | } 7 | 8 | const errorCases: ErrorCase[] = [ 9 | { 10 | isHit: (msg) => !!msg.match(/WATERCTL INTERNAL Unknown RXD data/), 11 | output: (container) => { 12 | container.innerText = "接收到未知数据。可能不影响使用。\n\n这可能是一个 Bug,请截图并"; 13 | container.innerHTML += "反馈给开发者。"; 14 | }, 15 | isFatal: false, 16 | showLogs: true, 17 | }, 18 | { 19 | isHit: (msg) => !!msg.match(/WATERCTL INTERNAL (?:Refused|Bad key)/), 20 | output: (container) => { 21 | container.innerText = 22 | "水控器拒绝启动。\n\n" + 23 | "蓝牙水控器 FOSS 当前不支持您的水控器,请不要重试,多次失败可能造成水控器锁定。\n" + 24 | "若发生锁定,水控器将拒绝一切传入连接;在通电状态下等待约一小时方可恢复。\n\n" + 25 | "请截图并"; 26 | container.innerHTML += "反馈给开发者。"; 27 | }, 28 | isFatal: true, 29 | showLogs: true, 30 | }, 31 | { 32 | isHit: (msg) => !!msg.match(/WATERCTL INTERNAL Operation timed out/), 33 | output: (container) => { 34 | container.innerText = "等待时间似乎太长了。\n\n如果该问题反复发生,这可能是一个 Bug,请截图并"; 35 | container.innerHTML += "反馈给开发者。"; 36 | }, 37 | isFatal: false, 38 | showLogs: true, 39 | }, 40 | { 41 | isHit: (msg) => !!msg.match(/No Services matching UUID|GATT Error: Not supported/), 42 | output: (container) => { 43 | container.innerText = "不支持的机型。\n\n如果您有能力,欢迎一同参与蓝牙水控器 FOSS 的开发。\n详情请参考"; 44 | container.innerHTML += "源代码仓库。"; 45 | }, 46 | isFatal: true, 47 | showLogs: false, 48 | }, 49 | { 50 | isHit: (msg) => !!msg.match(/User denied the browser permission|Web Bluetooth is not supported|Bluetooth adapter not available/), 51 | output: (container) => { 52 | container.innerText = "设备不支持蓝牙,或浏览器权限未开启。\n\n请参考"; 53 | container.innerHTML += "“疑难解答”,按步骤逐步排查。"; 54 | }, 55 | isFatal: true, 56 | showLogs: false, 57 | }, 58 | { 59 | isHit: (msg) => !!msg.match(/NetworkError|GATT operation failed|GATT Error Unknown/), 60 | output: (container) => { 61 | container.innerText = "连接不稳定,与水控器通信失败。\n请重试。"; 62 | }, 63 | isFatal: true, 64 | showLogs: false, 65 | }, 66 | { 67 | isHit: () => true, 68 | output: (container, error) => { 69 | container.innerText = error + "\n\n是什么呢\n\n(这可能是一个 Bug,请截图并"; 70 | container.innerHTML += "反馈给开发者。)"; 71 | }, 72 | isFatal: true, 73 | showLogs: true, 74 | }, 75 | ]; 76 | 77 | export function resolveError(error: unknown): ErrorCase { 78 | for (const c of errorCases) { 79 | if (c.isHit(error!.toString())) { 80 | return c; 81 | } 82 | } 83 | 84 | throw new Error(`WATERCTL INTERNAL Unhandled: ${error}`); 85 | } 86 | -------------------------------------------------------------------------------- /src/solvers.ts: -------------------------------------------------------------------------------- 1 | import { crc16cgaeaf, crc16changgong } from "./algorithms"; 2 | import { decAsHex } from "./utils"; 3 | import deputy from "./deputy.wasm?init"; 4 | 5 | // Vitest + WASM is a PITA: using the dynamic imports below is a workaround to get Vitest to work, but will make prod builds bigger 6 | // Comment the line above and uncomment the lines below when you need to run Vitest 7 | // A workaround for workaround! 8 | // See also: https://github.com/vitest-dev/vitest/discussions/4283 9 | 10 | // const deputy: () => Promise = async () => { 11 | // if (import.meta.env.SSR) { 12 | // const fs = await import("node:fs"); 13 | // const wasmBuffer = fs.readFileSync(new URL("./deputy.wasm", import.meta.url)); 14 | // const wasmModule = new WebAssembly.Module(wasmBuffer); 15 | // const instance = await WebAssembly.instantiate(wasmModule); 16 | // return instance; 17 | // } else { 18 | // const module = await import("./deputy.wasm?init") 19 | // return await module.default(); 20 | // } 21 | // } 22 | 23 | function makeRandomUserId(): Uint8Array { 24 | // explanation: XYZW (decimal) => [0xXY, 0xZW] 25 | const randomIdNumber = Math.floor(Math.random() * 9999) + 1; 26 | return new Uint8Array([randomIdNumber >> 8, randomIdNumber & 0xff]).map(decAsHex); 27 | } 28 | 29 | function makeDatetimeArray(): number[] { 30 | // explanation: 2013/1/11 12:34:56 => [0x13, 0x01, 0x11, 0x12, 0x34, 0x56] 31 | const now = new Date().toLocaleString("zh-CN", { timeZone: "Asia/Shanghai" }); 32 | const date = now 33 | .split(" ")[0] 34 | .split("/") 35 | .map((x) => parseInt(x)); 36 | const time = now 37 | .split(" ")[1] 38 | .split(":") 39 | .map((x) => parseInt(x)); 40 | const datetimeArrayDecimal = [date[0] % 100, ...date.slice(1), ...time]; 41 | return datetimeArrayDecimal.map(decAsHex); 42 | } 43 | 44 | interface Deputy { 45 | makeKey: ($0: number, $1: number, $2: number, $3: number) => void; 46 | memory: WebAssembly.Memory; 47 | } 48 | 49 | async function makeUnlockKey(array: Uint8Array): Promise { 50 | const { makeKey, memory } = (await deputy()).exports as unknown as Deputy; 51 | if (array.length !== 4) throw new Error("WATERCTL INTERNAL Bad unlock request"); 52 | // @ts-expect-error: we have already checked the length 53 | makeKey(...array); 54 | const key = new Uint32Array(memory.buffer)[524]; 55 | return new Uint8Array([(key >> 24) & 0xff, (key >> 16) & 0xff, (key >> 8) & 0xff, key & 0xff]); 56 | } 57 | 58 | export async function makeUnlockResponse(unlockRequestBuffer: ArrayBuffer, deviceName: string): Promise { 59 | const unlockRequest = new Uint8Array(unlockRequestBuffer); 60 | const unknownByte = unlockRequest[5]; // unknown, but we only need to echo it back 61 | const nonceBytes = unlockRequest.slice(6, 8); // not nonce in crypto sense, it's an auto-incrementing number and used for key calculation 62 | const mac = unlockRequest.slice(8, 10); // last 2 bytes of the MAC address 63 | 64 | // [0x00, 0x01] => 0x0001 => (add 1) => 0x0002 => [0x00, 0x02] 65 | const nonce = (nonceBytes[0] << 8) | nonceBytes[1]; 66 | const newNonce = nonce + 1; 67 | const newNonceBytes = nonce === 0xffff ? new Uint8Array([0x01, 0x00]) : new Uint8Array([(newNonce >> 8) & 0xff, newNonce & 0xff]); // bug-for-bug compatible 68 | 69 | const rawKey = await makeUnlockKey(new Uint8Array([...nonceBytes, ...mac])); // 2 bytes of nonce, 2 bytes of MAC 70 | 71 | const mask = deviceName 72 | .slice(-4) 73 | .split("") 74 | .map((x) => x.charCodeAt(0) - 0x30); 75 | const key = rawKey.map((x, i) => x ^ mask[i]); 76 | 77 | // prettier-ignore 78 | const checksumInput = new Uint8Array([ 79 | unknownByte, 80 | ...newNonceBytes, // 2 bytes of nonce 81 | ...key, // 4 bytes of key 82 | 0xFE, 0x87, 0x00, 0x00, 83 | 0x00, 0x00, 0x00, 0x00 84 | ]); 85 | 86 | const checksum = crc16cgaeaf(checksumInput); 87 | 88 | // prettier-ignore 89 | const finalPayload = new Uint8Array([ 90 | 0xFE, 0xFE, 0x09, 0xAF, 91 | checksum, ...checksumInput // 1 byte of checksum, 15 bytes of the rest 92 | ]); 93 | 94 | return finalPayload; 95 | } 96 | 97 | // the one true command to begin a session 98 | export function makeStartEpilogue(deviceName: string, isKeyAuthPresent = false): Uint8Array { 99 | const checksum = crc16changgong(deviceName.slice(-5)); 100 | const mn = isKeyAuthPresent ? 0x0b : 0xff; // magic number 101 | const ri = makeRandomUserId(); 102 | const dt = makeDatetimeArray(); 103 | // prettier-ignore 104 | return new Uint8Array([ 105 | 0xFE, 0xFE, 0x09, 0xB2, 106 | 0x01, checksum & 0xFF, checksum >> 8, mn, 107 | 0x00, ...ri, ...dt, // 2 bytes of random user id, 6 bytes of datetime 108 | 0x0F, 0x27, 0x00 109 | ]); 110 | } 111 | 112 | // Offlinebomb exploit 113 | // (deprecated, for reference only) 114 | export function makeStartEpilogueOfflinebomb(): Uint8Array { 115 | const ri = makeRandomUserId(); 116 | const dt = makeDatetimeArray(); 117 | // prettier-ignore 118 | return new Uint8Array([ 119 | 0xFE, 0xFE, 0x09, 0xBB, 120 | 0x01, 0x01, 0x0D, 0x00, 121 | 0x50, ...ri, ...dt, // 2 bytes of random user id, 6 bytes of datetime 122 | 0x00, 0x20, 0x00 123 | ]); 124 | } 125 | -------------------------------------------------------------------------------- /src/solvers.spec.ts: -------------------------------------------------------------------------------- 1 | import { expect, test } from "vitest"; 2 | import { makeUnlockResponse, makeStartEpilogue, makeStartEpilogueOfflinebomb } from "./solvers"; 3 | import { bufferToHexString } from "./utils"; 4 | import { crc16changgong } from "./algorithms"; 5 | 6 | const deviceName = "Water33982"; 7 | 8 | function formatDateToString(date: Date): string { 9 | const year = date.getFullYear().toString().slice(-2); 10 | const month = (date.getMonth() + 1).toString().padStart(2, "0"); 11 | const day = date.getDate().toString().padStart(2, "0"); 12 | const hours = date.getHours().toString().padStart(2, "0"); 13 | const minutes = date.getMinutes().toString().padStart(2, "0"); 14 | const seconds = date.getSeconds().toString().padStart(2, "0"); 15 | return `${year}${month}${day}${hours}${minutes}${seconds}`; 16 | } 17 | 18 | test("makeUnlockResponse", async () => { 19 | expect( 20 | await makeUnlockResponse( 21 | new Uint8Array([0xfd, 0xfd, 0x09, 0xae, 0x38, 0x00, 0x00, 0x05, 0x95, 0x27, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12]).buffer, 22 | deviceName, 23 | ), 24 | ).toEqual(new Uint8Array([0xfe, 0xfe, 0x09, 0xaf, 0x72, 0x00, 0x00, 0x06, 0xc3, 0x50, 0x9a, 0xc4, 0xfe, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); 25 | 26 | expect( 27 | await makeUnlockResponse( 28 | new Uint8Array([0xfd, 0xfd, 0x09, 0xae, 0xa2, 0x00, 0x9b, 0x05, 0x95, 0x27, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12]).buffer, 29 | deviceName, 30 | ), 31 | ).toEqual(new Uint8Array([0xfe, 0xfe, 0x09, 0xaf, 0x44, 0x00, 0x9b, 0x06, 0xf1, 0x30, 0x9a, 0xd3, 0xfe, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); 32 | 33 | expect( 34 | await makeUnlockResponse( 35 | new Uint8Array([0xfd, 0xfd, 0x09, 0xae, 0x5b, 0x39, 0x9b, 0x05, 0x95, 0x27, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12]).buffer, 36 | deviceName, 37 | ), 38 | ).toEqual(new Uint8Array([0xfe, 0xfe, 0x09, 0xaf, 0xbd, 0x39, 0x9b, 0x06, 0xf1, 0x30, 0x9a, 0xd3, 0xfe, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); 39 | 40 | expect( 41 | await makeUnlockResponse( 42 | new Uint8Array([0xfd, 0xfd, 0x09, 0xae, 0x9f, 0x00, 0xff, 0xff, 0x95, 0x27, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12]).buffer, 43 | deviceName, 44 | ), 45 | ).toEqual(new Uint8Array([0xfe, 0xfe, 0x09, 0xaf, 0xd9, 0x00, 0x01, 0x00, 0xd6, 0xac, 0x3f, 0x3b, 0xfe, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); 46 | 47 | expect( 48 | await makeUnlockResponse( 49 | new Uint8Array([0xfd, 0xfd, 0x09, 0xae, 0xb3, 0x00, 0x34, 0x64, 0x80, 0x14, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12]).buffer, 50 | deviceName, 51 | ), 52 | ).toEqual(new Uint8Array([0xfe, 0xfe, 0x09, 0xaf, 0xb1, 0x00, 0x34, 0x65, 0xd6, 0x28, 0x9a, 0xc1, 0xfe, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); 53 | 54 | expect( 55 | await makeUnlockResponse( 56 | new Uint8Array([0xfd, 0xfd, 0x09, 0xae, 0x01, 0x00, 0x81, 0xff, 0x68, 0x30, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12]).buffer, 57 | deviceName, 58 | ), 59 | ).toEqual(new Uint8Array([0xfe, 0xfe, 0x09, 0xaf, 0x68, 0x00, 0x82, 0x00, 0xf1, 0x12, 0xb2, 0x21, 0xfe, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); 60 | 61 | expect( 62 | await makeUnlockResponse( 63 | new Uint8Array([0xfd, 0xfd, 0x09, 0xae, 0xe5, 0x00, 0x01, 0xff, 0x73, 0x24, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12]).buffer, 64 | deviceName, 65 | ), 66 | ).toEqual(new Uint8Array([0xfe, 0xfe, 0x09, 0xaf, 0x4b, 0x00, 0x02, 0x00, 0xcc, 0xca, 0x9a, 0x20, 0xfe, 0x87, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00])); 67 | }); 68 | 69 | test("makeStartEpilogue Old Firmware", () => { 70 | const response = makeStartEpilogue(deviceName); 71 | 72 | expect(response.slice(0, 5)).toEqual(new Uint8Array([0xfe, 0xfe, 0x09, 0xb2, 0x01])); 73 | 74 | const checksum = response[5] | (response[6] << 8); 75 | expect(checksum).toEqual(crc16changgong(deviceName.slice(-5))); 76 | 77 | expect(response.slice(7, 9)).toEqual(new Uint8Array([0xff, 0x00])); 78 | 79 | const randomUserId = response.slice(9, 11); 80 | expect(randomUserId).toHaveLength(2); 81 | 82 | const datetimeString = bufferToHexString(response.slice(11, 17).buffer); 83 | expect(datetimeString).toEqual(formatDateToString(new Date())); 84 | 85 | const footer = response.slice(17, 20); 86 | expect(footer).toEqual(new Uint8Array([0x0f, 0x27, 0x00])); 87 | }); 88 | 89 | test("makeStartEpilogue New Firmware", () => { 90 | const response = makeStartEpilogue(deviceName, true); 91 | 92 | expect(response.slice(0, 5)).toEqual(new Uint8Array([0xfe, 0xfe, 0x09, 0xb2, 0x01])); 93 | 94 | const checksum = response[5] | (response[6] << 8); 95 | expect(checksum).toEqual(crc16changgong(deviceName.slice(-5))); 96 | 97 | expect(response.slice(7, 9)).toEqual(new Uint8Array([0x0b, 0x00])); 98 | 99 | const randomUserId = response.slice(9, 11); 100 | expect(randomUserId).toHaveLength(2); 101 | 102 | const datetimeString = bufferToHexString(response.slice(11, 17).buffer); 103 | expect(datetimeString).toEqual(formatDateToString(new Date())); 104 | 105 | const footer = response.slice(17, 20); 106 | expect(footer).toEqual(new Uint8Array([0x0f, 0x27, 0x00])); 107 | }); 108 | 109 | test("makeStartEpilogueOfflinebomb", () => { 110 | const response = makeStartEpilogueOfflinebomb(); 111 | 112 | const header = response.slice(0, 9); 113 | expect(header).toEqual(new Uint8Array([0xfe, 0xfe, 0x09, 0xbb, 0x01, 0x01, 0x0d, 0x00, 0x50])); 114 | 115 | const randomUserId = response.slice(9, 11); 116 | expect(randomUserId).toHaveLength(2); 117 | 118 | const datetimeString = bufferToHexString(response.slice(11, 17).buffer); 119 | expect(datetimeString).toEqual(formatDateToString(new Date())); 120 | 121 | const footer = response.slice(17, 20); 122 | expect(footer).toEqual(new Uint8Array([0x00, 0x20, 0x00])); 123 | }); 124 | -------------------------------------------------------------------------------- /src/bluetooth.ts: -------------------------------------------------------------------------------- 1 | import { resolveError } from "./errors"; 2 | import { clearLogs, getLogs, isLogEmpty, log } from "./logger"; 3 | import { endEpilogue, baAck, offlinebombFix, startPrologue, endPrologue } from "./payloads"; 4 | import { makeStartEpilogue, makeUnlockResponse } from "./solvers"; 5 | import { bufferToHexString } from "./utils"; 6 | 7 | let bluetoothDevice: BluetoothDevice; 8 | let txdCharacteristic: BluetoothRemoteGATTCharacteristic; 9 | let rxdCharacteristic: BluetoothRemoteGATTCharacteristic; 10 | 11 | let isStarted = false; 12 | 13 | let pendingStartEpilogue: number; // workaround for determining new firmware, see handleRxdNotifications 14 | let pendingTimeoutMessage: number; // if we don't get a response in time, we should show an error message 15 | 16 | // I'm really sorry to have a DOM manipulation here, but this is the best way unless going down the rabbit hole of React (or any other frameworks you like) 17 | // And... of course we had done that in the past (v0.x) 18 | function updateUi(stage: "pending" | "ok" | "standby") { 19 | const mainButton = document.getElementById("main-button") as HTMLButtonElement; 20 | const deviceName = document.getElementById("device-name") as HTMLSpanElement; 21 | 22 | switch (stage) { 23 | case "pending": 24 | mainButton.innerText = "请稍候"; 25 | mainButton.disabled = true; 26 | deviceName.innerText = "已连接:" + bluetoothDevice.name; 27 | break; 28 | case "ok": 29 | mainButton.innerText = "结束"; 30 | mainButton.disabled = false; 31 | break; 32 | case "standby": 33 | mainButton.innerText = "开启"; 34 | mainButton.disabled = false; 35 | deviceName.innerText = "未连接"; 36 | break; 37 | } 38 | } 39 | 40 | async function disconnect() { 41 | if (bluetoothDevice) bluetoothDevice.gatt!.disconnect(); 42 | isStarted = false; 43 | clearLogs(); 44 | clearTimeout(pendingStartEpilogue); 45 | clearTimeout(pendingTimeoutMessage); 46 | updateUi("standby"); 47 | } 48 | 49 | async function handleBluetoothError(error: unknown) { 50 | // this is so fucking ugly but i have no choice 51 | // you would never know how those shitty browsers behave 52 | if (!error) throw error; 53 | 54 | const e = error.toString(); 55 | 56 | if (e.match(/User cancelled/) || e == "2") { 57 | // "2" is a weird behavior of Bluefy browser on iOS 58 | return; 59 | } 60 | 61 | const dialogContent = document.getElementById("dialog-content") as HTMLParagraphElement; 62 | const dialogDebugContainer = document.getElementById("dialog-debug-container") as HTMLPreElement; 63 | const dialogDebugContent = document.getElementById("dialog-debug-content")!; 64 | 65 | const { output, isFatal, showLogs } = resolveError(error); 66 | output(dialogContent, error); 67 | 68 | dialogDebugContainer.style.display = "none"; 69 | if (!isLogEmpty() && showLogs) { 70 | dialogDebugContainer.style.display = "block"; 71 | dialogDebugContent.innerText = "调试信息:\n" + getLogs().join("\n"); 72 | } 73 | 74 | (document.getElementById("dialog") as HTMLDialogElement).showModal(); 75 | 76 | if (isFatal) disconnect(); 77 | 78 | throw error; 79 | } 80 | 81 | async function handleRxdNotifications(event: Event) { 82 | const value = (event.target! as BluetoothRemoteGATTCharacteristic).value!; 83 | 84 | log("RXD: " + bufferToHexString(value.buffer)); 85 | 86 | try { 87 | let payload = new Uint8Array(value.buffer); 88 | 89 | // due to a bug in the firmware, it may send an AT command "AT+STAS?" via RXD; it doesn't start with FDFD09 90 | if (payload[0] === 0x41 && payload[1] === 0x54 && payload[2] === 0x2b) { 91 | return; 92 | } 93 | 94 | if (payload[0] !== 0xfd && payload[0] !== 0x09) { 95 | throw new Error("WATERCTL INTERNAL Unknown RXD data"); 96 | } 97 | 98 | // sometimes, the first one or two bytes are missing maybe due to bad firmware implementation 99 | // explanation: [0xFD, 0x09, ...] => [0xFD, 0xFD, 0x09, ...] 100 | if (payload[1] === 0x09) { 101 | payload = new Uint8Array([0xfd, ...payload]); 102 | } 103 | 104 | // explanation: [0x09, ...] => [0xFD, 0xFD, 0x09, ...] 105 | if (payload[0] === 0x09) { 106 | payload = new Uint8Array([0xfd, 0xfd, ...payload]); 107 | } 108 | 109 | // ... and sometimes it sends a single byte 0xFD 110 | if (payload.length < 4) { 111 | return; 112 | } 113 | 114 | const dType = payload[3]; 115 | 116 | // https://github.com/prettier/prettier/issues/5158 117 | // prettier-ignore 118 | switch (dType) { 119 | case 0xB0: // start prologue ok; delay 500ms for key authentication request (AE) if this is a new firmware 120 | case 0xB1: 121 | clearTimeout(pendingStartEpilogue); 122 | pendingStartEpilogue = setTimeout(() => { 123 | txdCharacteristic.writeValue(makeStartEpilogue(bluetoothDevice.name!)); 124 | }, 500); 125 | break; 126 | case 0xAE: // receiving an unlock request (AE), this is a new firmware 127 | clearTimeout(pendingStartEpilogue); 128 | await txdCharacteristic.writeValue(await makeUnlockResponse(payload, bluetoothDevice.name!)); 129 | break; 130 | case 0xAF: 131 | switch (payload[5]) { 132 | case 0x55: // key authentication ok; continue to send start epilogue (B2) 133 | await txdCharacteristic.writeValue(makeStartEpilogue(bluetoothDevice.name!, true)); 134 | break; 135 | case 0x01: // key authentication failed; "err41" (bad key) 136 | case 0x02: // ? 137 | case 0x04: // "err43" (bad nonce) 138 | throw new Error("WATERCTL INTERNAL Bad key"); 139 | default: 140 | await txdCharacteristic.writeValue(makeStartEpilogue(bluetoothDevice.name!, true)); 141 | throw new Error("WATERCTL INTERNAL Unknown RXD data"); 142 | } 143 | break; 144 | case 0xB2: // start ok; update ui 145 | clearTimeout(pendingStartEpilogue); 146 | clearTimeout(pendingTimeoutMessage); 147 | isStarted = true; 148 | updateUi("ok"); 149 | break; 150 | case 0xB3: // end prologue ok (B3); disconnect 151 | await txdCharacteristic.writeValue(endEpilogue); 152 | disconnect(); 153 | break; 154 | case 0xAA: // telemetry, no need to respond 155 | case 0xB5: // temperature settings related, no need to respond 156 | case 0xB8: // unknown, no need to respond 157 | break; 158 | case 0xBA: // user info upload request; send BA ack to tell it we have done that (won't actually do it) 159 | await txdCharacteristic.writeValue(baAck); 160 | break; 161 | case 0xBC: // see offlinebombFix 162 | await txdCharacteristic.writeValue(offlinebombFix); 163 | break; 164 | case 0xC8: // start epilogue (B2) is refused 165 | throw new Error("WATERCTL INTERNAL Refused"); 166 | default: 167 | throw new Error("WATERCTL INTERNAL Unknown RXD data"); 168 | } 169 | } catch (error) { 170 | handleBluetoothError(error); 171 | } 172 | } 173 | 174 | function setupTimeoutMessage() { 175 | if (!pendingTimeoutMessage) { 176 | pendingTimeoutMessage = setTimeout(() => { 177 | handleBluetoothError("WATERCTL INTERNAL Operation timed out"); 178 | }, 15000); 179 | } 180 | } 181 | 182 | async function start() { 183 | try { 184 | bluetoothDevice = await navigator.bluetooth.requestDevice({ 185 | // https://github.com/WebBluetoothCG/web-bluetooth/issues/234 186 | filters: Array.from("0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ").map((c) => ({ namePrefix: c })), 187 | optionalServices: [window.navigator.userAgent.match(/Bluefy/) ? "generic_access" : 0xf1f0], // workaround for Bluefy 188 | }); 189 | 190 | updateUi("pending"); 191 | 192 | const server = await bluetoothDevice.gatt!.connect(); 193 | const service = await server.getPrimaryService(0xf1f0); 194 | txdCharacteristic = await service.getCharacteristic(0xf1f1); 195 | rxdCharacteristic = await service.getCharacteristic(0xf1f2); 196 | 197 | await rxdCharacteristic.startNotifications(); 198 | rxdCharacteristic.addEventListener("characteristicvaluechanged", handleRxdNotifications); 199 | 200 | await txdCharacteristic.writeValue(startPrologue); 201 | setupTimeoutMessage(); 202 | } catch (error) { 203 | handleBluetoothError(error); 204 | } 205 | } 206 | 207 | async function end() { 208 | try { 209 | await txdCharacteristic.writeValue(endPrologue); 210 | setupTimeoutMessage(); 211 | } catch (error) { 212 | handleBluetoothError(error); 213 | } 214 | } 215 | 216 | export function handleButtonClick() { 217 | isStarted ? end() : start(); 218 | } 219 | -------------------------------------------------------------------------------- /src/deputy.wat: -------------------------------------------------------------------------------- 1 | ;; 2 | ;; This file is part of waterctl 3 | ;; Copyright (c) 2021-2024 celesWuff, Deputy 4 | ;; 5 | ;; Permission is hereby granted, free of charge, to any person obtaining a copy 6 | ;; of this software and associated documentation files (the "Software"), to deal 7 | ;; in the Software without restriction, including without limitation the rights 8 | ;; to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | ;; copies of the Software, and to permit persons to whom the Software is 10 | ;; furnished to do so, subject to the following conditions: 11 | ;; 12 | ;; The above copyright notice and this permission notice shall be included in all 13 | ;; copies or substantial portions of the Software. 14 | ;; 15 | ;; THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | ;; IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | ;; FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | ;; AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | ;; LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | ;; OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | ;; SOFTWARE. 22 | ;; 23 | 24 | ;; 25 | ;; To compile this file, you need to install the WebAssembly Binary Toolkit (wabt) 26 | ;; You can install it using the following command: 27 | ;; sudo apt install wabt 28 | ;; 29 | ;; Then you can compile this file using the following command: 30 | ;; wat2wasm deputy.wat 31 | ;; 32 | 33 | (module 34 | (memory $BEYOND_THE_MEMORIES 1) 35 | (export "memory" (memory $BEYOND_THE_MEMORIES)) 36 | ;; BPM 088 37 | 38 | (data (i32.const 0) 39 | "\00\04\05\00\01\05\04\00\01\05\04\01\00\04\05\00" 40 | "\01\05\04\01\00\04\05\01\00\04\05\00\01\05\04\00" 41 | "\01\05\04\01\00\04\05\01\00\04\05\00\01\05\04\01" 42 | "\00\04\05\00\01\05\04\00\01\05\04\01\00\04\05\00" 43 | "\01\05\04\01\00\04\05\01\00\04\05\00\01\05\04\01" 44 | "\00\04\05\00\01\05\04\00\01\05\04\01\00\04\05\01" 45 | "\00\04\05\00\01\05\04\00\01\05\04\01\00\04\05\00" 46 | "\01\05\04\01\00\04\05\01\00\04\05\00\01\05\04\00" 47 | "\01\05\04\01\00\04\05\01\00\04\05\00\01\05\04\01" 48 | "\00\04\05\00\01\05\04\00\01\05\04\01\00\04\05\01" 49 | "\00\04\05\00\01\05\04\00\01\05\04\01\00\04\05\00" 50 | "\01\05\04\01\00\04\05\01\00\04\05\00\01\05\04\01" 51 | "\00\04\05\00\01\05\04\00\01\05\04\01\00\04\05\00" 52 | "\01\05\04\01\00\04\05\01\00\04\05\00\01\05\04\00" 53 | "\01\05\04\01\00\04\05\01\00\04\05\00\01\05\04\01" 54 | "\00\04\05\00\01\05\04\00\01\05\04\01\00\04\05\00" 55 | "\00\40\81\c0\01\41\80\c0\01\41\80\c1\00\40\81\c0" 56 | "\01\41\80\c1\00\40\81\c1\00\40\81\c0\01\41\80\c0" 57 | "\01\41\80\c1\00\40\81\c1\00\40\81\c0\01\41\80\c1" 58 | "\00\40\81\c0\01\41\80\c0\01\41\80\c1\00\40\81\c0" 59 | "\01\41\80\c1\00\40\81\c1\00\40\81\c0\01\41\80\c1" 60 | "\00\40\81\c0\01\41\80\c0\01\41\80\c1\00\40\81\c1" 61 | "\00\40\81\c0\01\41\80\c0\01\41\80\c1\00\40\81\c0" 62 | "\01\41\80\c1\00\40\81\c1\00\40\81\c0\01\41\80\c0" 63 | "\01\41\80\c1\00\40\81\c1\00\40\81\c0\01\41\80\c1" 64 | "\00\40\81\c0\01\41\80\c0\01\41\80\c1\00\40\81\c1" 65 | "\00\40\81\c0\01\41\80\c0\01\41\80\c1\00\40\81\c0" 66 | "\01\41\80\c1\00\40\81\c1\00\40\81\c0\01\41\80\c1" 67 | "\00\40\81\c0\01\41\80\c0\01\41\80\c1\00\40\81\c0" 68 | "\01\41\80\c1\00\40\81\c1\00\40\81\c0\01\41\80\c0" 69 | "\01\41\80\c1\00\40\81\c1\00\40\81\c0\01\41\80\c1" 70 | "\00\40\81\c0\01\41\80\c0\01\41\80\c1\00\40\81\c0" 71 | "\00\01\01\00\02\03\03\02\07\06\06\07\05\04\04\05" 72 | "\0d\0c\0c\0d\0f\0e\0e\0f\0a\0b\0b\0a\08\09\09\08" 73 | "\19\18\18\19\1b\1a\1a\1b\1e\1f\1f\1e\1c\1d\1d\1c" 74 | "\14\15\15\14\16\17\17\16\13\12\12\13\11\10\10\11" 75 | "\31\30\30\31\33\32\32\33\36\37\37\36\34\35\35\34" 76 | "\3c\3d\3d\3c\3e\3f\3f\3e\3b\3a\3a\3b\39\38\38\39" 77 | "\28\29\29\28\2a\2b\2b\2a\2f\2e\2e\2f\2d\2c\2c\2d" 78 | "\25\24\24\25\27\26\26\27\22\23\23\22\20\21\21\20" 79 | "\61\60\60\61\63\62\62\63\66\67\67\66\64\65\65\64" 80 | "\6c\6d\6d\6c\6e\6f\6f\6e\6b\6a\6a\6b\69\68\68\69" 81 | "\78\79\79\78\7a\7b\7b\7a\7f\7e\7e\7f\7d\7c\7c\7d" 82 | "\75\74\74\75\77\76\76\77\72\73\73\72\70\71\71\70" 83 | "\50\51\51\50\52\53\53\52\57\56\56\57\55\54\54\55" 84 | "\5d\5c\5c\5d\5f\5e\5e\5f\5a\5b\5b\5a\58\59\59\58" 85 | "\49\48\48\49\4b\4a\4a\4b\4e\4f\4f\4e\4c\4d\4d\4c" 86 | "\44\45\45\44\46\47\47\46\43\42\42\43\41\40\40\41" 87 | "\00\01\01\03\03\02\02\06\06\07\07\05\05\04\04\00" 88 | "\00\01\c1\03\c3\c2\02\06\c6\c7\07\c5\05\04\c4\0c" 89 | "\cc\cd\0d\cf\0f\0e\ce\ca\0a\0b\cb\09\c9\c8\08\18" 90 | "\d8\d9\19\db\1b\1a\da\de\1e\1f\df\1d\dd\dc\1c\d4" 91 | "\14\15\d5\17\d7\d6\16\12\d2\d3\13\d1\11\10\d0\30" 92 | "\f0\f1\31\f3\33\32\f2\f6\36\37\f7\35\f5\f4\34\fc" 93 | "\3c\3d\fd\3f\ff\fe\3e\3a\fa\fb\3b\f9\39\38\f8\e8" 94 | "\28\29\e9\2b\eb\ea\2a\2e\ee\ef\2f\ed\2d\2c\ec\24" 95 | "\e4\e5\25\e7\27\26\e6\e2\22\23\e3\21\e1\e0\20\60" 96 | "\a0\a1\61\a3\63\62\a2\a6\66\67\a7\65\a5\a4\64\ac" 97 | "\6c\6d\ad\6f\af\ae\6e\6a\aa\ab\6b\a9\69\68\a8\b8" 98 | "\78\79\b9\7b\bb\ba\7a\7e\be\bf\7f\bd\7d\7c\bc\74" 99 | "\b4\b5\75\b7\77\76\b6\b2\72\73\b3\71\b1\b0\70\90" 100 | "\50\51\91\53\93\92\52\56\96\97\57\95\55\54\94\5c" 101 | "\9c\9d\5d\9f\5f\5e\9e\9a\5a\5b\9b\59\99\98\58\48" 102 | "\88\89\49\8b\4b\4a\8a\8e\4e\4f\8f\4d\8d\8c\4c\84" 103 | "\44\45\85\47\87\86\46\42\82\83\43\81\41\40\80\50" 104 | "\00\90\60\f0\c0\50\a0\30\c0\50\a0\30\00\90\60\f0" 105 | "\c0\50\a0\30\00\90\60\f0\00\90\60\f0\c0\50\a0\30" 106 | "\c0\50\a0\30\00\90\60\f0\00\90\60\f0\c0\50\a0\30" 107 | "\00\90\60\f0\c0\50\a0\30\c0\50\a0\30\00\90\60\f0" 108 | "\c0\50\a0\30\00\90\60\f0\00\90\60\f0\c0\50\a0\30" 109 | "\00\90\60\f0\c0\50\a0\30\c0\50\a0\30\00\90\60\f0" 110 | "\00\90\60\f0\c0\50\a0\30\c0\50\a0\30\00\90\60\f0" 111 | "\c0\50\a0\30\00\90\60\f0\00\90\60\f0\c0\50\a0\30" 112 | "\c0\50\a0\30\00\90\60\f0\00\90\60\f0\c0\50\a0\30" 113 | "\00\90\60\f0\c0\50\a0\30\c0\50\a0\30\00\90\60\f0" 114 | "\00\90\60\f0\c0\50\a0\30\c0\50\a0\30\00\90\60\f0" 115 | "\c0\50\a0\30\00\90\60\f0\00\90\60\f0\c0\50\a0\30" 116 | "\00\90\60\f0\c0\50\a0\30\c0\50\a0\30\00\90\60\f0" 117 | "\c0\50\a0\30\00\90\60\f0\00\90\60\f0\c0\50\a0\30" 118 | "\c0\50\a0\30\00\90\60\f0\00\90\60\f0\c0\50\a0\30" 119 | "\00\90\60\f0\c0\50\a0\30\c0\50\a0\30\00\90\60\f0" 120 | "\00\01\02\03\04\05\06\07\08\09\0a\0b\0c\0d\0e\0f" 121 | "\10\11\12\13\11\14\11\15\16\17\18\19\1a\1b\1c\1d" 122 | "\13\1e\1f\20\21\22\0f\23\18\21\24\10\23\25\26\1f" 123 | "\22\27\28\29\2a\2b\2c\2d\25\20\0f\0c\13\2e\2f\2a" 124 | "\30\17\31\32\33\34\35\36\37\38\03\39\3a\0d\24\39" 125 | "\27\22\25\06\3b\3c\3d\3e\3f\05\0f\40\41\42\43\3a" 126 | "\44\45\46\2f\1e\05\2b\47\13\48\49\4a\4b\1f\1b\3e" 127 | "\4c\4d\20\0e\4c\4e\4f\50\51\52\31\1e\49\53\22\54" 128 | "\18\55\56\10\57\58\59\5a\42\24\0d\5b\33\5c\5d\14" 129 | "\20\5e\43\5f\1d\56\55\0b\3a\60\61\41\62\39\37\63" 130 | "\64\04\06\65\08\66\67\68\11\20\69\17\61\1b\1e\6a" 131 | "\31\16\6b\36\6c\07\38\6d\6e\3c\52\6f\26\41\70\27" 132 | "\71\5a\34\44\72\73\01\4c\0a\6a\16\1c\74\19\16\2b" 133 | "\51\4e\75\12\5c\3d\15\5d\02\22\53\4f\11\69\76\29" 134 | "\09\59\77\1b\78\44\79\7a\7b\05\0f\7c\2b\01\7d\2c" 135 | "\55\06\05\7e\0c\7f\80\0f\00\1a\81\03\31\1e\3c\78" 136 | "\00\01\02\03\00\04\05\06\07\08\09\0a\0b\0c\0d\0e" 137 | "\03\0f\10\11\12\13\14\15\16\17\18\19\1a\1b\1c\1d" 138 | "\16\1e\1f\20\21\22\23\24\25\26\14\27\28\29\2a\2b" 139 | "\2c\1c\2d\22\27\0c\2e\2f\30\31\32\33\34\35\36\33" 140 | "\13\34\02\37\38\39\3a\3b\3c\1a\14\3d\04\3e\3f\40" 141 | "\41\42\1e\43\44\45\0d\46\47\48\49\48\24\4a\48\4b" 142 | "\1f\4c\1b\4d\4b\01\4e\45\4d\13\1c\4e\35\1f\27\24" 143 | "\4f\28\50\24\37\13\35\1b\51\52\26\4f\53\54\1d\1c" 144 | "\26\46\44\55\42\46\56\39\33\3f\57\58\2b\09\59\5a" 145 | "\53\5b\5c\48\31\2d\1c\3e\5d\47\17\5e\4a\5f\32\2f" 146 | "\52\60\3f\26\61\13\14\62\63\64\2b\65\1e\66\67\3a" 147 | "\65\18\27\0c\04\68\43\40\13\69\6a\37\6b\14\1a\6c" 148 | "\49\5d\5e\0a\6d\02\01\10\59\67\6e\6f\4b\70\6c\45" 149 | "\71\06\00\72\11\73\74\03\38\6c\70\41\75\0f\76\35" 150 | "\56\27\1e\73\0d\5c\35\48\77\78\79\3b\7a\1a\3d\7b" 151 | "\1d\7c\11\4c\7d\2e\18\7e\7f\05\3c\80\07\81\46\0c" 152 | "\75\37\52\21\f6\d5\48\d8\a0\b9\bc\cc\92\39\54\f3" 153 | "\f9\3e\e2\57\a3\5b\c6\55\d1\97\32\66\c5\64\c0\34" 154 | "\65\c9\51\bd\61\d7\12\45\72\6a\ac\59\20\ca\5f\30" 155 | "\c1\63\67\cd\58\35\23\5c\5a\d4\f2\5d\a7\14\f5\60" 156 | "\38\46\c4\a6\68\40\27\a2\1c\a9\d3\41\31\91\f8\d0" 157 | "\04\0c\c7\89\24\56\d2\42\18\43\f0\0a\a4\19\1b\36" 158 | "\b7\cf\22\6d\d9\f7\16\49\15\3d\a1\62\53\3a\13\a8" 159 | "\f4\c8\ba\eb\1e\44\6f\10\c3\0f\a5\50\d6\69\00\f1" 160 | "\77\e9\cf\f2\a2\d5\b7\46\c0\59\20\43\45\4C\45\53" 161 | "\d4\61\39\f2\21\5a\5c\23\35\58\cd\67\63\55\c1\a6" 162 | "\c4\46\38\f3\d5\60\f5\14\a7\5d\48\d7\51\45\66\34" 163 | "\41\d3\a9\1c\57\a2\59\c0\30\27\40\68\24\89\c7\0c" 164 | "\04\d0\f8\31\54\65\91\92\cc\bc\b9\a0\d8\f6\52\37" 165 | "\75\64\c5\32\97\d1\c6\5b\3e\a3\e2\f9\12\bd\c9\ac" 166 | "\5f\ca\20\6a\72\1e\a1\eb\ba\f0\c8\6f\3d\19\a4\44" 167 | "\69\d6\50\a5\0f\c3\10\43\e9\77\f1\00\56\0a\18\42" 168 | "\d2\6d\22\cf\b7\36\1b\15\49\16\f7\d9\f4\a8\13\3a" 169 | "\53\62\31\eb\f3\d7\ba\37\65\92\57\55\46\46\3C\33" 170 | ;; 2096 bytes (131 * 16 bytes) 171 | ) 172 | 173 | (func $DEPUTY_EXTERNAL_SOMETHING_WHATEVER_PLEASE_USE_OR_CELESWUFF_WILL_CRY (param $a i32) (param $b i32) (param $c i32) (param $d i32) 174 | (local $iA i32) 175 | (local $iB i32) 176 | (local $iC i32) 177 | (local $iD i32) 178 | (local $m i32) 179 | 180 | (if (i32.and (i32.eq (local.get $b) (i32.const 255)) (i32.eq (i32.and (local.get $a) (i32.const 1)) (i32.const 1))) 181 | (then 182 | (local.set $a (i32.add (local.get $a) (i32.const 2))) 183 | ) 184 | ) 185 | (if (i32.eq (local.get $a) (i32.const 257)) 186 | (then 187 | (local.set $a (i32.const 0)) 188 | ) 189 | ) 190 | (local.set $iA (i32.and (local.get $d) (i32.const 7))) 191 | (local.set $m (i32.and (local.get $d) (i32.xor (i32.const -1) (i32.const 7)))) 192 | (if (i32.and (local.get $m) (i32.shl (i32.const 1) (i32.const 3))) 193 | (then 194 | (local.set $iA (i32.xor (local.get $iA) (i32.const 4))) 195 | ) 196 | ) 197 | (if (i32.and (local.get $m) (i32.shl (i32.const 1) (i32.const 4))) 198 | (then 199 | (local.set $iA (i32.xor (local.get $iA) (i32.const 4))) 200 | ) 201 | ) 202 | (if (i32.and (local.get $m) (i32.shl (i32.const 1) (i32.const 5))) 203 | (then 204 | (local.set $iA (i32.xor (local.get $iA) (i32.const 4))) 205 | ) 206 | ) 207 | (if (i32.and (local.get $m) (i32.shl (i32.const 1) (i32.const 6)) 208 | ) 209 | (then 210 | (local.set $iA (i32.xor (local.get $iA) (i32.const 4))) 211 | ) 212 | ) 213 | (if (i32.and (local.get $m) (i32.shl (i32.const 1) (i32.const 7))) 214 | (then 215 | (local.set $iA (i32.xor (local.get $iA) (i32.const 4))) 216 | ) 217 | ) 218 | (local.set $iA (i32.xor (local.get $iA) (i32.load8_u (i32.add (i32.const 0) (local.get $b))))) 219 | (if (i32.and (local.get $a) (i32.shl (i32.const 1) (i32.const 0))) 220 | (then 221 | (local.set $iA (i32.xor (local.get $iA) (i32.const 1))) 222 | ) 223 | ) 224 | (if (i32.and (local.get $a) (i32.shl (i32.const 1) (i32.const 1))) 225 | (then 226 | (local.set $iA (i32.xor (local.get $iA) (i32.const 1))) 227 | ) 228 | ) 229 | (if (i32.and (local.get $a) (i32.shl (i32.const 1) (i32.const 2))) 230 | (then 231 | (local.set $iA (i32.xor (local.get $iA) (i32.const 2))) 232 | ) 233 | ) 234 | (if (i32.and (local.get $a) (i32.shl (i32.const 1) (i32.const 3))) 235 | (then 236 | (local.set $iA (i32.xor (local.get $iA) (i32.const 7))) 237 | ) 238 | ) 239 | (if (i32.and (local.get $a) (i32.shl (i32.const 1) (i32.const 4))) 240 | (then 241 | (local.set $iA (i32.xor (local.get $iA) (i32.const 1))) 242 | ) 243 | ) 244 | (if (i32.and (local.get $a) (i32.shl (i32.const 1) (i32.const 5))) 245 | (then 246 | (local.set $iA (i32.xor (local.get $iA) (i32.const 1))) 247 | ) 248 | ) 249 | (if (i32.and (local.get $a) (i32.shl (i32.const 1) (i32.const 6))) 250 | (then 251 | (local.set $iA (i32.xor (local.get $iA) (i32.const 1))) 252 | ) 253 | ) 254 | (if (i32.and (local.get $a) (i32.shl (i32.const 1) (i32.const 7))) 255 | (then 256 | (local.set $iA (i32.xor (local.get $iA) (i32.const 1))) 257 | ) 258 | ) 259 | (local.set $iC (i32.and (local.get $c) (i32.const 7))) 260 | (local.set $m (i32.and (local.get $c) (i32.xor (i32.const -1) (i32.const 7)))) 261 | (if (i32.and (local.get $m) (i32.shl (i32.const 1) (i32.const 3))) 262 | (then 263 | (local.set $iC (i32.xor (local.get $iC) (i32.const 4))) 264 | ) 265 | ) 266 | (if (i32.and (local.get $m) (i32.shl (i32.const 1) (i32.const 4))) 267 | (then 268 | (local.set $iC (i32.xor (local.get $iC) (i32.const 4))) 269 | ) 270 | ) 271 | (if (i32.and (local.get $m) (i32.shl (i32.const 1) (i32.const 5))) 272 | (then 273 | (local.set $iC (i32.xor (local.get $iC) (i32.const 4))) 274 | ) 275 | ) 276 | (if (i32.and (local.get $m) (i32.shl (i32.const 1) (i32.const 6))) 277 | (then 278 | (local.set $iC (i32.xor (local.get $iC) (i32.const 4))) 279 | ) 280 | ) 281 | (if (i32.and (local.get $m) (i32.shl (i32.const 1) (i32.const 7))) 282 | (then 283 | (local.set $iC (i32.xor (local.get $iC) (i32.const 4))) 284 | ) 285 | ) 286 | (local.set $iC (i32.xor (local.get $iC) (i32.load8_u (i32.add (i32.const 768) (i32.and (local.get $b) (i32.const 15)))))) 287 | i32.const 256 288 | local.get $b 289 | i32.add 290 | i32.load8_u 291 | local.get $d 292 | i32.xor 293 | local.set $d 294 | i32.const 512 295 | local.get $a 296 | i32.add 297 | i32.load8_u 298 | local.get $d 299 | i32.xor 300 | local.set $d 301 | i32.const 1296 302 | local.get $d 303 | i32.add 304 | i32.load8_u 305 | local.set $iB 306 | i32.const 784 307 | local.get $b 308 | i32.add 309 | i32.load8_u 310 | local.get $c 311 | i32.xor 312 | local.set $c 313 | i32.const 1040 314 | local.get $a 315 | i32.add 316 | i32.load8_u 317 | local.get $c 318 | i32.xor 319 | local.set $c 320 | i32.const 1552 321 | local.get $c 322 | i32.add 323 | i32.load8_u 324 | local.set $iD 325 | i32.const 2099 326 | i32.const 1938 327 | local.get $iA 328 | i32.add 329 | i32.load8_u 330 | i32.store8 331 | i32.const 2098 332 | i32.const 1808 333 | local.get $iB 334 | i32.add 335 | i32.load8_u 336 | i32.store8 337 | i32.const 2097 338 | i32.const 2082 339 | local.get $iC 340 | i32.add 341 | i32.load8_u 342 | i32.store8 343 | i32.const 2096 344 | i32.const 1952 345 | local.get $iD 346 | i32.add 347 | i32.load8_u 348 | i32.store8 349 | ) 350 | 351 | (export "makeKey" (func $DEPUTY_EXTERNAL_SOMETHING_WHATEVER_PLEASE_USE_OR_CELESWUFF_WILL_CRY)) 352 | ) 353 | -------------------------------------------------------------------------------- /pnpm-lock.yaml: -------------------------------------------------------------------------------- 1 | lockfileVersion: '9.0' 2 | 3 | settings: 4 | autoInstallPeers: true 5 | excludeLinksFromLockfile: false 6 | 7 | importers: 8 | 9 | .: 10 | dependencies: 11 | '@sentry/browser': 12 | specifier: ^9.13.0 13 | version: 9.13.0 14 | water.css: 15 | specifier: ^2.1.1 16 | version: 2.1.1 17 | devDependencies: 18 | '@fullhuman/postcss-purgecss': 19 | specifier: ^6.0.0 20 | version: 6.0.0(postcss@8.5.3) 21 | '@types/web-bluetooth': 22 | specifier: ^0.0.21 23 | version: 0.0.21 24 | typescript: 25 | specifier: ^5.8.3 26 | version: 5.8.3 27 | vite: 28 | specifier: ^6.3.2 29 | version: 6.3.2(terser@5.39.0) 30 | vite-plugin-html: 31 | specifier: ^3.2.2 32 | version: 3.2.2(vite@6.3.2(terser@5.39.0)) 33 | vite-plugin-singlefile: 34 | specifier: ^2.2.0 35 | version: 2.2.0(rollup@4.40.0)(vite@6.3.2(terser@5.39.0)) 36 | vitest: 37 | specifier: ^3.1.1 38 | version: 3.1.1(terser@5.39.0) 39 | wabt: 40 | specifier: ^1.0.37 41 | version: 1.0.37 42 | 43 | packages: 44 | 45 | '@esbuild/aix-ppc64@0.25.2': 46 | resolution: {integrity: sha512-wCIboOL2yXZym2cgm6mlA742s9QeJ8DjGVaL39dLN4rRwrOgOyYSnOaFPhKZGLb2ngj4EyfAFjsNJwPXZvseag==} 47 | engines: {node: '>=18'} 48 | cpu: [ppc64] 49 | os: [aix] 50 | 51 | '@esbuild/android-arm64@0.25.2': 52 | resolution: {integrity: sha512-5ZAX5xOmTligeBaeNEPnPaeEuah53Id2tX4c2CVP3JaROTH+j4fnfHCkr1PjXMd78hMst+TlkfKcW/DlTq0i4w==} 53 | engines: {node: '>=18'} 54 | cpu: [arm64] 55 | os: [android] 56 | 57 | '@esbuild/android-arm@0.25.2': 58 | resolution: {integrity: sha512-NQhH7jFstVY5x8CKbcfa166GoV0EFkaPkCKBQkdPJFvo5u+nGXLEH/ooniLb3QI8Fk58YAx7nsPLozUWfCBOJA==} 59 | engines: {node: '>=18'} 60 | cpu: [arm] 61 | os: [android] 62 | 63 | '@esbuild/android-x64@0.25.2': 64 | resolution: {integrity: sha512-Ffcx+nnma8Sge4jzddPHCZVRvIfQ0kMsUsCMcJRHkGJ1cDmhe4SsrYIjLUKn1xpHZybmOqCWwB0zQvsjdEHtkg==} 65 | engines: {node: '>=18'} 66 | cpu: [x64] 67 | os: [android] 68 | 69 | '@esbuild/darwin-arm64@0.25.2': 70 | resolution: {integrity: sha512-MpM6LUVTXAzOvN4KbjzU/q5smzryuoNjlriAIx+06RpecwCkL9JpenNzpKd2YMzLJFOdPqBpuub6eVRP5IgiSA==} 71 | engines: {node: '>=18'} 72 | cpu: [arm64] 73 | os: [darwin] 74 | 75 | '@esbuild/darwin-x64@0.25.2': 76 | resolution: {integrity: sha512-5eRPrTX7wFyuWe8FqEFPG2cU0+butQQVNcT4sVipqjLYQjjh8a8+vUTfgBKM88ObB85ahsnTwF7PSIt6PG+QkA==} 77 | engines: {node: '>=18'} 78 | cpu: [x64] 79 | os: [darwin] 80 | 81 | '@esbuild/freebsd-arm64@0.25.2': 82 | resolution: {integrity: sha512-mLwm4vXKiQ2UTSX4+ImyiPdiHjiZhIaE9QvC7sw0tZ6HoNMjYAqQpGyui5VRIi5sGd+uWq940gdCbY3VLvsO1w==} 83 | engines: {node: '>=18'} 84 | cpu: [arm64] 85 | os: [freebsd] 86 | 87 | '@esbuild/freebsd-x64@0.25.2': 88 | resolution: {integrity: sha512-6qyyn6TjayJSwGpm8J9QYYGQcRgc90nmfdUb0O7pp1s4lTY+9D0H9O02v5JqGApUyiHOtkz6+1hZNvNtEhbwRQ==} 89 | engines: {node: '>=18'} 90 | cpu: [x64] 91 | os: [freebsd] 92 | 93 | '@esbuild/linux-arm64@0.25.2': 94 | resolution: {integrity: sha512-gq/sjLsOyMT19I8obBISvhoYiZIAaGF8JpeXu1u8yPv8BE5HlWYobmlsfijFIZ9hIVGYkbdFhEqC0NvM4kNO0g==} 95 | engines: {node: '>=18'} 96 | cpu: [arm64] 97 | os: [linux] 98 | 99 | '@esbuild/linux-arm@0.25.2': 100 | resolution: {integrity: sha512-UHBRgJcmjJv5oeQF8EpTRZs/1knq6loLxTsjc3nxO9eXAPDLcWW55flrMVc97qFPbmZP31ta1AZVUKQzKTzb0g==} 101 | engines: {node: '>=18'} 102 | cpu: [arm] 103 | os: [linux] 104 | 105 | '@esbuild/linux-ia32@0.25.2': 106 | resolution: {integrity: sha512-bBYCv9obgW2cBP+2ZWfjYTU+f5cxRoGGQ5SeDbYdFCAZpYWrfjjfYwvUpP8MlKbP0nwZ5gyOU/0aUzZ5HWPuvQ==} 107 | engines: {node: '>=18'} 108 | cpu: [ia32] 109 | os: [linux] 110 | 111 | '@esbuild/linux-loong64@0.25.2': 112 | resolution: {integrity: sha512-SHNGiKtvnU2dBlM5D8CXRFdd+6etgZ9dXfaPCeJtz+37PIUlixvlIhI23L5khKXs3DIzAn9V8v+qb1TRKrgT5w==} 113 | engines: {node: '>=18'} 114 | cpu: [loong64] 115 | os: [linux] 116 | 117 | '@esbuild/linux-mips64el@0.25.2': 118 | resolution: {integrity: sha512-hDDRlzE6rPeoj+5fsADqdUZl1OzqDYow4TB4Y/3PlKBD0ph1e6uPHzIQcv2Z65u2K0kpeByIyAjCmjn1hJgG0Q==} 119 | engines: {node: '>=18'} 120 | cpu: [mips64el] 121 | os: [linux] 122 | 123 | '@esbuild/linux-ppc64@0.25.2': 124 | resolution: {integrity: sha512-tsHu2RRSWzipmUi9UBDEzc0nLc4HtpZEI5Ba+Omms5456x5WaNuiG3u7xh5AO6sipnJ9r4cRWQB2tUjPyIkc6g==} 125 | engines: {node: '>=18'} 126 | cpu: [ppc64] 127 | os: [linux] 128 | 129 | '@esbuild/linux-riscv64@0.25.2': 130 | resolution: {integrity: sha512-k4LtpgV7NJQOml/10uPU0s4SAXGnowi5qBSjaLWMojNCUICNu7TshqHLAEbkBdAszL5TabfvQ48kK84hyFzjnw==} 131 | engines: {node: '>=18'} 132 | cpu: [riscv64] 133 | os: [linux] 134 | 135 | '@esbuild/linux-s390x@0.25.2': 136 | resolution: {integrity: sha512-GRa4IshOdvKY7M/rDpRR3gkiTNp34M0eLTaC1a08gNrh4u488aPhuZOCpkF6+2wl3zAN7L7XIpOFBhnaE3/Q8Q==} 137 | engines: {node: '>=18'} 138 | cpu: [s390x] 139 | os: [linux] 140 | 141 | '@esbuild/linux-x64@0.25.2': 142 | resolution: {integrity: sha512-QInHERlqpTTZ4FRB0fROQWXcYRD64lAoiegezDunLpalZMjcUcld3YzZmVJ2H/Cp0wJRZ8Xtjtj0cEHhYc/uUg==} 143 | engines: {node: '>=18'} 144 | cpu: [x64] 145 | os: [linux] 146 | 147 | '@esbuild/netbsd-arm64@0.25.2': 148 | resolution: {integrity: sha512-talAIBoY5M8vHc6EeI2WW9d/CkiO9MQJ0IOWX8hrLhxGbro/vBXJvaQXefW2cP0z0nQVTdQ/eNyGFV1GSKrxfw==} 149 | engines: {node: '>=18'} 150 | cpu: [arm64] 151 | os: [netbsd] 152 | 153 | '@esbuild/netbsd-x64@0.25.2': 154 | resolution: {integrity: sha512-voZT9Z+tpOxrvfKFyfDYPc4DO4rk06qamv1a/fkuzHpiVBMOhpjK+vBmWM8J1eiB3OLSMFYNaOaBNLXGChf5tg==} 155 | engines: {node: '>=18'} 156 | cpu: [x64] 157 | os: [netbsd] 158 | 159 | '@esbuild/openbsd-arm64@0.25.2': 160 | resolution: {integrity: sha512-dcXYOC6NXOqcykeDlwId9kB6OkPUxOEqU+rkrYVqJbK2hagWOMrsTGsMr8+rW02M+d5Op5NNlgMmjzecaRf7Tg==} 161 | engines: {node: '>=18'} 162 | cpu: [arm64] 163 | os: [openbsd] 164 | 165 | '@esbuild/openbsd-x64@0.25.2': 166 | resolution: {integrity: sha512-t/TkWwahkH0Tsgoq1Ju7QfgGhArkGLkF1uYz8nQS/PPFlXbP5YgRpqQR3ARRiC2iXoLTWFxc6DJMSK10dVXluw==} 167 | engines: {node: '>=18'} 168 | cpu: [x64] 169 | os: [openbsd] 170 | 171 | '@esbuild/sunos-x64@0.25.2': 172 | resolution: {integrity: sha512-cfZH1co2+imVdWCjd+D1gf9NjkchVhhdpgb1q5y6Hcv9TP6Zi9ZG/beI3ig8TvwT9lH9dlxLq5MQBBgwuj4xvA==} 173 | engines: {node: '>=18'} 174 | cpu: [x64] 175 | os: [sunos] 176 | 177 | '@esbuild/win32-arm64@0.25.2': 178 | resolution: {integrity: sha512-7Loyjh+D/Nx/sOTzV8vfbB3GJuHdOQyrOryFdZvPHLf42Tk9ivBU5Aedi7iyX+x6rbn2Mh68T4qq1SDqJBQO5Q==} 179 | engines: {node: '>=18'} 180 | cpu: [arm64] 181 | os: [win32] 182 | 183 | '@esbuild/win32-ia32@0.25.2': 184 | resolution: {integrity: sha512-WRJgsz9un0nqZJ4MfhabxaD9Ft8KioqU3JMinOTvobbX6MOSUigSBlogP8QB3uxpJDsFS6yN+3FDBdqE5lg9kg==} 185 | engines: {node: '>=18'} 186 | cpu: [ia32] 187 | os: [win32] 188 | 189 | '@esbuild/win32-x64@0.25.2': 190 | resolution: {integrity: sha512-kM3HKb16VIXZyIeVrM1ygYmZBKybX8N4p754bw390wGO3Tf2j4L2/WYL+4suWujpgf6GBYs3jv7TyUivdd05JA==} 191 | engines: {node: '>=18'} 192 | cpu: [x64] 193 | os: [win32] 194 | 195 | '@fullhuman/postcss-purgecss@6.0.0': 196 | resolution: {integrity: sha512-sUvk5PV7O5xvTJcxDYrQ00xlKtSxivvJdZrwgxE8F1GmNMs7w9U+dSbr83N/qEs9b+f+6QsZKXDs0k8nMjBIqA==} 197 | peerDependencies: 198 | postcss: ^8.0.0 199 | 200 | '@isaacs/cliui@8.0.2': 201 | resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} 202 | engines: {node: '>=12'} 203 | 204 | '@jridgewell/gen-mapping@0.3.8': 205 | resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} 206 | engines: {node: '>=6.0.0'} 207 | 208 | '@jridgewell/resolve-uri@3.1.2': 209 | resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} 210 | engines: {node: '>=6.0.0'} 211 | 212 | '@jridgewell/set-array@1.2.1': 213 | resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} 214 | engines: {node: '>=6.0.0'} 215 | 216 | '@jridgewell/source-map@0.3.6': 217 | resolution: {integrity: sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ==} 218 | 219 | '@jridgewell/sourcemap-codec@1.5.0': 220 | resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} 221 | 222 | '@jridgewell/trace-mapping@0.3.25': 223 | resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} 224 | 225 | '@nodelib/fs.scandir@2.1.5': 226 | resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} 227 | engines: {node: '>= 8'} 228 | 229 | '@nodelib/fs.stat@2.0.5': 230 | resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} 231 | engines: {node: '>= 8'} 232 | 233 | '@nodelib/fs.walk@1.2.8': 234 | resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} 235 | engines: {node: '>= 8'} 236 | 237 | '@pkgjs/parseargs@0.11.0': 238 | resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} 239 | engines: {node: '>=14'} 240 | 241 | '@rollup/pluginutils@4.2.1': 242 | resolution: {integrity: sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==} 243 | engines: {node: '>= 8.0.0'} 244 | 245 | '@rollup/rollup-android-arm-eabi@4.40.0': 246 | resolution: {integrity: sha512-+Fbls/diZ0RDerhE8kyC6hjADCXA1K4yVNlH0EYfd2XjyH0UGgzaQ8MlT0pCXAThfxv3QUAczHaL+qSv1E4/Cg==} 247 | cpu: [arm] 248 | os: [android] 249 | 250 | '@rollup/rollup-android-arm64@4.40.0': 251 | resolution: {integrity: sha512-PPA6aEEsTPRz+/4xxAmaoWDqh67N7wFbgFUJGMnanCFs0TV99M0M8QhhaSCks+n6EbQoFvLQgYOGXxlMGQe/6w==} 252 | cpu: [arm64] 253 | os: [android] 254 | 255 | '@rollup/rollup-darwin-arm64@4.40.0': 256 | resolution: {integrity: sha512-GwYOcOakYHdfnjjKwqpTGgn5a6cUX7+Ra2HeNj/GdXvO2VJOOXCiYYlRFU4CubFM67EhbmzLOmACKEfvp3J1kQ==} 257 | cpu: [arm64] 258 | os: [darwin] 259 | 260 | '@rollup/rollup-darwin-x64@4.40.0': 261 | resolution: {integrity: sha512-CoLEGJ+2eheqD9KBSxmma6ld01czS52Iw0e2qMZNpPDlf7Z9mj8xmMemxEucinev4LgHalDPczMyxzbq+Q+EtA==} 262 | cpu: [x64] 263 | os: [darwin] 264 | 265 | '@rollup/rollup-freebsd-arm64@4.40.0': 266 | resolution: {integrity: sha512-r7yGiS4HN/kibvESzmrOB/PxKMhPTlz+FcGvoUIKYoTyGd5toHp48g1uZy1o1xQvybwwpqpe010JrcGG2s5nkg==} 267 | cpu: [arm64] 268 | os: [freebsd] 269 | 270 | '@rollup/rollup-freebsd-x64@4.40.0': 271 | resolution: {integrity: sha512-mVDxzlf0oLzV3oZOr0SMJ0lSDd3xC4CmnWJ8Val8isp9jRGl5Dq//LLDSPFrasS7pSm6m5xAcKaw3sHXhBjoRw==} 272 | cpu: [x64] 273 | os: [freebsd] 274 | 275 | '@rollup/rollup-linux-arm-gnueabihf@4.40.0': 276 | resolution: {integrity: sha512-y/qUMOpJxBMy8xCXD++jeu8t7kzjlOCkoxxajL58G62PJGBZVl/Gwpm7JK9+YvlB701rcQTzjUZ1JgUoPTnoQA==} 277 | cpu: [arm] 278 | os: [linux] 279 | 280 | '@rollup/rollup-linux-arm-musleabihf@4.40.0': 281 | resolution: {integrity: sha512-GoCsPibtVdJFPv/BOIvBKO/XmwZLwaNWdyD8TKlXuqp0veo2sHE+A/vpMQ5iSArRUz/uaoj4h5S6Pn0+PdhRjg==} 282 | cpu: [arm] 283 | os: [linux] 284 | 285 | '@rollup/rollup-linux-arm64-gnu@4.40.0': 286 | resolution: {integrity: sha512-L5ZLphTjjAD9leJzSLI7rr8fNqJMlGDKlazW2tX4IUF9P7R5TMQPElpH82Q7eNIDQnQlAyiNVfRPfP2vM5Avvg==} 287 | cpu: [arm64] 288 | os: [linux] 289 | 290 | '@rollup/rollup-linux-arm64-musl@4.40.0': 291 | resolution: {integrity: sha512-ATZvCRGCDtv1Y4gpDIXsS+wfFeFuLwVxyUBSLawjgXK2tRE6fnsQEkE4csQQYWlBlsFztRzCnBvWVfcae/1qxQ==} 292 | cpu: [arm64] 293 | os: [linux] 294 | 295 | '@rollup/rollup-linux-loongarch64-gnu@4.40.0': 296 | resolution: {integrity: sha512-wG9e2XtIhd++QugU5MD9i7OnpaVb08ji3P1y/hNbxrQ3sYEelKJOq1UJ5dXczeo6Hj2rfDEL5GdtkMSVLa/AOg==} 297 | cpu: [loong64] 298 | os: [linux] 299 | 300 | '@rollup/rollup-linux-powerpc64le-gnu@4.40.0': 301 | resolution: {integrity: sha512-vgXfWmj0f3jAUvC7TZSU/m/cOE558ILWDzS7jBhiCAFpY2WEBn5jqgbqvmzlMjtp8KlLcBlXVD2mkTSEQE6Ixw==} 302 | cpu: [ppc64] 303 | os: [linux] 304 | 305 | '@rollup/rollup-linux-riscv64-gnu@4.40.0': 306 | resolution: {integrity: sha512-uJkYTugqtPZBS3Z136arevt/FsKTF/J9dEMTX/cwR7lsAW4bShzI2R0pJVw+hcBTWF4dxVckYh72Hk3/hWNKvA==} 307 | cpu: [riscv64] 308 | os: [linux] 309 | 310 | '@rollup/rollup-linux-riscv64-musl@4.40.0': 311 | resolution: {integrity: sha512-rKmSj6EXQRnhSkE22+WvrqOqRtk733x3p5sWpZilhmjnkHkpeCgWsFFo0dGnUGeA+OZjRl3+VYq+HyCOEuwcxQ==} 312 | cpu: [riscv64] 313 | os: [linux] 314 | 315 | '@rollup/rollup-linux-s390x-gnu@4.40.0': 316 | resolution: {integrity: sha512-SpnYlAfKPOoVsQqmTFJ0usx0z84bzGOS9anAC0AZ3rdSo3snecihbhFTlJZ8XMwzqAcodjFU4+/SM311dqE5Sw==} 317 | cpu: [s390x] 318 | os: [linux] 319 | 320 | '@rollup/rollup-linux-x64-gnu@4.40.0': 321 | resolution: {integrity: sha512-RcDGMtqF9EFN8i2RYN2W+64CdHruJ5rPqrlYw+cgM3uOVPSsnAQps7cpjXe9be/yDp8UC7VLoCoKC8J3Kn2FkQ==} 322 | cpu: [x64] 323 | os: [linux] 324 | 325 | '@rollup/rollup-linux-x64-musl@4.40.0': 326 | resolution: {integrity: sha512-HZvjpiUmSNx5zFgwtQAV1GaGazT2RWvqeDi0hV+AtC8unqqDSsaFjPxfsO6qPtKRRg25SisACWnJ37Yio8ttaw==} 327 | cpu: [x64] 328 | os: [linux] 329 | 330 | '@rollup/rollup-win32-arm64-msvc@4.40.0': 331 | resolution: {integrity: sha512-UtZQQI5k/b8d7d3i9AZmA/t+Q4tk3hOC0tMOMSq2GlMYOfxbesxG4mJSeDp0EHs30N9bsfwUvs3zF4v/RzOeTQ==} 332 | cpu: [arm64] 333 | os: [win32] 334 | 335 | '@rollup/rollup-win32-ia32-msvc@4.40.0': 336 | resolution: {integrity: sha512-+m03kvI2f5syIqHXCZLPVYplP8pQch9JHyXKZ3AGMKlg8dCyr2PKHjwRLiW53LTrN/Nc3EqHOKxUxzoSPdKddA==} 337 | cpu: [ia32] 338 | os: [win32] 339 | 340 | '@rollup/rollup-win32-x64-msvc@4.40.0': 341 | resolution: {integrity: sha512-lpPE1cLfP5oPzVjKMx10pgBmKELQnFJXHgvtHCtuJWOv8MxqdEIMNtgHgBFf7Ea2/7EuVwa9fodWUfXAlXZLZQ==} 342 | cpu: [x64] 343 | os: [win32] 344 | 345 | '@sentry-internal/browser-utils@9.13.0': 346 | resolution: {integrity: sha512-uZcbwcGI49oPC/YDEConJ+3xi2mu0TsVsDiMQKb6JoSc33KH37wq2IwXJb9nakzKJXxyMNemb44r8irAswjItw==} 347 | engines: {node: '>=18'} 348 | 349 | '@sentry-internal/feedback@9.13.0': 350 | resolution: {integrity: sha512-fOhMnhEbOR5QVPtn5Gc5+UKQHjvAN/LmtYE6Qya3w2FDh3ZlnIXNFJWqwOneuICV3kCWjN4lLckwmzzwychr7A==} 351 | engines: {node: '>=18'} 352 | 353 | '@sentry-internal/replay-canvas@9.13.0': 354 | resolution: {integrity: sha512-5muW2BmEfWP1fpVWDNcIsph/WgqOqpHaXC1QMr4hk8/BWgt1/S2KPy85YiGVtM5lJJr0VhASKK8rBXG+9zm9IQ==} 355 | engines: {node: '>=18'} 356 | 357 | '@sentry-internal/replay@9.13.0': 358 | resolution: {integrity: sha512-l+Atwab/bqI1N8+PSG1WWTCVmiOl7swL85Z9ntwS39QBnd66CTyzt/+j/n/UbAs8GienJK6FIfX1dvG1WmvUhA==} 359 | engines: {node: '>=18'} 360 | 361 | '@sentry/browser@9.13.0': 362 | resolution: {integrity: sha512-KiC8s9/6HvdlfCRqA420YbiBiXMBif7GYESJ8VQqOKUmlPczn8V2CRrEZjMqxhlHdIGiR0PS6jb2VSgeJBchJQ==} 363 | engines: {node: '>=18'} 364 | 365 | '@sentry/core@9.13.0': 366 | resolution: {integrity: sha512-Zn1Qec5XNkNRE/M5QjL6YJLghETg6P188G/v2OzdHdHIRf0Y58/SnJilu3louF+ogos6kaSqqdMgzqKgZ8tCdg==} 367 | engines: {node: '>=18'} 368 | 369 | '@types/estree@1.0.7': 370 | resolution: {integrity: sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ==} 371 | 372 | '@types/web-bluetooth@0.0.21': 373 | resolution: {integrity: sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==} 374 | 375 | '@vitest/expect@3.1.1': 376 | resolution: {integrity: sha512-q/zjrW9lgynctNbwvFtQkGK9+vvHA5UzVi2V8APrp1C6fG6/MuYYkmlx4FubuqLycCeSdHD5aadWfua/Vr0EUA==} 377 | 378 | '@vitest/mocker@3.1.1': 379 | resolution: {integrity: sha512-bmpJJm7Y7i9BBELlLuuM1J1Q6EQ6K5Ye4wcyOpOMXMcePYKSIYlpcrCm4l/O6ja4VJA5G2aMJiuZkZdnxlC3SA==} 380 | peerDependencies: 381 | msw: ^2.4.9 382 | vite: ^5.0.0 || ^6.0.0 383 | peerDependenciesMeta: 384 | msw: 385 | optional: true 386 | vite: 387 | optional: true 388 | 389 | '@vitest/pretty-format@3.1.1': 390 | resolution: {integrity: sha512-dg0CIzNx+hMMYfNmSqJlLSXEmnNhMswcn3sXO7Tpldr0LiGmg3eXdLLhwkv2ZqgHb/d5xg5F7ezNFRA1fA13yA==} 391 | 392 | '@vitest/runner@3.1.1': 393 | resolution: {integrity: sha512-X/d46qzJuEDO8ueyjtKfxffiXraPRfmYasoC4i5+mlLEJ10UvPb0XH5M9C3gWuxd7BAQhpK42cJgJtq53YnWVA==} 394 | 395 | '@vitest/snapshot@3.1.1': 396 | resolution: {integrity: sha512-bByMwaVWe/+1WDf9exFxWWgAixelSdiwo2p33tpqIlM14vW7PRV5ppayVXtfycqze4Qhtwag5sVhX400MLBOOw==} 397 | 398 | '@vitest/spy@3.1.1': 399 | resolution: {integrity: sha512-+EmrUOOXbKzLkTDwlsc/xrwOlPDXyVk3Z6P6K4oiCndxz7YLpp/0R0UsWVOKT0IXWjjBJuSMk6D27qipaupcvQ==} 400 | 401 | '@vitest/utils@3.1.1': 402 | resolution: {integrity: sha512-1XIjflyaU2k3HMArJ50bwSh3wKWPD6Q47wz/NUSmRV0zNywPc4w79ARjg/i/aNINHwA+mIALhUVqD9/aUvZNgg==} 403 | 404 | acorn@8.14.1: 405 | resolution: {integrity: sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg==} 406 | engines: {node: '>=0.4.0'} 407 | hasBin: true 408 | 409 | ansi-regex@5.0.1: 410 | resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} 411 | engines: {node: '>=8'} 412 | 413 | ansi-regex@6.1.0: 414 | resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} 415 | engines: {node: '>=12'} 416 | 417 | ansi-styles@4.3.0: 418 | resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} 419 | engines: {node: '>=8'} 420 | 421 | ansi-styles@6.2.1: 422 | resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} 423 | engines: {node: '>=12'} 424 | 425 | assertion-error@2.0.1: 426 | resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} 427 | engines: {node: '>=12'} 428 | 429 | async@3.2.6: 430 | resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} 431 | 432 | balanced-match@1.0.2: 433 | resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} 434 | 435 | boolbase@1.0.0: 436 | resolution: {integrity: sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==} 437 | 438 | brace-expansion@1.1.11: 439 | resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} 440 | 441 | brace-expansion@2.0.1: 442 | resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} 443 | 444 | braces@3.0.3: 445 | resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} 446 | engines: {node: '>=8'} 447 | 448 | buffer-from@1.1.2: 449 | resolution: {integrity: sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==} 450 | 451 | cac@6.7.14: 452 | resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} 453 | engines: {node: '>=8'} 454 | 455 | camel-case@4.1.2: 456 | resolution: {integrity: sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==} 457 | 458 | chai@5.2.0: 459 | resolution: {integrity: sha512-mCuXncKXk5iCLhfhwTc0izo0gtEmpz5CtG2y8GiOINBlMVS6v8TMRc5TaLWKS6692m9+dVVfzgeVxR5UxWHTYw==} 460 | engines: {node: '>=12'} 461 | 462 | chalk@4.1.2: 463 | resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} 464 | engines: {node: '>=10'} 465 | 466 | check-error@2.1.1: 467 | resolution: {integrity: sha512-OAlb+T7V4Op9OwdkjmguYRqncdlx5JiofwOAUkmTF+jNdHwzTaTs4sRAGpzLF3oOz5xAyDGrPgeIDFQmDOTiJw==} 468 | engines: {node: '>= 16'} 469 | 470 | clean-css@5.3.3: 471 | resolution: {integrity: sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==} 472 | engines: {node: '>= 10.0'} 473 | 474 | color-convert@2.0.1: 475 | resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} 476 | engines: {node: '>=7.0.0'} 477 | 478 | color-name@1.1.4: 479 | resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} 480 | 481 | colorette@2.0.20: 482 | resolution: {integrity: sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==} 483 | 484 | commander@12.1.0: 485 | resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} 486 | engines: {node: '>=18'} 487 | 488 | commander@2.20.3: 489 | resolution: {integrity: sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==} 490 | 491 | commander@8.3.0: 492 | resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} 493 | engines: {node: '>= 12'} 494 | 495 | concat-map@0.0.1: 496 | resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} 497 | 498 | connect-history-api-fallback@1.6.0: 499 | resolution: {integrity: sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==} 500 | engines: {node: '>=0.8'} 501 | 502 | consola@2.15.3: 503 | resolution: {integrity: sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==} 504 | 505 | cross-spawn@7.0.6: 506 | resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} 507 | engines: {node: '>= 8'} 508 | 509 | css-select@4.3.0: 510 | resolution: {integrity: sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==} 511 | 512 | css-what@6.1.0: 513 | resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} 514 | engines: {node: '>= 6'} 515 | 516 | cssesc@3.0.0: 517 | resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} 518 | engines: {node: '>=4'} 519 | hasBin: true 520 | 521 | debug@4.4.0: 522 | resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} 523 | engines: {node: '>=6.0'} 524 | peerDependencies: 525 | supports-color: '*' 526 | peerDependenciesMeta: 527 | supports-color: 528 | optional: true 529 | 530 | deep-eql@5.0.2: 531 | resolution: {integrity: sha512-h5k/5U50IJJFpzfL6nO9jaaumfjO/f2NjK/oYB2Djzm4p9L+3T9qWpZqZ2hAbLPuuYq9wrU08WQyBTL5GbPk5Q==} 532 | engines: {node: '>=6'} 533 | 534 | dom-serializer@1.4.1: 535 | resolution: {integrity: sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==} 536 | 537 | domelementtype@2.3.0: 538 | resolution: {integrity: sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==} 539 | 540 | domhandler@4.3.1: 541 | resolution: {integrity: sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==} 542 | engines: {node: '>= 4'} 543 | 544 | domutils@2.8.0: 545 | resolution: {integrity: sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==} 546 | 547 | dot-case@3.0.4: 548 | resolution: {integrity: sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==} 549 | 550 | dotenv-expand@8.0.3: 551 | resolution: {integrity: sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==} 552 | engines: {node: '>=12'} 553 | 554 | dotenv@16.5.0: 555 | resolution: {integrity: sha512-m/C+AwOAr9/W1UOIZUo232ejMNnJAJtYQjUbHoNTBNTJSvqzzDh7vnrei3o3r3m9blf6ZoDkvcw0VmozNRFJxg==} 556 | engines: {node: '>=12'} 557 | 558 | eastasianwidth@0.2.0: 559 | resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} 560 | 561 | ejs@3.1.10: 562 | resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} 563 | engines: {node: '>=0.10.0'} 564 | hasBin: true 565 | 566 | emoji-regex@8.0.0: 567 | resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} 568 | 569 | emoji-regex@9.2.2: 570 | resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} 571 | 572 | entities@2.2.0: 573 | resolution: {integrity: sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==} 574 | 575 | es-module-lexer@1.6.0: 576 | resolution: {integrity: sha512-qqnD1yMU6tk/jnaMosogGySTZP8YtUgAffA9nMN+E/rjxcfRQ6IEk7IiozUjgxKoFHBGjTLnrHB/YC45r/59EQ==} 577 | 578 | esbuild@0.25.2: 579 | resolution: {integrity: sha512-16854zccKPnC+toMywC+uKNeYSv+/eXkevRAfwRD/G9Cleq66m8XFIrigkbvauLLlCfDL45Q2cWegSg53gGBnQ==} 580 | engines: {node: '>=18'} 581 | hasBin: true 582 | 583 | estree-walker@2.0.2: 584 | resolution: {integrity: sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==} 585 | 586 | estree-walker@3.0.3: 587 | resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} 588 | 589 | expect-type@1.2.1: 590 | resolution: {integrity: sha512-/kP8CAwxzLVEeFrMm4kMmy4CCDlpipyA7MYLVrdJIkV0fYF0UaigQHRsxHiuY/GEea+bh4KSv3TIlgr+2UL6bw==} 591 | engines: {node: '>=12.0.0'} 592 | 593 | fast-glob@3.3.3: 594 | resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} 595 | engines: {node: '>=8.6.0'} 596 | 597 | fastq@1.19.1: 598 | resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} 599 | 600 | fdir@6.4.3: 601 | resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} 602 | peerDependencies: 603 | picomatch: ^3 || ^4 604 | peerDependenciesMeta: 605 | picomatch: 606 | optional: true 607 | 608 | filelist@1.0.4: 609 | resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} 610 | 611 | fill-range@7.1.1: 612 | resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} 613 | engines: {node: '>=8'} 614 | 615 | foreground-child@3.3.1: 616 | resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} 617 | engines: {node: '>=14'} 618 | 619 | fs-extra@10.1.0: 620 | resolution: {integrity: sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==} 621 | engines: {node: '>=12'} 622 | 623 | fsevents@2.3.3: 624 | resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} 625 | engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} 626 | os: [darwin] 627 | 628 | glob-parent@5.1.2: 629 | resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} 630 | engines: {node: '>= 6'} 631 | 632 | glob@10.4.5: 633 | resolution: {integrity: sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==} 634 | hasBin: true 635 | 636 | graceful-fs@4.2.11: 637 | resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} 638 | 639 | has-flag@4.0.0: 640 | resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} 641 | engines: {node: '>=8'} 642 | 643 | he@1.2.0: 644 | resolution: {integrity: sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==} 645 | hasBin: true 646 | 647 | html-minifier-terser@6.1.0: 648 | resolution: {integrity: sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==} 649 | engines: {node: '>=12'} 650 | hasBin: true 651 | 652 | is-extglob@2.1.1: 653 | resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} 654 | engines: {node: '>=0.10.0'} 655 | 656 | is-fullwidth-code-point@3.0.0: 657 | resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} 658 | engines: {node: '>=8'} 659 | 660 | is-glob@4.0.3: 661 | resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} 662 | engines: {node: '>=0.10.0'} 663 | 664 | is-number@7.0.0: 665 | resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} 666 | engines: {node: '>=0.12.0'} 667 | 668 | isexe@2.0.0: 669 | resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} 670 | 671 | jackspeak@3.4.3: 672 | resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} 673 | 674 | jake@10.9.2: 675 | resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} 676 | engines: {node: '>=10'} 677 | hasBin: true 678 | 679 | jsonfile@6.1.0: 680 | resolution: {integrity: sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==} 681 | 682 | loupe@3.1.3: 683 | resolution: {integrity: sha512-kkIp7XSkP78ZxJEsSxW3712C6teJVoeHHwgo9zJ380de7IYyJ2ISlxojcH2pC5OFLewESmnRi/+XCDIEEVyoug==} 684 | 685 | lower-case@2.0.2: 686 | resolution: {integrity: sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==} 687 | 688 | lru-cache@10.4.3: 689 | resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} 690 | 691 | magic-string@0.30.17: 692 | resolution: {integrity: sha512-sNPKHvyjVf7gyjwS4xGTaW/mCnF8wnjtifKBEhxfZ7E/S8tQ0rssrwGNn6q8JH/ohItJfSQp9mBtQYuTlH5QnA==} 693 | 694 | merge2@1.4.1: 695 | resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} 696 | engines: {node: '>= 8'} 697 | 698 | micromatch@4.0.8: 699 | resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} 700 | engines: {node: '>=8.6'} 701 | 702 | minimatch@3.1.2: 703 | resolution: {integrity: sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==} 704 | 705 | minimatch@5.1.6: 706 | resolution: {integrity: sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==} 707 | engines: {node: '>=10'} 708 | 709 | minimatch@9.0.5: 710 | resolution: {integrity: sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==} 711 | engines: {node: '>=16 || 14 >=14.17'} 712 | 713 | minipass@7.1.2: 714 | resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} 715 | engines: {node: '>=16 || 14 >=14.17'} 716 | 717 | ms@2.1.3: 718 | resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} 719 | 720 | nanoid@3.3.11: 721 | resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} 722 | engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} 723 | hasBin: true 724 | 725 | no-case@3.0.4: 726 | resolution: {integrity: sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==} 727 | 728 | node-html-parser@5.4.2: 729 | resolution: {integrity: sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==} 730 | 731 | nth-check@2.1.1: 732 | resolution: {integrity: sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==} 733 | 734 | package-json-from-dist@1.0.1: 735 | resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} 736 | 737 | param-case@3.0.4: 738 | resolution: {integrity: sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==} 739 | 740 | pascal-case@3.1.2: 741 | resolution: {integrity: sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==} 742 | 743 | path-key@3.1.1: 744 | resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} 745 | engines: {node: '>=8'} 746 | 747 | path-scurry@1.11.1: 748 | resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} 749 | engines: {node: '>=16 || 14 >=14.18'} 750 | 751 | pathe@0.2.0: 752 | resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==} 753 | 754 | pathe@2.0.3: 755 | resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} 756 | 757 | pathval@2.0.0: 758 | resolution: {integrity: sha512-vE7JKRyES09KiunauX7nd2Q9/L7lhok4smP9RZTDeD4MVs72Dp2qNFVz39Nz5a0FVEW0BJR6C0DYrq6unoziZA==} 759 | engines: {node: '>= 14.16'} 760 | 761 | picocolors@1.1.1: 762 | resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} 763 | 764 | picomatch@2.3.1: 765 | resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} 766 | engines: {node: '>=8.6'} 767 | 768 | picomatch@4.0.2: 769 | resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} 770 | engines: {node: '>=12'} 771 | 772 | postcss-selector-parser@6.1.2: 773 | resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} 774 | engines: {node: '>=4'} 775 | 776 | postcss@8.5.3: 777 | resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} 778 | engines: {node: ^10 || ^12 || >=14} 779 | 780 | purgecss@6.0.0: 781 | resolution: {integrity: sha512-s3EBxg5RSWmpqd0KGzNqPiaBbWDz1/As+2MzoYVGMqgDqRTLBhJW6sywfTBek7OwNfoS/6pS0xdtvChNhFj2cw==} 782 | hasBin: true 783 | 784 | queue-microtask@1.2.3: 785 | resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} 786 | 787 | relateurl@0.2.7: 788 | resolution: {integrity: sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==} 789 | engines: {node: '>= 0.10'} 790 | 791 | reusify@1.1.0: 792 | resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} 793 | engines: {iojs: '>=1.0.0', node: '>=0.10.0'} 794 | 795 | rollup@4.40.0: 796 | resolution: {integrity: sha512-Noe455xmA96nnqH5piFtLobsGbCij7Tu+tb3c1vYjNbTkfzGqXqQXG3wJaYXkRZuQ0vEYN4bhwg7QnIrqB5B+w==} 797 | engines: {node: '>=18.0.0', npm: '>=8.0.0'} 798 | hasBin: true 799 | 800 | run-parallel@1.2.0: 801 | resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} 802 | 803 | shebang-command@2.0.0: 804 | resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} 805 | engines: {node: '>=8'} 806 | 807 | shebang-regex@3.0.0: 808 | resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} 809 | engines: {node: '>=8'} 810 | 811 | siginfo@2.0.0: 812 | resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} 813 | 814 | signal-exit@4.1.0: 815 | resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} 816 | engines: {node: '>=14'} 817 | 818 | source-map-js@1.2.1: 819 | resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} 820 | engines: {node: '>=0.10.0'} 821 | 822 | source-map-support@0.5.21: 823 | resolution: {integrity: sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==} 824 | 825 | source-map@0.6.1: 826 | resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} 827 | engines: {node: '>=0.10.0'} 828 | 829 | stackback@0.0.2: 830 | resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} 831 | 832 | std-env@3.9.0: 833 | resolution: {integrity: sha512-UGvjygr6F6tpH7o2qyqR6QYpwraIjKSdtzyBdyytFOHmPZY917kwdwLG0RbOjWOnKmnm3PeHjaoLLMie7kPLQw==} 834 | 835 | string-width@4.2.3: 836 | resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} 837 | engines: {node: '>=8'} 838 | 839 | string-width@5.1.2: 840 | resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} 841 | engines: {node: '>=12'} 842 | 843 | strip-ansi@6.0.1: 844 | resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} 845 | engines: {node: '>=8'} 846 | 847 | strip-ansi@7.1.0: 848 | resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} 849 | engines: {node: '>=12'} 850 | 851 | supports-color@7.2.0: 852 | resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} 853 | engines: {node: '>=8'} 854 | 855 | terser@5.39.0: 856 | resolution: {integrity: sha512-LBAhFyLho16harJoWMg/nZsQYgTrg5jXOn2nCYjRUcZZEdE3qa2zb8QEDRUGVZBW4rlazf2fxkg8tztybTaqWw==} 857 | engines: {node: '>=10'} 858 | hasBin: true 859 | 860 | tinybench@2.9.0: 861 | resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} 862 | 863 | tinyexec@0.3.2: 864 | resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} 865 | 866 | tinyglobby@0.2.12: 867 | resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} 868 | engines: {node: '>=12.0.0'} 869 | 870 | tinypool@1.0.2: 871 | resolution: {integrity: sha512-al6n+QEANGFOMf/dmUMsuS5/r9B06uwlyNjZZql/zv8J7ybHCgoihBNORZCY2mzUuAnomQa2JdhyHKzZxPCrFA==} 872 | engines: {node: ^18.0.0 || >=20.0.0} 873 | 874 | tinyrainbow@2.0.0: 875 | resolution: {integrity: sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==} 876 | engines: {node: '>=14.0.0'} 877 | 878 | tinyspy@3.0.2: 879 | resolution: {integrity: sha512-n1cw8k1k0x4pgA2+9XrOkFydTerNcJ1zWCO5Nn9scWHTD+5tp8dghT2x1uduQePZTZgd3Tupf+x9BxJjeJi77Q==} 880 | engines: {node: '>=14.0.0'} 881 | 882 | to-regex-range@5.0.1: 883 | resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} 884 | engines: {node: '>=8.0'} 885 | 886 | tslib@2.8.1: 887 | resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} 888 | 889 | typescript@5.8.3: 890 | resolution: {integrity: sha512-p1diW6TqL9L07nNxvRMM7hMMw4c5XOo/1ibL4aAIGmSAt9slTE1Xgw5KWuof2uTOvCg9BY7ZRi+GaF+7sfgPeQ==} 891 | engines: {node: '>=14.17'} 892 | hasBin: true 893 | 894 | universalify@2.0.1: 895 | resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} 896 | engines: {node: '>= 10.0.0'} 897 | 898 | util-deprecate@1.0.2: 899 | resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} 900 | 901 | vite-node@3.1.1: 902 | resolution: {integrity: sha512-V+IxPAE2FvXpTCHXyNem0M+gWm6J7eRyWPR6vYoG/Gl+IscNOjXzztUhimQgTxaAoUoj40Qqimaa0NLIOOAH4w==} 903 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 904 | hasBin: true 905 | 906 | vite-plugin-html@3.2.2: 907 | resolution: {integrity: sha512-vb9C9kcdzcIo/Oc3CLZVS03dL5pDlOFuhGlZYDCJ840BhWl/0nGeZWf3Qy7NlOayscY4Cm/QRgULCQkEZige5Q==} 908 | peerDependencies: 909 | vite: '>=2.0.0' 910 | 911 | vite-plugin-singlefile@2.2.0: 912 | resolution: {integrity: sha512-Ik1wXmJaGzeQtUeIV7JprDUqqy6DlLzXAY27Blei5peE4c9VJF+Kp9xWDJeuX0RJUZmFbIAuw1/RAh06A+Ql7w==} 913 | engines: {node: '>18.0.0'} 914 | peerDependencies: 915 | rollup: ^4.35.0 916 | vite: ^5.4.11 || ^6.0.0 917 | 918 | vite@6.3.2: 919 | resolution: {integrity: sha512-ZSvGOXKGceizRQIZSz7TGJ0pS3QLlVY/9hwxVh17W3re67je1RKYzFHivZ/t0tubU78Vkyb9WnHPENSBCzbckg==} 920 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 921 | hasBin: true 922 | peerDependencies: 923 | '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 924 | jiti: '>=1.21.0' 925 | less: '*' 926 | lightningcss: ^1.21.0 927 | sass: '*' 928 | sass-embedded: '*' 929 | stylus: '*' 930 | sugarss: '*' 931 | terser: ^5.16.0 932 | tsx: ^4.8.1 933 | yaml: ^2.4.2 934 | peerDependenciesMeta: 935 | '@types/node': 936 | optional: true 937 | jiti: 938 | optional: true 939 | less: 940 | optional: true 941 | lightningcss: 942 | optional: true 943 | sass: 944 | optional: true 945 | sass-embedded: 946 | optional: true 947 | stylus: 948 | optional: true 949 | sugarss: 950 | optional: true 951 | terser: 952 | optional: true 953 | tsx: 954 | optional: true 955 | yaml: 956 | optional: true 957 | 958 | vitest@3.1.1: 959 | resolution: {integrity: sha512-kiZc/IYmKICeBAZr9DQ5rT7/6bD9G7uqQEki4fxazi1jdVl2mWGzedtBs5s6llz59yQhVb7FFY2MbHzHCnT79Q==} 960 | engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} 961 | hasBin: true 962 | peerDependencies: 963 | '@edge-runtime/vm': '*' 964 | '@types/debug': ^4.1.12 965 | '@types/node': ^18.0.0 || ^20.0.0 || >=22.0.0 966 | '@vitest/browser': 3.1.1 967 | '@vitest/ui': 3.1.1 968 | happy-dom: '*' 969 | jsdom: '*' 970 | peerDependenciesMeta: 971 | '@edge-runtime/vm': 972 | optional: true 973 | '@types/debug': 974 | optional: true 975 | '@types/node': 976 | optional: true 977 | '@vitest/browser': 978 | optional: true 979 | '@vitest/ui': 980 | optional: true 981 | happy-dom: 982 | optional: true 983 | jsdom: 984 | optional: true 985 | 986 | wabt@1.0.37: 987 | resolution: {integrity: sha512-2B/TH4ppwtlkUosLtuIimKsTVnqM8aoXxYHnu/WOxiSqa+CGoZXmG+pQyfDQjEKIAc7GqFlJsuCKuK8rIPL1sg==} 988 | hasBin: true 989 | 990 | water.css@2.1.1: 991 | resolution: {integrity: sha512-gkO5byC+pZ7ndEV18hs/RmxKoDtEZXx06tZU4ocI3IBdv4xV64tlhjIFbDjurysRnNkiy2oQTr8PakRyzZWPJw==} 992 | 993 | which@2.0.2: 994 | resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} 995 | engines: {node: '>= 8'} 996 | hasBin: true 997 | 998 | why-is-node-running@2.3.0: 999 | resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} 1000 | engines: {node: '>=8'} 1001 | hasBin: true 1002 | 1003 | wrap-ansi@7.0.0: 1004 | resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} 1005 | engines: {node: '>=10'} 1006 | 1007 | wrap-ansi@8.1.0: 1008 | resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} 1009 | engines: {node: '>=12'} 1010 | 1011 | snapshots: 1012 | 1013 | '@esbuild/aix-ppc64@0.25.2': 1014 | optional: true 1015 | 1016 | '@esbuild/android-arm64@0.25.2': 1017 | optional: true 1018 | 1019 | '@esbuild/android-arm@0.25.2': 1020 | optional: true 1021 | 1022 | '@esbuild/android-x64@0.25.2': 1023 | optional: true 1024 | 1025 | '@esbuild/darwin-arm64@0.25.2': 1026 | optional: true 1027 | 1028 | '@esbuild/darwin-x64@0.25.2': 1029 | optional: true 1030 | 1031 | '@esbuild/freebsd-arm64@0.25.2': 1032 | optional: true 1033 | 1034 | '@esbuild/freebsd-x64@0.25.2': 1035 | optional: true 1036 | 1037 | '@esbuild/linux-arm64@0.25.2': 1038 | optional: true 1039 | 1040 | '@esbuild/linux-arm@0.25.2': 1041 | optional: true 1042 | 1043 | '@esbuild/linux-ia32@0.25.2': 1044 | optional: true 1045 | 1046 | '@esbuild/linux-loong64@0.25.2': 1047 | optional: true 1048 | 1049 | '@esbuild/linux-mips64el@0.25.2': 1050 | optional: true 1051 | 1052 | '@esbuild/linux-ppc64@0.25.2': 1053 | optional: true 1054 | 1055 | '@esbuild/linux-riscv64@0.25.2': 1056 | optional: true 1057 | 1058 | '@esbuild/linux-s390x@0.25.2': 1059 | optional: true 1060 | 1061 | '@esbuild/linux-x64@0.25.2': 1062 | optional: true 1063 | 1064 | '@esbuild/netbsd-arm64@0.25.2': 1065 | optional: true 1066 | 1067 | '@esbuild/netbsd-x64@0.25.2': 1068 | optional: true 1069 | 1070 | '@esbuild/openbsd-arm64@0.25.2': 1071 | optional: true 1072 | 1073 | '@esbuild/openbsd-x64@0.25.2': 1074 | optional: true 1075 | 1076 | '@esbuild/sunos-x64@0.25.2': 1077 | optional: true 1078 | 1079 | '@esbuild/win32-arm64@0.25.2': 1080 | optional: true 1081 | 1082 | '@esbuild/win32-ia32@0.25.2': 1083 | optional: true 1084 | 1085 | '@esbuild/win32-x64@0.25.2': 1086 | optional: true 1087 | 1088 | '@fullhuman/postcss-purgecss@6.0.0(postcss@8.5.3)': 1089 | dependencies: 1090 | postcss: 8.5.3 1091 | purgecss: 6.0.0 1092 | 1093 | '@isaacs/cliui@8.0.2': 1094 | dependencies: 1095 | string-width: 5.1.2 1096 | string-width-cjs: string-width@4.2.3 1097 | strip-ansi: 7.1.0 1098 | strip-ansi-cjs: strip-ansi@6.0.1 1099 | wrap-ansi: 8.1.0 1100 | wrap-ansi-cjs: wrap-ansi@7.0.0 1101 | 1102 | '@jridgewell/gen-mapping@0.3.8': 1103 | dependencies: 1104 | '@jridgewell/set-array': 1.2.1 1105 | '@jridgewell/sourcemap-codec': 1.5.0 1106 | '@jridgewell/trace-mapping': 0.3.25 1107 | 1108 | '@jridgewell/resolve-uri@3.1.2': {} 1109 | 1110 | '@jridgewell/set-array@1.2.1': {} 1111 | 1112 | '@jridgewell/source-map@0.3.6': 1113 | dependencies: 1114 | '@jridgewell/gen-mapping': 0.3.8 1115 | '@jridgewell/trace-mapping': 0.3.25 1116 | 1117 | '@jridgewell/sourcemap-codec@1.5.0': {} 1118 | 1119 | '@jridgewell/trace-mapping@0.3.25': 1120 | dependencies: 1121 | '@jridgewell/resolve-uri': 3.1.2 1122 | '@jridgewell/sourcemap-codec': 1.5.0 1123 | 1124 | '@nodelib/fs.scandir@2.1.5': 1125 | dependencies: 1126 | '@nodelib/fs.stat': 2.0.5 1127 | run-parallel: 1.2.0 1128 | 1129 | '@nodelib/fs.stat@2.0.5': {} 1130 | 1131 | '@nodelib/fs.walk@1.2.8': 1132 | dependencies: 1133 | '@nodelib/fs.scandir': 2.1.5 1134 | fastq: 1.19.1 1135 | 1136 | '@pkgjs/parseargs@0.11.0': 1137 | optional: true 1138 | 1139 | '@rollup/pluginutils@4.2.1': 1140 | dependencies: 1141 | estree-walker: 2.0.2 1142 | picomatch: 2.3.1 1143 | 1144 | '@rollup/rollup-android-arm-eabi@4.40.0': 1145 | optional: true 1146 | 1147 | '@rollup/rollup-android-arm64@4.40.0': 1148 | optional: true 1149 | 1150 | '@rollup/rollup-darwin-arm64@4.40.0': 1151 | optional: true 1152 | 1153 | '@rollup/rollup-darwin-x64@4.40.0': 1154 | optional: true 1155 | 1156 | '@rollup/rollup-freebsd-arm64@4.40.0': 1157 | optional: true 1158 | 1159 | '@rollup/rollup-freebsd-x64@4.40.0': 1160 | optional: true 1161 | 1162 | '@rollup/rollup-linux-arm-gnueabihf@4.40.0': 1163 | optional: true 1164 | 1165 | '@rollup/rollup-linux-arm-musleabihf@4.40.0': 1166 | optional: true 1167 | 1168 | '@rollup/rollup-linux-arm64-gnu@4.40.0': 1169 | optional: true 1170 | 1171 | '@rollup/rollup-linux-arm64-musl@4.40.0': 1172 | optional: true 1173 | 1174 | '@rollup/rollup-linux-loongarch64-gnu@4.40.0': 1175 | optional: true 1176 | 1177 | '@rollup/rollup-linux-powerpc64le-gnu@4.40.0': 1178 | optional: true 1179 | 1180 | '@rollup/rollup-linux-riscv64-gnu@4.40.0': 1181 | optional: true 1182 | 1183 | '@rollup/rollup-linux-riscv64-musl@4.40.0': 1184 | optional: true 1185 | 1186 | '@rollup/rollup-linux-s390x-gnu@4.40.0': 1187 | optional: true 1188 | 1189 | '@rollup/rollup-linux-x64-gnu@4.40.0': 1190 | optional: true 1191 | 1192 | '@rollup/rollup-linux-x64-musl@4.40.0': 1193 | optional: true 1194 | 1195 | '@rollup/rollup-win32-arm64-msvc@4.40.0': 1196 | optional: true 1197 | 1198 | '@rollup/rollup-win32-ia32-msvc@4.40.0': 1199 | optional: true 1200 | 1201 | '@rollup/rollup-win32-x64-msvc@4.40.0': 1202 | optional: true 1203 | 1204 | '@sentry-internal/browser-utils@9.13.0': 1205 | dependencies: 1206 | '@sentry/core': 9.13.0 1207 | 1208 | '@sentry-internal/feedback@9.13.0': 1209 | dependencies: 1210 | '@sentry/core': 9.13.0 1211 | 1212 | '@sentry-internal/replay-canvas@9.13.0': 1213 | dependencies: 1214 | '@sentry-internal/replay': 9.13.0 1215 | '@sentry/core': 9.13.0 1216 | 1217 | '@sentry-internal/replay@9.13.0': 1218 | dependencies: 1219 | '@sentry-internal/browser-utils': 9.13.0 1220 | '@sentry/core': 9.13.0 1221 | 1222 | '@sentry/browser@9.13.0': 1223 | dependencies: 1224 | '@sentry-internal/browser-utils': 9.13.0 1225 | '@sentry-internal/feedback': 9.13.0 1226 | '@sentry-internal/replay': 9.13.0 1227 | '@sentry-internal/replay-canvas': 9.13.0 1228 | '@sentry/core': 9.13.0 1229 | 1230 | '@sentry/core@9.13.0': {} 1231 | 1232 | '@types/estree@1.0.7': {} 1233 | 1234 | '@types/web-bluetooth@0.0.21': {} 1235 | 1236 | '@vitest/expect@3.1.1': 1237 | dependencies: 1238 | '@vitest/spy': 3.1.1 1239 | '@vitest/utils': 3.1.1 1240 | chai: 5.2.0 1241 | tinyrainbow: 2.0.0 1242 | 1243 | '@vitest/mocker@3.1.1(vite@6.3.2(terser@5.39.0))': 1244 | dependencies: 1245 | '@vitest/spy': 3.1.1 1246 | estree-walker: 3.0.3 1247 | magic-string: 0.30.17 1248 | optionalDependencies: 1249 | vite: 6.3.2(terser@5.39.0) 1250 | 1251 | '@vitest/pretty-format@3.1.1': 1252 | dependencies: 1253 | tinyrainbow: 2.0.0 1254 | 1255 | '@vitest/runner@3.1.1': 1256 | dependencies: 1257 | '@vitest/utils': 3.1.1 1258 | pathe: 2.0.3 1259 | 1260 | '@vitest/snapshot@3.1.1': 1261 | dependencies: 1262 | '@vitest/pretty-format': 3.1.1 1263 | magic-string: 0.30.17 1264 | pathe: 2.0.3 1265 | 1266 | '@vitest/spy@3.1.1': 1267 | dependencies: 1268 | tinyspy: 3.0.2 1269 | 1270 | '@vitest/utils@3.1.1': 1271 | dependencies: 1272 | '@vitest/pretty-format': 3.1.1 1273 | loupe: 3.1.3 1274 | tinyrainbow: 2.0.0 1275 | 1276 | acorn@8.14.1: {} 1277 | 1278 | ansi-regex@5.0.1: {} 1279 | 1280 | ansi-regex@6.1.0: {} 1281 | 1282 | ansi-styles@4.3.0: 1283 | dependencies: 1284 | color-convert: 2.0.1 1285 | 1286 | ansi-styles@6.2.1: {} 1287 | 1288 | assertion-error@2.0.1: {} 1289 | 1290 | async@3.2.6: {} 1291 | 1292 | balanced-match@1.0.2: {} 1293 | 1294 | boolbase@1.0.0: {} 1295 | 1296 | brace-expansion@1.1.11: 1297 | dependencies: 1298 | balanced-match: 1.0.2 1299 | concat-map: 0.0.1 1300 | 1301 | brace-expansion@2.0.1: 1302 | dependencies: 1303 | balanced-match: 1.0.2 1304 | 1305 | braces@3.0.3: 1306 | dependencies: 1307 | fill-range: 7.1.1 1308 | 1309 | buffer-from@1.1.2: {} 1310 | 1311 | cac@6.7.14: {} 1312 | 1313 | camel-case@4.1.2: 1314 | dependencies: 1315 | pascal-case: 3.1.2 1316 | tslib: 2.8.1 1317 | 1318 | chai@5.2.0: 1319 | dependencies: 1320 | assertion-error: 2.0.1 1321 | check-error: 2.1.1 1322 | deep-eql: 5.0.2 1323 | loupe: 3.1.3 1324 | pathval: 2.0.0 1325 | 1326 | chalk@4.1.2: 1327 | dependencies: 1328 | ansi-styles: 4.3.0 1329 | supports-color: 7.2.0 1330 | 1331 | check-error@2.1.1: {} 1332 | 1333 | clean-css@5.3.3: 1334 | dependencies: 1335 | source-map: 0.6.1 1336 | 1337 | color-convert@2.0.1: 1338 | dependencies: 1339 | color-name: 1.1.4 1340 | 1341 | color-name@1.1.4: {} 1342 | 1343 | colorette@2.0.20: {} 1344 | 1345 | commander@12.1.0: {} 1346 | 1347 | commander@2.20.3: {} 1348 | 1349 | commander@8.3.0: {} 1350 | 1351 | concat-map@0.0.1: {} 1352 | 1353 | connect-history-api-fallback@1.6.0: {} 1354 | 1355 | consola@2.15.3: {} 1356 | 1357 | cross-spawn@7.0.6: 1358 | dependencies: 1359 | path-key: 3.1.1 1360 | shebang-command: 2.0.0 1361 | which: 2.0.2 1362 | 1363 | css-select@4.3.0: 1364 | dependencies: 1365 | boolbase: 1.0.0 1366 | css-what: 6.1.0 1367 | domhandler: 4.3.1 1368 | domutils: 2.8.0 1369 | nth-check: 2.1.1 1370 | 1371 | css-what@6.1.0: {} 1372 | 1373 | cssesc@3.0.0: {} 1374 | 1375 | debug@4.4.0: 1376 | dependencies: 1377 | ms: 2.1.3 1378 | 1379 | deep-eql@5.0.2: {} 1380 | 1381 | dom-serializer@1.4.1: 1382 | dependencies: 1383 | domelementtype: 2.3.0 1384 | domhandler: 4.3.1 1385 | entities: 2.2.0 1386 | 1387 | domelementtype@2.3.0: {} 1388 | 1389 | domhandler@4.3.1: 1390 | dependencies: 1391 | domelementtype: 2.3.0 1392 | 1393 | domutils@2.8.0: 1394 | dependencies: 1395 | dom-serializer: 1.4.1 1396 | domelementtype: 2.3.0 1397 | domhandler: 4.3.1 1398 | 1399 | dot-case@3.0.4: 1400 | dependencies: 1401 | no-case: 3.0.4 1402 | tslib: 2.8.1 1403 | 1404 | dotenv-expand@8.0.3: {} 1405 | 1406 | dotenv@16.5.0: {} 1407 | 1408 | eastasianwidth@0.2.0: {} 1409 | 1410 | ejs@3.1.10: 1411 | dependencies: 1412 | jake: 10.9.2 1413 | 1414 | emoji-regex@8.0.0: {} 1415 | 1416 | emoji-regex@9.2.2: {} 1417 | 1418 | entities@2.2.0: {} 1419 | 1420 | es-module-lexer@1.6.0: {} 1421 | 1422 | esbuild@0.25.2: 1423 | optionalDependencies: 1424 | '@esbuild/aix-ppc64': 0.25.2 1425 | '@esbuild/android-arm': 0.25.2 1426 | '@esbuild/android-arm64': 0.25.2 1427 | '@esbuild/android-x64': 0.25.2 1428 | '@esbuild/darwin-arm64': 0.25.2 1429 | '@esbuild/darwin-x64': 0.25.2 1430 | '@esbuild/freebsd-arm64': 0.25.2 1431 | '@esbuild/freebsd-x64': 0.25.2 1432 | '@esbuild/linux-arm': 0.25.2 1433 | '@esbuild/linux-arm64': 0.25.2 1434 | '@esbuild/linux-ia32': 0.25.2 1435 | '@esbuild/linux-loong64': 0.25.2 1436 | '@esbuild/linux-mips64el': 0.25.2 1437 | '@esbuild/linux-ppc64': 0.25.2 1438 | '@esbuild/linux-riscv64': 0.25.2 1439 | '@esbuild/linux-s390x': 0.25.2 1440 | '@esbuild/linux-x64': 0.25.2 1441 | '@esbuild/netbsd-arm64': 0.25.2 1442 | '@esbuild/netbsd-x64': 0.25.2 1443 | '@esbuild/openbsd-arm64': 0.25.2 1444 | '@esbuild/openbsd-x64': 0.25.2 1445 | '@esbuild/sunos-x64': 0.25.2 1446 | '@esbuild/win32-arm64': 0.25.2 1447 | '@esbuild/win32-ia32': 0.25.2 1448 | '@esbuild/win32-x64': 0.25.2 1449 | 1450 | estree-walker@2.0.2: {} 1451 | 1452 | estree-walker@3.0.3: 1453 | dependencies: 1454 | '@types/estree': 1.0.7 1455 | 1456 | expect-type@1.2.1: {} 1457 | 1458 | fast-glob@3.3.3: 1459 | dependencies: 1460 | '@nodelib/fs.stat': 2.0.5 1461 | '@nodelib/fs.walk': 1.2.8 1462 | glob-parent: 5.1.2 1463 | merge2: 1.4.1 1464 | micromatch: 4.0.8 1465 | 1466 | fastq@1.19.1: 1467 | dependencies: 1468 | reusify: 1.1.0 1469 | 1470 | fdir@6.4.3(picomatch@4.0.2): 1471 | optionalDependencies: 1472 | picomatch: 4.0.2 1473 | 1474 | filelist@1.0.4: 1475 | dependencies: 1476 | minimatch: 5.1.6 1477 | 1478 | fill-range@7.1.1: 1479 | dependencies: 1480 | to-regex-range: 5.0.1 1481 | 1482 | foreground-child@3.3.1: 1483 | dependencies: 1484 | cross-spawn: 7.0.6 1485 | signal-exit: 4.1.0 1486 | 1487 | fs-extra@10.1.0: 1488 | dependencies: 1489 | graceful-fs: 4.2.11 1490 | jsonfile: 6.1.0 1491 | universalify: 2.0.1 1492 | 1493 | fsevents@2.3.3: 1494 | optional: true 1495 | 1496 | glob-parent@5.1.2: 1497 | dependencies: 1498 | is-glob: 4.0.3 1499 | 1500 | glob@10.4.5: 1501 | dependencies: 1502 | foreground-child: 3.3.1 1503 | jackspeak: 3.4.3 1504 | minimatch: 9.0.5 1505 | minipass: 7.1.2 1506 | package-json-from-dist: 1.0.1 1507 | path-scurry: 1.11.1 1508 | 1509 | graceful-fs@4.2.11: {} 1510 | 1511 | has-flag@4.0.0: {} 1512 | 1513 | he@1.2.0: {} 1514 | 1515 | html-minifier-terser@6.1.0: 1516 | dependencies: 1517 | camel-case: 4.1.2 1518 | clean-css: 5.3.3 1519 | commander: 8.3.0 1520 | he: 1.2.0 1521 | param-case: 3.0.4 1522 | relateurl: 0.2.7 1523 | terser: 5.39.0 1524 | 1525 | is-extglob@2.1.1: {} 1526 | 1527 | is-fullwidth-code-point@3.0.0: {} 1528 | 1529 | is-glob@4.0.3: 1530 | dependencies: 1531 | is-extglob: 2.1.1 1532 | 1533 | is-number@7.0.0: {} 1534 | 1535 | isexe@2.0.0: {} 1536 | 1537 | jackspeak@3.4.3: 1538 | dependencies: 1539 | '@isaacs/cliui': 8.0.2 1540 | optionalDependencies: 1541 | '@pkgjs/parseargs': 0.11.0 1542 | 1543 | jake@10.9.2: 1544 | dependencies: 1545 | async: 3.2.6 1546 | chalk: 4.1.2 1547 | filelist: 1.0.4 1548 | minimatch: 3.1.2 1549 | 1550 | jsonfile@6.1.0: 1551 | dependencies: 1552 | universalify: 2.0.1 1553 | optionalDependencies: 1554 | graceful-fs: 4.2.11 1555 | 1556 | loupe@3.1.3: {} 1557 | 1558 | lower-case@2.0.2: 1559 | dependencies: 1560 | tslib: 2.8.1 1561 | 1562 | lru-cache@10.4.3: {} 1563 | 1564 | magic-string@0.30.17: 1565 | dependencies: 1566 | '@jridgewell/sourcemap-codec': 1.5.0 1567 | 1568 | merge2@1.4.1: {} 1569 | 1570 | micromatch@4.0.8: 1571 | dependencies: 1572 | braces: 3.0.3 1573 | picomatch: 2.3.1 1574 | 1575 | minimatch@3.1.2: 1576 | dependencies: 1577 | brace-expansion: 1.1.11 1578 | 1579 | minimatch@5.1.6: 1580 | dependencies: 1581 | brace-expansion: 2.0.1 1582 | 1583 | minimatch@9.0.5: 1584 | dependencies: 1585 | brace-expansion: 2.0.1 1586 | 1587 | minipass@7.1.2: {} 1588 | 1589 | ms@2.1.3: {} 1590 | 1591 | nanoid@3.3.11: {} 1592 | 1593 | no-case@3.0.4: 1594 | dependencies: 1595 | lower-case: 2.0.2 1596 | tslib: 2.8.1 1597 | 1598 | node-html-parser@5.4.2: 1599 | dependencies: 1600 | css-select: 4.3.0 1601 | he: 1.2.0 1602 | 1603 | nth-check@2.1.1: 1604 | dependencies: 1605 | boolbase: 1.0.0 1606 | 1607 | package-json-from-dist@1.0.1: {} 1608 | 1609 | param-case@3.0.4: 1610 | dependencies: 1611 | dot-case: 3.0.4 1612 | tslib: 2.8.1 1613 | 1614 | pascal-case@3.1.2: 1615 | dependencies: 1616 | no-case: 3.0.4 1617 | tslib: 2.8.1 1618 | 1619 | path-key@3.1.1: {} 1620 | 1621 | path-scurry@1.11.1: 1622 | dependencies: 1623 | lru-cache: 10.4.3 1624 | minipass: 7.1.2 1625 | 1626 | pathe@0.2.0: {} 1627 | 1628 | pathe@2.0.3: {} 1629 | 1630 | pathval@2.0.0: {} 1631 | 1632 | picocolors@1.1.1: {} 1633 | 1634 | picomatch@2.3.1: {} 1635 | 1636 | picomatch@4.0.2: {} 1637 | 1638 | postcss-selector-parser@6.1.2: 1639 | dependencies: 1640 | cssesc: 3.0.0 1641 | util-deprecate: 1.0.2 1642 | 1643 | postcss@8.5.3: 1644 | dependencies: 1645 | nanoid: 3.3.11 1646 | picocolors: 1.1.1 1647 | source-map-js: 1.2.1 1648 | 1649 | purgecss@6.0.0: 1650 | dependencies: 1651 | commander: 12.1.0 1652 | glob: 10.4.5 1653 | postcss: 8.5.3 1654 | postcss-selector-parser: 6.1.2 1655 | 1656 | queue-microtask@1.2.3: {} 1657 | 1658 | relateurl@0.2.7: {} 1659 | 1660 | reusify@1.1.0: {} 1661 | 1662 | rollup@4.40.0: 1663 | dependencies: 1664 | '@types/estree': 1.0.7 1665 | optionalDependencies: 1666 | '@rollup/rollup-android-arm-eabi': 4.40.0 1667 | '@rollup/rollup-android-arm64': 4.40.0 1668 | '@rollup/rollup-darwin-arm64': 4.40.0 1669 | '@rollup/rollup-darwin-x64': 4.40.0 1670 | '@rollup/rollup-freebsd-arm64': 4.40.0 1671 | '@rollup/rollup-freebsd-x64': 4.40.0 1672 | '@rollup/rollup-linux-arm-gnueabihf': 4.40.0 1673 | '@rollup/rollup-linux-arm-musleabihf': 4.40.0 1674 | '@rollup/rollup-linux-arm64-gnu': 4.40.0 1675 | '@rollup/rollup-linux-arm64-musl': 4.40.0 1676 | '@rollup/rollup-linux-loongarch64-gnu': 4.40.0 1677 | '@rollup/rollup-linux-powerpc64le-gnu': 4.40.0 1678 | '@rollup/rollup-linux-riscv64-gnu': 4.40.0 1679 | '@rollup/rollup-linux-riscv64-musl': 4.40.0 1680 | '@rollup/rollup-linux-s390x-gnu': 4.40.0 1681 | '@rollup/rollup-linux-x64-gnu': 4.40.0 1682 | '@rollup/rollup-linux-x64-musl': 4.40.0 1683 | '@rollup/rollup-win32-arm64-msvc': 4.40.0 1684 | '@rollup/rollup-win32-ia32-msvc': 4.40.0 1685 | '@rollup/rollup-win32-x64-msvc': 4.40.0 1686 | fsevents: 2.3.3 1687 | 1688 | run-parallel@1.2.0: 1689 | dependencies: 1690 | queue-microtask: 1.2.3 1691 | 1692 | shebang-command@2.0.0: 1693 | dependencies: 1694 | shebang-regex: 3.0.0 1695 | 1696 | shebang-regex@3.0.0: {} 1697 | 1698 | siginfo@2.0.0: {} 1699 | 1700 | signal-exit@4.1.0: {} 1701 | 1702 | source-map-js@1.2.1: {} 1703 | 1704 | source-map-support@0.5.21: 1705 | dependencies: 1706 | buffer-from: 1.1.2 1707 | source-map: 0.6.1 1708 | 1709 | source-map@0.6.1: {} 1710 | 1711 | stackback@0.0.2: {} 1712 | 1713 | std-env@3.9.0: {} 1714 | 1715 | string-width@4.2.3: 1716 | dependencies: 1717 | emoji-regex: 8.0.0 1718 | is-fullwidth-code-point: 3.0.0 1719 | strip-ansi: 6.0.1 1720 | 1721 | string-width@5.1.2: 1722 | dependencies: 1723 | eastasianwidth: 0.2.0 1724 | emoji-regex: 9.2.2 1725 | strip-ansi: 7.1.0 1726 | 1727 | strip-ansi@6.0.1: 1728 | dependencies: 1729 | ansi-regex: 5.0.1 1730 | 1731 | strip-ansi@7.1.0: 1732 | dependencies: 1733 | ansi-regex: 6.1.0 1734 | 1735 | supports-color@7.2.0: 1736 | dependencies: 1737 | has-flag: 4.0.0 1738 | 1739 | terser@5.39.0: 1740 | dependencies: 1741 | '@jridgewell/source-map': 0.3.6 1742 | acorn: 8.14.1 1743 | commander: 2.20.3 1744 | source-map-support: 0.5.21 1745 | 1746 | tinybench@2.9.0: {} 1747 | 1748 | tinyexec@0.3.2: {} 1749 | 1750 | tinyglobby@0.2.12: 1751 | dependencies: 1752 | fdir: 6.4.3(picomatch@4.0.2) 1753 | picomatch: 4.0.2 1754 | 1755 | tinypool@1.0.2: {} 1756 | 1757 | tinyrainbow@2.0.0: {} 1758 | 1759 | tinyspy@3.0.2: {} 1760 | 1761 | to-regex-range@5.0.1: 1762 | dependencies: 1763 | is-number: 7.0.0 1764 | 1765 | tslib@2.8.1: {} 1766 | 1767 | typescript@5.8.3: {} 1768 | 1769 | universalify@2.0.1: {} 1770 | 1771 | util-deprecate@1.0.2: {} 1772 | 1773 | vite-node@3.1.1(terser@5.39.0): 1774 | dependencies: 1775 | cac: 6.7.14 1776 | debug: 4.4.0 1777 | es-module-lexer: 1.6.0 1778 | pathe: 2.0.3 1779 | vite: 6.3.2(terser@5.39.0) 1780 | transitivePeerDependencies: 1781 | - '@types/node' 1782 | - jiti 1783 | - less 1784 | - lightningcss 1785 | - sass 1786 | - sass-embedded 1787 | - stylus 1788 | - sugarss 1789 | - supports-color 1790 | - terser 1791 | - tsx 1792 | - yaml 1793 | 1794 | vite-plugin-html@3.2.2(vite@6.3.2(terser@5.39.0)): 1795 | dependencies: 1796 | '@rollup/pluginutils': 4.2.1 1797 | colorette: 2.0.20 1798 | connect-history-api-fallback: 1.6.0 1799 | consola: 2.15.3 1800 | dotenv: 16.5.0 1801 | dotenv-expand: 8.0.3 1802 | ejs: 3.1.10 1803 | fast-glob: 3.3.3 1804 | fs-extra: 10.1.0 1805 | html-minifier-terser: 6.1.0 1806 | node-html-parser: 5.4.2 1807 | pathe: 0.2.0 1808 | vite: 6.3.2(terser@5.39.0) 1809 | 1810 | vite-plugin-singlefile@2.2.0(rollup@4.40.0)(vite@6.3.2(terser@5.39.0)): 1811 | dependencies: 1812 | micromatch: 4.0.8 1813 | rollup: 4.40.0 1814 | vite: 6.3.2(terser@5.39.0) 1815 | 1816 | vite@6.3.2(terser@5.39.0): 1817 | dependencies: 1818 | esbuild: 0.25.2 1819 | fdir: 6.4.3(picomatch@4.0.2) 1820 | picomatch: 4.0.2 1821 | postcss: 8.5.3 1822 | rollup: 4.40.0 1823 | tinyglobby: 0.2.12 1824 | optionalDependencies: 1825 | fsevents: 2.3.3 1826 | terser: 5.39.0 1827 | 1828 | vitest@3.1.1(terser@5.39.0): 1829 | dependencies: 1830 | '@vitest/expect': 3.1.1 1831 | '@vitest/mocker': 3.1.1(vite@6.3.2(terser@5.39.0)) 1832 | '@vitest/pretty-format': 3.1.1 1833 | '@vitest/runner': 3.1.1 1834 | '@vitest/snapshot': 3.1.1 1835 | '@vitest/spy': 3.1.1 1836 | '@vitest/utils': 3.1.1 1837 | chai: 5.2.0 1838 | debug: 4.4.0 1839 | expect-type: 1.2.1 1840 | magic-string: 0.30.17 1841 | pathe: 2.0.3 1842 | std-env: 3.9.0 1843 | tinybench: 2.9.0 1844 | tinyexec: 0.3.2 1845 | tinypool: 1.0.2 1846 | tinyrainbow: 2.0.0 1847 | vite: 6.3.2(terser@5.39.0) 1848 | vite-node: 3.1.1(terser@5.39.0) 1849 | why-is-node-running: 2.3.0 1850 | transitivePeerDependencies: 1851 | - jiti 1852 | - less 1853 | - lightningcss 1854 | - msw 1855 | - sass 1856 | - sass-embedded 1857 | - stylus 1858 | - sugarss 1859 | - supports-color 1860 | - terser 1861 | - tsx 1862 | - yaml 1863 | 1864 | wabt@1.0.37: {} 1865 | 1866 | water.css@2.1.1: {} 1867 | 1868 | which@2.0.2: 1869 | dependencies: 1870 | isexe: 2.0.0 1871 | 1872 | why-is-node-running@2.3.0: 1873 | dependencies: 1874 | siginfo: 2.0.0 1875 | stackback: 0.0.2 1876 | 1877 | wrap-ansi@7.0.0: 1878 | dependencies: 1879 | ansi-styles: 4.3.0 1880 | string-width: 4.2.3 1881 | strip-ansi: 6.0.1 1882 | 1883 | wrap-ansi@8.1.0: 1884 | dependencies: 1885 | ansi-styles: 6.2.1 1886 | string-width: 5.1.2 1887 | strip-ansi: 7.1.0 1888 | --------------------------------------------------------------------------------