From 9ba5b12dac211d8592f2d6e5d7aec2f0804fd34a Mon Sep 17 00:00:00 2001 From: Zoe <62722391+juls0730@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:48:00 -0600 Subject: [PATCH] Clean up code. Reorganize files. Port stuff from other branches. + more This turns the project into a monorepo using pnpm workspaces, dramatically simplifying the build process. It also fixes a lot of bugs and just generally makes the codebase a lot cleaner. --- .gitignore | 1 + README.md | 6 +- example-app/.example.env | 2 +- example-app/README.md | 4 +- example-app/app/pages/index.vue | 417 +- example-app/app/pages/widget.vue | 38 - example-app/app/utils/worker-name.ts | 6 - example-app/app/utils/worker.ts | 170 - example-app/config.toml | 4 +- example-app/nuxt.config.ts | 12 +- example-app/package-lock.json | 1455 ++- example-app/package.json | 12 +- example-app/server/api/pow/challenge.post.ts | 23 +- justfile | 26 - package.json | 17 + packages/lib/package-lock.json | 3 - packages/lib/package.json | 2 +- packages/lib/src/solver.ts | 19 +- packages/lib/src/validator.ts | 12 +- packages/lib/vite.config.ts | 2 +- {solver => packages/solver}/.gitignore | 0 {solver => packages/solver}/build.zig | 28 +- packages/solver/package.json | 11 + .../solver/src/argon2.zig | 19 +- {solver => packages/solver}/src/solver.zig | 104 +- packages/solver/src/utils.zig | 43 + {solver => packages/solver}/src/validator.zig | 37 +- packages/widget/package-lock.json | 12 +- packages/widget/package.json | 4 +- .../widget/src/{pow-captcha.ts => captcha.ts} | 295 +- packages/widget/src/entry.ts | 2 +- packages/widget/src/solver-worker.ts | 119 +- packages/widget/src/types/worker.ts | 60 - packages/widget/src/vite-env.d.ts | 2 +- packages/widget/vite.config.ts | 7 +- pnpm-lock.yaml | 8648 +++++++++++++++++ pnpm-workspace.yaml | 10 + solver/src/kctf.zig | 7 - 38 files changed, 10459 insertions(+), 1180 deletions(-) delete mode 100644 example-app/app/pages/widget.vue delete mode 100644 example-app/app/utils/worker-name.ts delete mode 100644 example-app/app/utils/worker.ts delete mode 100644 justfile create mode 100644 package.json rename {solver => packages/solver}/.gitignore (100%) rename {solver => packages/solver}/build.zig (56%) create mode 100644 packages/solver/package.json rename solver/src/hasher.zig => packages/solver/src/argon2.zig (51%) rename {solver => packages/solver}/src/solver.zig (58%) create mode 100644 packages/solver/src/utils.zig rename {solver => packages/solver}/src/validator.zig (66%) rename packages/widget/src/{pow-captcha.ts => captcha.ts} (57%) delete mode 100644 packages/widget/src/types/worker.ts create mode 100644 pnpm-lock.yaml create mode 100644 pnpm-workspace.yaml delete mode 100644 solver/src/kctf.zig diff --git a/.gitignore b/.gitignore index e69de29..b512c09 100644 --- a/.gitignore +++ b/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/README.md b/README.md index 5fece17..9f14a13 100644 --- a/README.md +++ b/README.md @@ -14,6 +14,6 @@ This is the impost monorepo, containing the following packages: - `@impost/lib`: A library that can be used to generate, solve, and verify proofs. -It also contains a `solver` package, which is the PoW solver written in Zig, -`@impost/lib` is built on top of, an example of how to use the solver in a -nuxt 3 project. +It also contains a `solver` package, which is the Zig PoW solver that +`@impost/lib` is built on top of, an example of how to use the solver in +example-app diff --git a/example-app/.example.env b/example-app/.example.env index 771536b..b30114a 100644 --- a/example-app/.example.env +++ b/example-app/.example.env @@ -1 +1 @@ -YAPTCHA_HMAC_SECRET=xxx # openssl rand -base64 32 +IMPOST_HMAC_SECRET=xxx # openssl rand -base64 32 diff --git a/example-app/README.md b/example-app/README.md index c605814..fa4b924 100644 --- a/example-app/README.md +++ b/example-app/README.md @@ -1,10 +1,10 @@ -# YAPTCHA +# IMPOST Yet Another Pow capTCHA. ## What is this -YAPTCHA is a proof of work based challenge-response system that is designed to +IMPOST is a proof of work based challenge-response system that is designed to ward off spam and abuse. diff --git a/example-app/app/pages/index.vue b/example-app/app/pages/index.vue index e1609f6..ce598fe 100644 --- a/example-app/app/pages/index.vue +++ b/example-app/app/pages/index.vue @@ -1,411 +1,30 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/example-app/app/pages/widget.vue b/example-app/app/pages/widget.vue deleted file mode 100644 index af5a7e7..0000000 --- a/example-app/app/pages/widget.vue +++ /dev/null @@ -1,38 +0,0 @@ - - - \ No newline at end of file diff --git a/example-app/app/utils/worker-name.ts b/example-app/app/utils/worker-name.ts deleted file mode 100644 index 7d7e09f..0000000 --- a/example-app/app/utils/worker-name.ts +++ /dev/null @@ -1,6 +0,0 @@ -const adjectives = ['swift', 'silent', 'hidden', 'clever', 'brave', 'sharp', 'shadow', 'crimson', 'bright', 'quiet', 'loud', 'happy', 'dark', 'evil', 'good', 'intelligent', 'lovely', 'mysterious', 'peaceful', 'powerful', 'pure', 'quiet', 'shiny', 'sleepy', 'strong', 'sweet', 'tall', 'warm', 'gentle', 'kind', 'nice', 'polite', 'rough', 'rude', 'scary', 'shy', 'silly', 'smart', 'strange', 'tough', 'ugly', 'vivid', 'wicked', 'wise', 'young', 'sleepy']; -const nouns = ['fox', 'river', 'stone', 'cipher', 'link', 'comet', 'falcon', 'signal', 'anchor', 'spark', 'stone', 'comet', 'rocket', 'snake', 'snail', 'shark', 'elephant', 'cat', 'dog', 'whale', 'orca', 'cactus', 'flower', 'frog', 'toad', 'apple', 'strawberry', 'raspberry', 'lemon', 'bot', 'gopher', 'dinosaur', 'racoon', 'penguin', 'chameleon', 'atom', 'particle', 'witch', 'wizard', 'warlock', 'deer'] - -export function getWorkerName() { - return `${adjectives[Math.floor(Math.random() * adjectives.length)]}-${nouns[Math.floor(Math.random() * nouns.length)]}`; -} \ No newline at end of file diff --git a/example-app/app/utils/worker.ts b/example-app/app/utils/worker.ts deleted file mode 100644 index b5d05db..0000000 --- a/example-app/app/utils/worker.ts +++ /dev/null @@ -1,170 +0,0 @@ -// This worker just sits on another thread and waits for message to solve -// challenges so that we dont block the render thread - -import { - type WorkerRequest, - type SolutionMessage, - WorkerMessageType, - WorkerResponseType, - ChallengeStrategy, -} from "~/types/pow"; - -const worker_name = getWorkerName(); -let solver: SolverModule | null = null; - -let atomic_nonce: Int32Array | null = null; -let atomic_solution: Int32Array | null = null; - -async function loadWasmSolver(module: WebAssembly.Module) { - if (atomic_nonce === null || atomic_solution === null) { - throw createError("Atomics not initialized"); - } - - console.debug(`[${worker_name}]: Loading WASM solver`); - - solver = await WebAssembly.instantiate(module, { - env: { - __get_solution: () => Atomics.load(atomic_solution!, 0), - __set_solution: (value: number) => Atomics.store(atomic_solution!, 0, value), - __cmpxchg_solution: (expected: number, replacement: number) => Atomics.compareExchange(atomic_solution!, 0, expected, replacement), - __fetch_add_nonce: (value: number) => Atomics.add(atomic_nonce!, 0, value), - __log: (ptr: number, len: number) => { - const string_data = new Uint8Array(solver!.exports.memory.buffer, ptr, len); - console.log(`[${worker_name}]: ${new TextDecoder().decode(string_data)}`); - }, - } - }) as unknown as SolverModule; - console.debug(`[${worker_name}]: WASM solver loaded`); -} - -onmessage = async (event: MessageEvent) => { - if (event.data.type === WorkerMessageType.Init) { - console.log(`[${worker_name}]: Initializing...`); - - atomic_nonce = new Int32Array(event.data.sab, 0, 1); - atomic_solution = new Int32Array(event.data.sab, 4, 1); - - try { - await loadWasmSolver(event.data.module); - } catch (error: any) { - console.error(`[${worker_name}]: Failed to load WASM solver:`, error); - postMessage({ - type: WorkerResponseType.Error, - error: `Could not load WASM solver: ${error.message}`, - } as SolutionMessage); - return; - } - - if (!solver) { - console.error(`[${worker_name}]: Failed to load WASM solver`); - postMessage({ - type: WorkerResponseType.Error, - error: "Failed to load WASM solver", - } as SolutionMessage); - return; - } - - postMessage({ - type: WorkerResponseType.Ok, - } as SolutionMessage); - return; - } - - if (!solver) { - postMessage({ - type: WorkerResponseType.Error, - error: "WASM solver not loaded", - } as SolutionMessage); - return; - } - - const { strategy } = event.data; - - const encoder = new TextEncoder(); - - let solution: number; - let target: string = event.data.target; - let target_bytes, target_ptr; - let memory; - switch (strategy) { - case ChallengeStrategy.LeadingZeroes: - const { difficulty } = event.data; - console.debug(`[${worker_name}]: recieved ${strategy} challenge: ${target}, difficulty: ${difficulty}`); - - target_bytes = encoder.encode(target); - - target_ptr = solver.exports.malloc(target_bytes.length); - if (target_ptr === 0 || target_ptr === null) { - console.error(`[${worker_name}]: Failed to allocate memory for challenge string`); - postMessage({ - type: WorkerResponseType.Error, - error: "Failed to allocate memory for challenge string", - } as SolutionMessage); - return; - } - - memory = new Uint8Array(solver.exports.memory.buffer); - memory.set(target_bytes, target_ptr); - - solution = solver.exports.solve_leaading_zeroes_challenge( - target_ptr, - target.length, - difficulty, - ); - console.debug(`[${worker_name}]: WASM solver found nonce: ${solution}`); - break; - case ChallengeStrategy.TargetNumber: - const { salt } = event.data; - console.debug(`[${worker_name}]: recieved ${strategy} challenge: ${target}, salt: ${salt}`); - - const salt_bytes = encoder.encode(salt); - target_bytes = encoder.encode(target); - - const salt_ptr = solver.exports.malloc(salt_bytes.length); - if (salt_ptr === 0 || salt_ptr === null) { - console.error(`[${worker_name}]: Failed to allocate memory for salt string`); - postMessage({ - type: WorkerResponseType.Error, - error: "Failed to allocate memory for salt string", - } as SolutionMessage); - return; - } - - target_ptr = solver.exports.malloc(target_bytes.length); - if (target_ptr === 0 || target_ptr === null) { - console.error(`[${worker_name}]: Failed to allocate memory for target string`); - postMessage({ - type: WorkerResponseType.Error, - error: "Failed to allocate memory for target string", - } as SolutionMessage); - return; - } - - memory = new Uint8Array(solver.exports.memory.buffer); - memory.set(salt_bytes, salt_ptr); - memory.set(target_bytes, target_ptr); - - solution = solver.exports.solve_target_number_challenge( - target_ptr, - target_bytes.length, - salt_ptr, - salt_bytes.length, - ); - console.debug(`[${worker_name}]: WASM solver found nonce: ${solution}`); - - break; - } - - // we are just assuming that if its less than -1, its the min i32 - if (solution < 0) { - return postMessage({ - type: WorkerResponseType.Error, - error: "failed to solve challenge", - } as SolutionMessage); - } - - postMessage({ - type: WorkerResponseType.Solution, - nonce: solution === -1 ? null : solution.toString() - } as SolutionMessage); -}; diff --git a/example-app/config.toml b/example-app/config.toml index 175d496..4b8d716 100644 --- a/example-app/config.toml +++ b/example-app/config.toml @@ -1,7 +1,7 @@ strategy = "target_number" [leading_zeroes] -difficulty = 4 +difficulty = 2 [target_number] -max_number = 10000 +max_number = 192 diff --git a/example-app/nuxt.config.ts b/example-app/nuxt.config.ts index 9e2bc96..843a6d0 100644 --- a/example-app/nuxt.config.ts +++ b/example-app/nuxt.config.ts @@ -19,7 +19,7 @@ export default defineNuxtConfig({ } }, }, - modules: ['@unocss/nuxt', ['nuxt-ssr-lit', { litElementPrefix: 'pow-' }]], + modules: ['@unocss/nuxt', ['nuxt-ssr-lit', { litElementPrefix: 'impost-' }]], nitro: { moduleSideEffects: ["@impost/widget"], @@ -28,17 +28,9 @@ export default defineNuxtConfig({ }, }, - imports: { - transform: { - // only necessary in the monorepo since vite is going out to the - // source of the widget and transforming it and clobbering it. - exclude: [/impost/], - } - }, - vue: { compilerOptions: { - isCustomElement: (tag) => tag.includes('pow-'), + isCustomElement: (tag) => tag.startsWith('impost-'), }, }, diff --git a/example-app/package-lock.json b/example-app/package-lock.json index 133031d..0d19301 100644 --- a/example-app/package-lock.json +++ b/example-app/package-lock.json @@ -8,10 +8,7 @@ "dependencies": { "@impost/lib": "file:../packages/lib", "@impost/widget": "file:../packages/widget", - "@lit/reactive-element": "^2.1.1", "js-toml": "^1.0.2", - "lit-element": "^4.2.1", - "lit-html": "^3.3.1", "nuxt": "latest", "nuxt-ssr-lit": "1.6.32", "zod": "^4.1.12" @@ -41,6 +38,9 @@ "name": "@impost/lib", "version": "0.1.0", "license": "BSL-1.0", + "dependencies": { + "uuidv7": "^1.0.2" + }, "devDependencies": { "oxc-minify": "^0.97.0", "tslib": "^2.6.2", @@ -53,6 +53,9 @@ "name": "@impost/widget", "version": "0.1.0", "license": "BSL-1.0", + "dependencies": { + "comlink": "^4.4.2" + }, "devDependencies": { "@types/node": "^20.11.24", "lit": "^3.1.2", @@ -110,6 +113,7 @@ "node_modules/@babel/core": { "version": "7.28.5", "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -525,6 +529,293 @@ "version": "0.1.1", "license": "MIT" }, + "node_modules/@emnapi/core": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.7.1.tgz", + "integrity": "sha512-o1uhUASyo921r2XtHYOHy7gdkGLge8ghBEQHMWmyJFoXlpU58kIrhhN3w26lpQb6dspetweapMn2CSNwQ8I4wg==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/wasi-threads": "1.1.0", + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/runtime": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.7.1.tgz", + "integrity": "sha512-PVtJr5CmLwYAU9PZDMITZoR5iAOShYREoR45EyyLrbntV50mdePTgUn4AmOw90Ifcj+x2kRjdzr1HP3RrNiHGA==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@emnapi/wasi-threads": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.1.0.tgz", + "integrity": "sha512-WI0DdZ8xFSbgMjR1sFsKABJ/C5OnRrjT06JXbZKexJGrDuPTzZdDYfFlsgcCXCyf+suG5QU2e/y1Wo2V/OapLQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", + "cpu": [ + "mips64el" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/linux-x64": { "version": "0.25.12", "cpu": [ @@ -539,6 +830,150 @@ "node": ">=18" } }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@iconify/types": { "version": "2.0.0", "dev": true, @@ -738,6 +1173,18 @@ "node": ">=8" } }, + "node_modules/@napi-rs/wasm-runtime": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.0.7.tgz", + "integrity": "sha512-SeDnOO0Tk7Okiq6DbXmmBODgOAb9dp9gjlphokTUxmt8U3liIP1ZsozBahH69j/RJv+Rfs6IwUKHTgQYJ/HBAw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.5.0", + "@emnapi/runtime": "^1.5.0", + "@tybys/wasm-util": "^0.10.1" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "license": "MIT", @@ -1082,6 +1529,166 @@ } } }, + "node_modules/@oxc-minify/binding-android-arm64": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-android-arm64/-/binding-android-arm64-0.96.0.tgz", + "integrity": "sha512-lzeIEMu/v6Y+La5JSesq4hvyKtKBq84cgQpKYTYM/yGuNk2tfd5Ha31hnC+mTh48lp/5vZH+WBfjVUjjINCfug==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-darwin-arm64": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-darwin-arm64/-/binding-darwin-arm64-0.96.0.tgz", + "integrity": "sha512-i0LkJAUXb4BeBFrJQbMKQPoxf8+cFEffDyLSb7NEzzKuPcH8qrVsnEItoOzeAdYam8Sr6qCHVwmBNEQzl7PWpw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-darwin-x64": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-darwin-x64/-/binding-darwin-x64-0.96.0.tgz", + "integrity": "sha512-C5vI0WPR+KPIFAD5LMOJk2J8iiT+Nv65vDXmemzXEXouzfEOLYNqnW+u6NSsccpuZHHWAiLyPFkYvKFduveAUQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-freebsd-x64": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-freebsd-x64/-/binding-freebsd-x64-0.96.0.tgz", + "integrity": "sha512-3//5DNx+xUjVBMLLk2sl6hfe4fwfENJtjVQUBXjxzwPuv8xgZUqASG4cRG3WqG5Qe8dV6SbCI4EgKQFjO4KCZA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-arm-gnueabihf": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.96.0.tgz", + "integrity": "sha512-WXChFKV7VdDk1NePDK1J31cpSvxACAVztJ7f7lJVYBTkH+iz5D0lCqPcE7a9eb7nC3xvz4yk7DM6dA9wlUQkQg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-arm-musleabihf": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.96.0.tgz", + "integrity": "sha512-7B18glYMX4Z/YoqgE3VRLs/2YhVLxlxNKSgrtsRpuR8xv58xca+hEhiFwZN1Rn+NSMZ29Z33LWD7iYWnqYFvRA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-arm64-gnu": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.96.0.tgz", + "integrity": "sha512-Yl+KcTldsEJNcaYxxonwAXZ2q3gxIzn3kXYQWgKWdaGIpNhOCWqF+KE5WLsldoh5Ro5SHtomvb8GM6cXrIBMog==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-arm64-musl": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.96.0.tgz", + "integrity": "sha512-rNqoFWOWaxwMmUY5fspd/h5HfvgUlA3sv9CUdA2MpnHFiyoJNovR7WU8tGh+Yn0qOAs0SNH0a05gIthHig14IA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-riscv64-gnu": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.96.0.tgz", + "integrity": "sha512-3paajIuzGnukHwSI3YBjYVqbd72pZd8NJxaayaNFR0AByIm8rmIT5RqFXbq8j2uhtpmNdZRXiu0em1zOmIScWA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-linux-s390x-gnu": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.96.0.tgz", + "integrity": "sha512-9ESrpkB2XG0lQ89JlsxlZa86iQCOs+jkDZLl6O+u5wb7ynUy21bpJJ1joauCOSYIOUlSy3+LbtJLiqi7oSQt5Q==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@oxc-minify/binding-linux-x64-gnu": { "version": "0.96.0", "cpu": [ @@ -1110,6 +1717,214 @@ "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/@oxc-minify/binding-wasm32-wasi": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-wasm32-wasi/-/binding-wasm32-wasi-0.96.0.tgz", + "integrity": "sha512-bjGDjkGzo3GWU9Vg2qiFUrfoo5QxojPNV/2RHTlbIB5FWkkV4ExVjsfyqihFiAuj0NXIZqd2SAiEq9htVd3RFw==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.7" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-minify/binding-win32-arm64-msvc": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.96.0.tgz", + "integrity": "sha512-4L4DlHUT47qMWQuTyUghpncR3NZHWtxvd0G1KgSjVgXf+cXzFdWQCWZZtCU0yrmOoVCNUf4S04IFCJyAe+Ie7A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-minify/binding-win32-x64-msvc": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-minify/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.96.0.tgz", + "integrity": "sha512-T2ijfqZLpV2bgGGocXV4SXTuMoouqN0asYTIm+7jVOLvT5XgDogf3ZvCmiEnSWmxl21+r5wHcs8voU2iUROXAg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-android-arm64": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.96.0.tgz", + "integrity": "sha512-CofbPOiW1PG+hi8bgElJPK0ioHfw8nt4Vw9d+Q9JuMhygS6LbQyu1W6tIFZ1OPFofeFRdWus3vD29FBx+tvFOA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-darwin-arm64": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.96.0.tgz", + "integrity": "sha512-+HZ2L1a/1BsUXYik8XqQwT2Tl5Z3jRQ/RRQiPV9UsB2skKyd91NLDlQlMpdhjLGs9Qe7Y42unFjRg2iHjIiwnw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-darwin-x64": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.96.0.tgz", + "integrity": "sha512-GC8wH1W0XaCLyTeGsmyaMdnItiYQkqfTcn9Ygc55AWI+m11lCjQeoKDIsDCm/QwrKLCN07u3WWWsuPs5ubfXpA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-freebsd-x64": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.96.0.tgz", + "integrity": "sha512-8SeXi2FmlN15uPY5oM03cua5RXBDYmY34Uewongv6RUiAaU/kWxLvzuijpyNC+yQ1r4fC2LbWJhAsKpX5qkA6g==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.96.0.tgz", + "integrity": "sha512-UEs+Zf6T2/FwQlLgv7gfZsKmY19sl3hK57r2BQVc2eCmCmF/deeqDcWyFjzkNLgdDDucY60PoNhNGClDm605uQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm-musleabihf": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.96.0.tgz", + "integrity": "sha512-1kuWvjR2+ORJMoyxt9LSbLcDhXZnL25XOuv9VmH6NmSPvLgewzuubSlm++W03x+U7SzWFilBsdwIHtD/0mjERw==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-gnu": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.96.0.tgz", + "integrity": "sha512-PHH4ETR1t0fymxuhpQNj3Z9t/78/zZa2Lj3Z3I0ZOd+/Ex+gtdhGoB5xYyy7lcYGAPMfZ+Gmr+dTCr1GYNZ3BA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-arm64-musl": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.96.0.tgz", + "integrity": "sha512-fjDPbZjkqaDSTBe0FM8nZ9zBw4B/NF/I0gH7CfvNDwIj9smISaNFypYeomkvubORpnbX9ORhvhYwg3TxQ60OGA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-riscv64-gnu": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.96.0.tgz", + "integrity": "sha512-59KAHd/6/LmjkdSAuJn0piKmwSavMasWNUKuYLX/UnqI5KkGIp14+LBwwaBG6KzOtIq1NrRCnmlL4XSEaNkzTg==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-linux-s390x-gnu": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.96.0.tgz", + "integrity": "sha512-VtupojtgahY8XmLwpVpM3C1WQEgMD1JxpB8lzUtdSLwosWaaz1EAl+VXWNuxTTZusNuLBtmR+F0qql22ISi/9g==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@oxc-parser/binding-linux-x64-gnu": { "version": "0.96.0", "cpu": [ @@ -1138,6 +1953,54 @@ "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/@oxc-parser/binding-wasm32-wasi": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.96.0.tgz", + "integrity": "sha512-TJ/sNPbVD4u6kUwm7sDKa5iRDEB8vd7ZIMjYqFrrAo9US1RGYOSvt6Ie9sDRekUL9fZhNsykvSrpmIj6dg/C2w==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.7" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-parser/binding-win32-arm64-msvc": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.96.0.tgz", + "integrity": "sha512-zCOhRB7MYVIHLj+2QYoTuLyaipiD8JG/ggUjfsMUaupRPpvwQNhsxINLIcTcb0AS+OsT7/OREhydjO74STqQzQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-parser/binding-win32-x64-msvc": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.96.0.tgz", + "integrity": "sha512-J6zfx9TE0oS+TrqBUjMVMOi/d/j3HMj69Pip263pETOEPm788N0HXKPsc2X2jUfSTHzD9vmdjq0QFymbf2LhWg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@oxc-project/types": { "version": "0.96.0", "license": "MIT", @@ -1145,6 +2008,166 @@ "url": "https://github.com/sponsors/Boshen" } }, + "node_modules/@oxc-transform/binding-android-arm64": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-android-arm64/-/binding-android-arm64-0.96.0.tgz", + "integrity": "sha512-wOm+ZsqFvyZ7B9RefUMsj0zcXw77Z2pXA51nbSQyPXqr+g0/pDGxriZWP8Sdpz/e4AEaKPA9DvrwyOZxu7GRDQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-darwin-arm64": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-darwin-arm64/-/binding-darwin-arm64-0.96.0.tgz", + "integrity": "sha512-td1sbcvzsyuoNRiNdIRodPXRtFFwxzPpC/6/yIUtRRhKn30XQcizxupIvQQVpJWWchxkphbBDh6UN+u+2CJ8Zw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-darwin-x64": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-darwin-x64/-/binding-darwin-x64-0.96.0.tgz", + "integrity": "sha512-xgqxnqhPYH2NYkgbqtnCJfhbXvxIf/pnhF/ig5UBK8PYpCEWIP/cfLpQRQ9DcQnRfuxi7RMIF6LdmB1AiS6Fkg==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-freebsd-x64": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-freebsd-x64/-/binding-freebsd-x64-0.96.0.tgz", + "integrity": "sha512-1i67OXdl/rvSkcTXqDlh6qGRXYseEmf0rl/R+/i88scZ/o3A+FzlX56sThuaPzSSv9eVgesnoYUjIBJELFc1oA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-arm-gnueabihf": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.96.0.tgz", + "integrity": "sha512-9MJBs0SWODsqyzO3eAnacXgJ/sZu1xqinjEwBzkcZ3tQI8nKhMADOzu2NzbVWDWujeoC8DESXaO08tujvUru+Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-arm-musleabihf": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.96.0.tgz", + "integrity": "sha512-BQom57I2ScccixljNYh2Wy+5oVZtF1LXiiUPxSLtDHbsanpEvV/+kzCagQpTjk1BVzSQzOxfEUWjvL7mY53pRQ==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-arm64-gnu": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.96.0.tgz", + "integrity": "sha512-kaqvUzNu8LL4aBSXqcqGVLFG13GmJEplRI2+yqzkgAItxoP/LfFMdEIErlTWLGyBwd0OLiNMHrOvkcCQRWadVg==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-arm64-musl": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.96.0.tgz", + "integrity": "sha512-EiG/L3wEkPgTm4p906ufptyblBgtiQWTubGg/JEw82f8uLRroayr5zhbUqx40EgH037a3SfJthIyLZi7XPRFJw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-riscv64-gnu": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.96.0.tgz", + "integrity": "sha512-r01CY6OxKGtVeYnvH4mGmtkQMlLkXdPWWNXwo5o7fE2s/fgZPMpqh8bAuXEhuMXipZRJrjxTk1+ZQ4KCHpMn3Q==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-linux-s390x-gnu": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.96.0.tgz", + "integrity": "sha512-4djg2vYLGbVeS8YiA2K4RPPpZE4fxTGCX5g/bOMbCYyirDbmBAIop4eOAj8vOA9i1CcWbDtmp+PVJ1dSw7f3IQ==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@oxc-transform/binding-linux-x64-gnu": { "version": "0.96.0", "cpu": [ @@ -1173,6 +2196,54 @@ "node": "^20.19.0 || >=22.12.0" } }, + "node_modules/@oxc-transform/binding-wasm32-wasi": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-wasm32-wasi/-/binding-wasm32-wasi-0.96.0.tgz", + "integrity": "sha512-A91ARLiuZHGN4hBds9s7bW3czUuLuHLsV+cz44iF9j8e1zX9m2hNGXf/acQRbg/zcFUXmjz5nmk8EkZyob876w==", + "cpu": [ + "wasm32" + ], + "license": "MIT", + "optional": true, + "dependencies": { + "@napi-rs/wasm-runtime": "^1.0.7" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@oxc-transform/binding-win32-arm64-msvc": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.96.0.tgz", + "integrity": "sha512-IedJf40djKgDObomhYjdRAlmSYUEdfqX3A3M9KfUltl9AghTBBLkTzUMA7O09oo71vYf5TEhbFM7+Vn5vqw7AQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, + "node_modules/@oxc-transform/binding-win32-x64-msvc": { + "version": "0.96.0", + "resolved": "https://registry.npmjs.org/@oxc-transform/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.96.0.tgz", + "integrity": "sha512-0fI0P0W7bSO/GCP/N5dkmtB9vBqCA4ggo1WmXTnxNJVmFFOtcA1vYm1I9jl8fxo+sucW2WnlpnI4fjKdo3JKxA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^20.19.0 || >=22.12.0" + } + }, "node_modules/@parcel/watcher": { "version": "2.5.1", "hasInstallScript": true, @@ -1603,6 +2674,7 @@ "node_modules/@rollup/plugin-commonjs/node_modules/picomatch": { "version": "4.0.3", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -1739,6 +2811,201 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.53.2.tgz", + "integrity": "sha512-yDPzwsgiFO26RJA4nZo8I+xqzh7sJTZIWQOxn+/XOdPE31lAvLIYCKqjV+lNH/vxE2L2iH3plKxDCRK6i+CwhA==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.53.2.tgz", + "integrity": "sha512-k8FontTxIE7b0/OGKeSN5B6j25EuppBcWM33Z19JoVT7UTXFSo3D9CdU39wGTeb29NO3XxpMNauh09B+Ibw+9g==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.53.2.tgz", + "integrity": "sha512-A6s4gJpomNBtJ2yioj8bflM2oogDwzUiMl2yNJ2v9E7++sHrSrsQ29fOfn5DM/iCzpWcebNYEdXpaK4tr2RhfQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.53.2.tgz", + "integrity": "sha512-e6XqVmXlHrBlG56obu9gDRPW3O3hLxpwHpLsBJvuI8qqnsrtSZ9ERoWUXtPOkY8c78WghyPHZdmPhHLWNdAGEw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.53.2.tgz", + "integrity": "sha512-v0E9lJW8VsrwPux5Qe5CwmH/CF/2mQs6xU1MF3nmUxmZUCHazCjLgYvToOk+YuuUqLQBio1qkkREhxhc656ViA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.53.2.tgz", + "integrity": "sha512-ClAmAPx3ZCHtp6ysl4XEhWU69GUB1D+s7G9YjHGhIGCSrsg00nEGRRZHmINYxkdoJehde8VIsDC5t9C0gb6yqA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.53.2.tgz", + "integrity": "sha512-EPlb95nUsz6Dd9Qy13fI5kUPXNSljaG9FiJ4YUGU1O/Q77i5DYFW5KR8g1OzTcdZUqQQ1KdDqsTohdFVwCwjqg==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.53.2.tgz", + "integrity": "sha512-BOmnVW+khAUX+YZvNfa0tGTEMVVEerOxN0pDk2E6N6DsEIa2Ctj48FOMfNDdrwinocKaC7YXUZ1pHlKpnkja/Q==", + "cpu": [ + "arm" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.53.2.tgz", + "integrity": "sha512-Xt2byDZ+6OVNuREgBXr4+CZDJtrVso5woFtpKdGPhpTPHcNG7D8YXeQzpNbFRxzTVqJf7kvPMCub/pcGUWgBjA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.53.2.tgz", + "integrity": "sha512-+LdZSldy/I9N8+klim/Y1HsKbJ3BbInHav5qE9Iy77dtHC/pibw1SR/fXlWyAk0ThnpRKoODwnAuSjqxFRDHUQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.53.2.tgz", + "integrity": "sha512-8ms8sjmyc1jWJS6WdNSA23rEfdjWB30LH8Wqj0Cqvv7qSHnvw6kgMMXRdop6hkmGPlyYBdRPkjJnj3KCUHV/uQ==", + "cpu": [ + "loong64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.53.2.tgz", + "integrity": "sha512-3HRQLUQbpBDMmzoxPJYd3W6vrVHOo2cVW8RUo87Xz0JPJcBLBr5kZ1pGcQAhdZgX9VV7NbGNipah1omKKe23/g==", + "cpu": [ + "ppc64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.53.2.tgz", + "integrity": "sha512-fMjKi+ojnmIvhk34gZP94vjogXNNUKMEYs+EDaB/5TG/wUkoeua7p7VCHnE6T2Tx+iaghAqQX8teQzcvrYpaQA==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.53.2.tgz", + "integrity": "sha512-XuGFGU+VwUUV5kLvoAdi0Wz5Xbh2SrjIxCtZj6Wq8MDp4bflb/+ThZsVxokM7n0pcbkEr2h5/pzqzDYI7cCgLQ==", + "cpu": [ + "riscv64" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.53.2.tgz", + "integrity": "sha512-w6yjZF0P+NGzWR3AXWX9zc0DNEGdtvykB03uhonSHMRa+oWA6novflo2WaJr6JZakG2ucsyb+rvhrKac6NIy+w==", + "cpu": [ + "s390x" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, "node_modules/@rollup/rollup-linux-x64-gnu": { "version": "4.53.2", "cpu": [ @@ -1761,6 +3028,71 @@ "linux" ] }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.53.2.tgz", + "integrity": "sha512-4VEd19Wmhr+Zy7hbUsFZ6YXEiP48hE//KPLCSVNY5RMGX2/7HZ+QkN55a3atM1C/BZCGIgqN+xrVgtdak2S9+A==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.53.2.tgz", + "integrity": "sha512-IlbHFYc/pQCgew/d5fslcy1KEaYVCJ44G8pajugd8VoOEI8ODhtb/j8XMhLpwHCMB3yk2J07ctup10gpw2nyMA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.53.2.tgz", + "integrity": "sha512-lNlPEGgdUfSzdCWU176ku/dQRnA7W+Gp8d+cWv73jYrb8uT7HTVVxq62DUYxjbaByuf1Yk0RIIAbDzp+CnOTFg==", + "cpu": [ + "ia32" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.53.2.tgz", + "integrity": "sha512-S6YojNVrHybQis2lYov1sd+uj7K0Q05NxHcGktuMMdIQ2VixGwAfbJ23NnlvvVV1bdpR2m5MsNBViHJKcA4ADw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.53.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.53.2.tgz", + "integrity": "sha512-k+/Rkcyx//P6fetPoLMb8pBeqJBNGx81uuf7iljX9++yNBVRDQgD04L+SVXmXmh5ZP4/WOp4mWF0kmi06PW2tA==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@sindresorhus/is": { "version": "7.1.1", "license": "MIT", @@ -1785,11 +3117,20 @@ "version": "1.2.12", "license": "CC0-1.0" }, + "node_modules/@tybys/wasm-util": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", + "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@types/eslint": { "version": "9.6.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/estree": "*", "@types/json-schema": "*" @@ -1799,7 +3140,6 @@ "version": "3.7.7", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint": "*", "@types/estree": "*" @@ -1812,13 +3152,13 @@ "node_modules/@types/json-schema": { "version": "7.0.15", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/node": { "version": "24.10.1", "devOptional": true, "license": "MIT", + "peer": true, "dependencies": { "undici-types": "~7.16.0" } @@ -2342,7 +3682,9 @@ } }, "node_modules/@vitejs/plugin-vue-jsx/node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.50", + "version": "1.0.0-beta.52", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.52.tgz", + "integrity": "sha512-/L0htLJZbaZFL1g9OHOblTxbCYIGefErJjtYOwgl9ZqNx27P3L0SDfjhhHIss32gu5NWgnxuT2a2Hnnv6QGHKA==", "license": "MIT" }, "node_modules/@volar/language-core": { @@ -2447,6 +3789,7 @@ "node_modules/@vue/compiler-sfc": { "version": "3.5.24", "license": "MIT", + "peer": true, "dependencies": { "@babel/parser": "^7.28.5", "@vue/compiler-core": "3.5.24", @@ -2581,7 +3924,6 @@ "version": "1.14.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/helper-numbers": "1.13.2", "@webassemblyjs/helper-wasm-bytecode": "1.13.2" @@ -2590,26 +3932,22 @@ "node_modules/@webassemblyjs/floating-point-hex-parser": { "version": "1.13.2", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-api-error": { "version": "1.13.2", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-buffer": { "version": "1.14.1", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-numbers": { "version": "1.13.2", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/floating-point-hex-parser": "1.13.2", "@webassemblyjs/helper-api-error": "1.13.2", @@ -2619,14 +3957,12 @@ "node_modules/@webassemblyjs/helper-wasm-bytecode": { "version": "1.13.2", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/helper-wasm-section": { "version": "1.14.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -2638,7 +3974,6 @@ "version": "1.13.2", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@xtuc/ieee754": "^1.2.0" } @@ -2647,7 +3982,6 @@ "version": "1.13.2", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@xtuc/long": "4.2.2" } @@ -2655,14 +3989,12 @@ "node_modules/@webassemblyjs/utf8": { "version": "1.13.2", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@webassemblyjs/wasm-edit": { "version": "1.14.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -2678,7 +4010,6 @@ "version": "1.14.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-wasm-bytecode": "1.13.2", @@ -2691,7 +4022,6 @@ "version": "1.14.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-buffer": "1.14.1", @@ -2703,7 +4033,6 @@ "version": "1.14.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@webassemblyjs/helper-api-error": "1.13.2", @@ -2717,7 +4046,6 @@ "version": "1.14.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@webassemblyjs/ast": "1.14.1", "@xtuc/long": "4.2.2" @@ -2730,14 +4058,12 @@ "node_modules/@xtuc/ieee754": { "version": "1.2.0", "dev": true, - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/@xtuc/long": { "version": "4.2.2", "dev": true, - "license": "Apache-2.0", - "peer": true + "license": "Apache-2.0" }, "node_modules/abbrev": { "version": "3.0.1", @@ -2759,6 +4085,7 @@ "node_modules/acorn": { "version": "8.15.0", "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -2777,7 +4104,6 @@ "version": "1.0.4", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10.13.0" }, @@ -2812,7 +4138,6 @@ "version": "2.1.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -2829,7 +4154,6 @@ "version": "5.1.0", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -3108,6 +4432,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.8.25", "caniuse-lite": "^1.0.30001754", @@ -3299,7 +4624,6 @@ "version": "1.0.4", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.0" } @@ -4071,7 +5395,6 @@ "version": "5.1.1", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" @@ -4084,7 +5407,6 @@ "version": "4.3.0", "dev": true, "license": "BSD-2-Clause", - "peer": true, "dependencies": { "estraverse": "^5.2.0" }, @@ -4096,7 +5418,6 @@ "version": "5.3.0", "dev": true, "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" } @@ -4105,7 +5426,6 @@ "version": "4.3.0", "dev": true, "license": "BSD-2-Clause", - "peer": true, "engines": { "node": ">=4.0" } @@ -4170,8 +5490,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/fast-fifo": { "version": "1.3.2", @@ -4211,8 +5530,7 @@ "url": "https://opencollective.com/fastify" } ], - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/fastq": { "version": "1.19.1", @@ -4414,8 +5732,7 @@ "node_modules/glob-to-regexp": { "version": "0.4.1", "dev": true, - "license": "BSD-2-Clause", - "peer": true + "license": "BSD-2-Clause" }, "node_modules/global-directory": { "version": "4.0.1", @@ -4500,7 +5817,6 @@ "version": "4.0.0", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -4877,7 +6193,6 @@ "version": "27.5.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/node": "*", "merge-stream": "^2.0.0", @@ -4919,14 +6234,12 @@ "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "1.0.0", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", @@ -5077,7 +6390,6 @@ "version": "4.3.1", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=6.11.5" }, @@ -5228,13 +6540,19 @@ } }, "node_modules/mime-types": { - "version": "3.0.1", + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "dependencies": { "mime-db": "^1.54.0" }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/mimic-fn": { @@ -5346,8 +6664,7 @@ "node_modules/neo-async": { "version": "2.6.2", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/nitropack": { "version": "2.12.9", @@ -5620,6 +6937,7 @@ "version": "4.2.1", "resolved": "https://registry.npmjs.org/nuxt/-/nuxt-4.2.1.tgz", "integrity": "sha512-OE5ONizgwkKhjTGlUYB3ksE+2q2/I30QIYFl3N1yYz1r2rwhunGA3puUvqkzXwgLQ3AdsNcigPDmyQsqjbSdoQ==", + "peer": true, "dependencies": { "@dxup/nuxt": "^0.2.1", "@nuxt/cli": "^3.30.0", @@ -5897,6 +7215,7 @@ "node_modules/oxc-parser": { "version": "0.96.0", "license": "MIT", + "peer": true, "dependencies": { "@oxc-project/types": "^0.96.0" }, @@ -6105,6 +7424,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -6673,7 +7993,6 @@ "version": "2.0.2", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -6718,6 +8037,7 @@ "node_modules/rollup": { "version": "4.53.2", "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, @@ -6853,7 +8173,6 @@ "version": "4.3.3", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/json-schema": "^7.0.9", "ajv": "^8.9.0", @@ -7234,7 +8553,6 @@ "version": "8.1.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -7359,7 +8677,6 @@ "version": "5.3.14", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", "jest-worker": "^27.4.5", @@ -7443,6 +8760,7 @@ "node_modules/tinyglobby/node_modules/picomatch": { "version": "4.0.3", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -7478,6 +8796,13 @@ "version": "0.0.3", "license": "MIT" }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "license": "0BSD", + "optional": true + }, "node_modules/type-fest": { "version": "5.2.0", "license": "(MIT OR CC0-1.0)", @@ -7971,6 +9296,7 @@ "node_modules/vite": { "version": "7.2.2", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", @@ -8302,6 +9628,7 @@ "node_modules/vite/node_modules/picomatch": { "version": "4.0.3", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -8316,6 +9643,7 @@ "node_modules/vue": { "version": "3.5.24", "license": "MIT", + "peer": true, "dependencies": { "@vue/compiler-dom": "3.5.24", "@vue/compiler-sfc": "3.5.24", @@ -8351,6 +9679,7 @@ "node_modules/vue-router": { "version": "4.6.3", "license": "MIT", + "peer": true, "dependencies": { "@vue/devtools-api": "^6.6.4" }, @@ -8365,7 +9694,6 @@ "version": "2.4.4", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "glob-to-regexp": "^0.4.1", "graceful-fs": "^4.1.2" @@ -8389,7 +9717,6 @@ "version": "5.102.1", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "@types/eslint-scope": "^3.7.7", "@types/estree": "^1.0.8", @@ -8449,7 +9776,6 @@ "version": "1.52.0", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -8458,7 +9784,6 @@ "version": "2.1.35", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mime-db": "1.52.0" }, diff --git a/example-app/package.json b/example-app/package.json index 2d301c0..3ad50bf 100644 --- a/example-app/package.json +++ b/example-app/package.json @@ -1,5 +1,5 @@ { - "name": "hello-nuxt", + "name": "example-app", "private": true, "type": "module", "scripts": { @@ -9,15 +9,13 @@ "preview": "nuxt preview" }, "dependencies": { - "@lit/reactive-element": "^2.1.1", + "@impost/lib": "workspace:*", + "@impost/widget": "workspace:*", "js-toml": "^1.0.2", - "lit-element": "^4.2.1", - "lit-html": "^3.3.1", "nuxt": "latest", "nuxt-ssr-lit": "1.6.32", - "zod": "^4.1.12", - "@impost/lib": "file:../packages/lib", - "@impost/widget": "file:../packages/widget" + "vue": "^3.5.25", + "zod": "^4.1.12" }, "devDependencies": { "@types/node": "^24.10.0", diff --git a/example-app/server/api/pow/challenge.post.ts b/example-app/server/api/pow/challenge.post.ts index e140f92..0d37684 100644 --- a/example-app/server/api/pow/challenge.post.ts +++ b/example-app/server/api/pow/challenge.post.ts @@ -13,26 +13,29 @@ export default defineEventHandler(async (event) => { const body = await readValidatedBody(event, challengeSchema.safeParse); if (!body.success) { + const errors = z.treeifyError(body.error); + const error_message = Object.entries(errors.errors).map(([key, value]) => `${key}: ${value}`).join('\n'); + throw createError({ statusCode: 400, - statusMessage: 'Validation failed' + statusMessage: error_message }) } - let target = body.data.challenge; - let nonce = body.data.nonce; + let { challenge, nonce } = body.data; + + const outstanding_challenge = outstandingChallenges.get(body.data.challenge)!; + // always delete the challenge on a solve attempt + clearTimeout(outstanding_challenge.timeout); + outstandingChallenges.delete(challenge); // check if the challenge is valid - let challenge_valid = await validate_challenge(outstandingChallenges.get(target)!.challenge, { - challenge: target, - nonce: nonce + let challenge_valid = await validate_challenge(outstandingChallenges.get(challenge)!.challenge, { + challenge, + nonce, }); if (challenge_valid) { - // clear the challenge - clearTimeout(outstandingChallenges.get(target)!.timeout); - outstandingChallenges.delete(target); - return { message: 'Challenge solved' }; diff --git a/justfile b/justfile deleted file mode 100644 index 3d10f4c..0000000 --- a/justfile +++ /dev/null @@ -1,26 +0,0 @@ -build: build-widget - -wasm-opt-args := "--strip-debug --strip-dwarf --enable-tail-call --enable-bulk-memory -Oz" -zig-build-args := "--release=fast" - -npm-runner := "npm" - -[working-directory: "solver"] -build-wasm: - zig build {{zig-build-args}} - wasm-opt {{wasm-opt-args}} --enable-simd -o zig-out/bin/solver.wasm zig-out/bin/solver.wasm - # The server does not support simd, so we disable it here - wasm-opt {{wasm-opt-args}} --disable-simd -o zig-out/bin/validator.wasm zig-out/bin/validator.wasm - -[working-directory: "packages/lib"] -build-lib: build-wasm - {{npm-runner}} install - {{npm-runner}} link - {{npm-runner}} run build - -[working-directory: "packages/widget"] -build-widget: build-lib - {{npm-runner}} install - {{npm-runner}} link @impost/lib - {{npm-runner}} link - {{npm-runner}} run build diff --git a/package.json b/package.json new file mode 100644 index 0000000..abc11a6 --- /dev/null +++ b/package.json @@ -0,0 +1,17 @@ +{ + "name": "impost-monorepo", + "version": "0.0.0", + "private": true, + "scripts": { + "build": "pnpm build:all", + "build:all": "pnpm build:solver && pnpm build:lib && pnpm build:widget", + "build:solver": "pnpm --filter solver build", + "build:lib": "pnpm --filter lib build", + "build:widget": "pnpm --filter widget build", + "dev:example": "pnpm --filter example-app dev" + }, + "devDependencies": { + "typescript": "^5.0.0" + }, + "packageManager": "pnpm@10.21.0+sha512.da3337267e400fdd3d479a6c68079ac6db01d8ca4f67572083e722775a796788a7a9956613749e000fac20d424b594f7a791a5f4e2e13581c5ef947f26968a40" +} diff --git a/packages/lib/package-lock.json b/packages/lib/package-lock.json index d643549..e66168c 100644 --- a/packages/lib/package-lock.json +++ b/packages/lib/package-lock.json @@ -2058,7 +2058,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2332,7 +2331,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2383,7 +2381,6 @@ "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", diff --git a/packages/lib/package.json b/packages/lib/package.json index f20e057..463c77f 100644 --- a/packages/lib/package.json +++ b/packages/lib/package.json @@ -36,4 +36,4 @@ "dependencies": { "uuidv7": "^1.0.2" } -} +} \ No newline at end of file diff --git a/packages/lib/src/solver.ts b/packages/lib/src/solver.ts index 7161566..203334d 100644 --- a/packages/lib/src/solver.ts +++ b/packages/lib/src/solver.ts @@ -1,4 +1,5 @@ -import WASMSolverUrl from '../../../solver/zig-out/bin/solver.wasm?url&inline'; +import WASMSolverUrl from '../../solver/zig-out/bin/solver.wasm?url&inline'; +import { ChallengeStrategy, type Challenge, type ChallengeLeadingZeroes, type ChallengeTargetNumber } from '.'; type WasmExports = Record & { "malloc": (byte_count: number) => number | null; @@ -17,6 +18,7 @@ export type SolverEnv = { __set_solution: (value: number) => void; __cmpxchg_solution: (expected: number, replacement: number) => number; __fetch_add_nonce: (value: number) => number; + __log: (str_ptr: number, str_len: number) => void; }; export async function get_wasm_module(): Promise { @@ -29,7 +31,18 @@ export async function init_solver(env: SolverEnv, module: WebAssembly.Module): P }) as unknown as SolverModule; } -export function solve_leaading_zeroes_challenge(solver: SolverModule, challenge: { salt: string, difficulty: number }): number { +export function solve_challenge(solver: SolverModule, challenge: Challenge): number { + switch (challenge.strategy) { + case ChallengeStrategy.LeadingZeroes: + return solve_leaading_zeroes_challenge(solver, challenge); + case ChallengeStrategy.TargetNumber: + return solve_target_number_challenge(solver, challenge); + default: + throw new Error("Invalid challenge strategy"); + } +} + +function solve_leaading_zeroes_challenge(solver: SolverModule, challenge: ChallengeLeadingZeroes): number { const { salt, difficulty } = challenge; const encoder = new TextEncoder(); @@ -56,7 +69,7 @@ export function solve_leaading_zeroes_challenge(solver: SolverModule, challenge: return ret; } -export function solve_target_number_challenge(solver: SolverModule, challenge: { salt: string, target: string }): number { +function solve_target_number_challenge(solver: SolverModule, challenge: ChallengeTargetNumber): number { const { salt, target } = challenge; const encoder = new TextEncoder(); diff --git a/packages/lib/src/validator.ts b/packages/lib/src/validator.ts index 3c5185c..66432a1 100644 --- a/packages/lib/src/validator.ts +++ b/packages/lib/src/validator.ts @@ -1,5 +1,5 @@ -import { ChallengeAlgorithm, ChallengeStrategy, type Challenge } from '.'; -import WASMValidatorUrl from '../../../solver/zig-out/bin/validator.wasm?url&inline'; +import { ChallengeAlgorithm, ChallengeStrategy, type Challenge, type ChallengeLeadingZeroes, type ChallengeTargetNumber } from '.'; +import WASMValidatorUrl from '../../solver/zig-out/bin/validator.wasm?url&inline'; type WasmExports = Record & { "malloc": (byte_count: number) => number | null; @@ -18,11 +18,11 @@ function array_to_base64(buffer: ArrayBuffer): string { return btoa(String.fromCharCode(...new Uint8Array(buffer))); } -async function generate_leading_zeroes_challenge(parameters: Object, difficulty: number): Promise { +async function generate_leading_zeroes_challenge(parameters: Object, difficulty: number): Promise { let parameters_str = Object.entries(parameters).map(([key, value]) => `${key}=${value}`).join("&"); let salt = `${array_to_base64(crypto.getRandomValues(new Uint8Array(32)).buffer)}?${parameters_str}`; - let challenge: Challenge = { + let challenge: ChallengeLeadingZeroes = { algorithm: ChallengeAlgorithm.Argon2id, strategy: ChallengeStrategy.LeadingZeroes, salt, @@ -32,7 +32,7 @@ async function generate_leading_zeroes_challenge(parameters: Object, difficulty: return challenge; } -async function generate_target_number_challenge(parameters: Object, max_number: number): Promise { +async function generate_target_number_challenge(parameters: Object, max_number: number): Promise { // in target number config, since we need to generate a target hash, we // need to hash the salt + nonce, so the client knows what the target is const validator = (await WebAssembly.instantiateStreaming(fetch(WASMValidatorUrl))).instance as unknown as ValidatorModule; @@ -68,7 +68,7 @@ async function generate_target_number_challenge(parameters: Object, max_number: let target_slice = new Uint8Array(validator.exports.memory.buffer.slice(target_ptr, target_ptr + target_len)); const target = new TextDecoder().decode(target_slice); - let challenge: Challenge = { + let challenge: ChallengeTargetNumber = { algorithm: ChallengeAlgorithm.Argon2id, strategy: ChallengeStrategy.TargetNumber, salt, diff --git a/packages/lib/vite.config.ts b/packages/lib/vite.config.ts index 6384f8c..9118a98 100644 --- a/packages/lib/vite.config.ts +++ b/packages/lib/vite.config.ts @@ -18,7 +18,7 @@ export default defineConfig({ preserveModules: false } }, - sourcemap: true + minify: true }, resolve: { alias: { diff --git a/solver/.gitignore b/packages/solver/.gitignore similarity index 100% rename from solver/.gitignore rename to packages/solver/.gitignore diff --git a/solver/build.zig b/packages/solver/build.zig similarity index 56% rename from solver/build.zig rename to packages/solver/build.zig index 848d1d3..6a8d6bb 100644 --- a/solver/build.zig +++ b/packages/solver/build.zig @@ -2,17 +2,13 @@ const std = @import("std"); pub fn build(b: *std.Build) void { const optimize = b.standardOptimizeOption(.{}); - const wasm_target = b.resolveTargetQuery(.{ - .cpu_arch = .wasm32, - .os_tag = .freestanding, - .cpu_features_add = std.Target.wasm.featureSet(&.{ .bulk_memory, .bulk_memory_opt, .simd128, .tail_call }), - }); + const target = b.standardTargetOptions(.{}); // solver const solver_mod = b.addModule("solver", .{ .root_source_file = b.path("src/solver.zig"), .optimize = optimize, - .target = wasm_target, + .target = target, }); const solver_exe = b.addExecutable(.{ @@ -20,10 +16,12 @@ pub fn build(b: *std.Build) void { .root_module = solver_mod, }); - solver_exe.entry = .disabled; - solver_exe.rdynamic = true; - solver_exe.lto = .full; - solver_exe.link_gc_sections = true; + if (target.result.cpu.arch == .wasm32) { + solver_exe.entry = .disabled; + solver_exe.rdynamic = true; + solver_exe.link_gc_sections = true; + solver_exe.lto = .full; + } b.installArtifact(solver_exe); @@ -31,16 +29,18 @@ pub fn build(b: *std.Build) void { const validator_mod = b.addModule("validator", .{ .root_source_file = b.path("src/validator.zig"), .optimize = optimize, - .target = wasm_target, + .target = target, }); - const validator_exe = b.addLibrary(.{ + const validator_exe = b.addExecutable(.{ .name = "validator", .root_module = validator_mod, }); - validator_exe.entry = .disabled; - validator_exe.rdynamic = true; + if (target.result.cpu.arch == .wasm32) { + validator_exe.entry = .disabled; + validator_exe.rdynamic = true; + } b.installArtifact(validator_exe); } diff --git a/packages/solver/package.json b/packages/solver/package.json new file mode 100644 index 0000000..d53f225 --- /dev/null +++ b/packages/solver/package.json @@ -0,0 +1,11 @@ +{ + "name": "solver", + "description": "Zig WASM POW solver, not an actual node package, just using this so that pnpm will build it for me", + "version": "0.1.0", + "private": true, + "scripts": { + "build": "pnpm run build:wasm && pnpm run minify", + "build:wasm": "zig build --release=fast -Dtarget=wasm32-freestanding -Dcpu=generic+bulk_memory+bulk_memory_opt+simd128+tail_call", + "minify": "wasm-opt --strip-debug --strip-dwarf -O4 -o zig-out/bin/solver.wasm zig-out/bin/solver.wasm && wasm-opt --strip-debug --strip-dwarf -O4 -o zig-out/bin/validator.wasm zig-out/bin/validator.wasm" + } +} \ No newline at end of file diff --git a/solver/src/hasher.zig b/packages/solver/src/argon2.zig similarity index 51% rename from solver/src/hasher.zig rename to packages/solver/src/argon2.zig index 888b38e..d2d5945 100644 --- a/solver/src/hasher.zig +++ b/packages/solver/src/argon2.zig @@ -2,8 +2,8 @@ const std = @import("std"); const Allocator = std.mem.Allocator; var argon2_params = std.crypto.pwhash.argon2.Params{ - .t = 4, // time cost - .m = 256, // memory cost (in KiB) + .t = 3, // time cost + .m = 8192, // memory cost (in KiB) .p = 1, // parallelism (this doesnt do anything because we are targeting wasm, and we do multithreading differently anyways) }; @@ -11,21 +11,8 @@ const dk_len: usize = 32; // 16 or 32 byte key var derived: [dk_len]u8 = undefined; var buffer_hash_hex: [64]u8 = undefined; -fn bytesToHex(bytes: []const u8, output: []u8) void { - const hex_chars = "0123456789abcdef"; - var i: usize = 0; - while (i < bytes.len) : (i += 1) { - output[i * 2] = hex_chars[(bytes[i] >> 4)]; - output[i * 2 + 1] = hex_chars[bytes[i] & 0x0F]; - } -} - pub fn hash(allocator: Allocator, challenge: []const u8, nonce: []const u8) ![]u8 { try std.crypto.pwhash.argon2.kdf(allocator, &derived, nonce, challenge, argon2_params, .argon2id); - var hash_bytes: [32]u8 = undefined; - std.crypto.hash.sha2.Sha256.hash(&derived, @ptrCast(hash_bytes[0..].ptr), .{}); - - bytesToHex(&hash_bytes, &buffer_hash_hex); - return buffer_hash_hex[0..]; + return derived[0..]; } diff --git a/solver/src/solver.zig b/packages/solver/src/solver.zig similarity index 58% rename from solver/src/solver.zig rename to packages/solver/src/solver.zig index 744dc13..bd489a5 100644 --- a/solver/src/solver.zig +++ b/packages/solver/src/solver.zig @@ -1,23 +1,42 @@ const std = @import("std"); +const builtin = @import("builtin"); + const Allocator = std.mem.Allocator; +const utils = @import("utils.zig"); -const hasher = @import("hasher.zig"); +const argon2 = @import("argon2.zig"); -// var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; -// var allocator = gpa.allocator(); - -var allocator = std.heap.wasm_allocator; +var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; +var allocator = gpa.allocator(); extern fn __get_solution() i32; extern fn __set_solution(value: i32) void; extern fn __cmpxchg_solution(old: i32, new: i32) i32; extern fn __fetch_add_nonce(value: i32) i32; +extern fn __log(str_ptr: usize, str_len: usize) void; -// fn log(comptime fmt: []const u8, args: anytype) void { -// const formatted = std.fmt.allocPrint(allocator, fmt, args) catch return; -// __log(@intFromPtr(formatted.ptr), formatted.len); -// allocator.free(formatted); -// } +fn log(comptime level: std.log.Level, comptime scope: @TypeOf(.EnumLiteral), comptime fmt: []const u8, args: anytype) void { + if (comptime builtin.target.cpu.arch != .wasm32) { + std.log.defaultLog(level, scope, fmt, args); + return; + } + + const log_level_str = switch (level) { + .err => "Error: ", + .warn => "Warning: ", + .info => "Info: ", + .debug => "Debug: ", + }; + + const formatted = std.fmt.allocPrint(allocator, fmt, args) catch return; + const log_str = std.fmt.allocPrint(allocator, "{s}{s}", .{ log_level_str, formatted }) catch return; + allocator.free(formatted); + __log(@intFromPtr(log_str.ptr), log_str.len); + allocator.free(log_str); +} + +pub const std_options: std.Options = .{ .logFn = log }; +var hex_encoder = utils.HexEncoder{}; export fn malloc(byte_count: usize) ?*u8 { const ptr = allocator.alloc(u8, byte_count) catch return null; @@ -31,42 +50,21 @@ export fn free(ptr: ?*anyopaque, byte_count: usize) void { } } -const SolveError = enum(u32) { - InvalidDifficulty = 1, - InvalidNonce = 2, - NoSolution = 3, - OutOfMemory = 4, -}; - -var solve_error: ?SolveError = null; -export fn get_solve_error() u32 { - if (solve_error) |err| { - return @intFromEnum(err); - } - return 0; -} - // returns nonce on success, -1 on failure // to get the error, call get_solve_error export fn solve_leaading_zeroes_challenge(challenge_ptr: [*]u8, challenge_len: usize, difficulty: u32) i32 { - solve_error = null; - const challenge_slice = challenge_ptr[0..challenge_len]; if (difficulty < 1 or difficulty > 64) { - solve_error = SolveError.InvalidDifficulty; + std.log.err("Invalid difficulty for leading zeroes\n", .{}); return -1; } - var target_prefix_buffer: [64]u8 = @splat('0'); - const target_prefix = target_prefix_buffer[0..difficulty]; - const max_nonce_iterations: u64 = 1_000_000_000; // 64 + 9 digits for nonce since the max nonce is 999_999_999 (not 1 billion since nonce < max_nonce_iterations) var input_buffer: []u8 = allocator.alloc(u8, challenge_len + 9) catch { - // log("Failed to allocate memory for challenge\n", .{}); - solve_error = SolveError.OutOfMemory; + std.log.err("Failed to allocate memory for challenge\n", .{}); return -1; }; // dont leak memory :pepega: @@ -83,34 +81,34 @@ export fn solve_leaading_zeroes_challenge(challenge_ptr: [*]u8, challenge_len: u } const nonce_str = std.fmt.bufPrint(input_buffer[challenge_len..], "{d}", .{nonce}) catch { - solve_error = SolveError.InvalidNonce; + std.log.err("Failed to allocate memory for nonce\n", .{}); return -1; }; - const hash_hex_slice = hasher.hash(allocator, input_buffer[0..challenge_len], input_buffer[challenge_len .. challenge_len + nonce_str.len]) catch { - solve_error = SolveError.OutOfMemory; + const argon2_key = argon2.hash(allocator, input_buffer[0..challenge_len], input_buffer[challenge_len .. challenge_len + nonce_str.len]) catch { + std.log.err("Failed to hash argon2 key\n", .{}); return -1; }; - if (std.mem.startsWith(u8, hash_hex_slice, target_prefix)) { - // Found a solution! - if (__cmpxchg_solution(-1, nonce) == -1) { - // we found a solution, and we are the first to do so - return nonce; - } else { - // we found a solution, but we are not the first to do so - return 0; - } + _ = hex_encoder.encode(argon2_key); + if (!hex_encoder.countZeroes(difficulty)) { + continue; + } + + // Found a solution! + if (__cmpxchg_solution(-1, nonce) == -1) { + // we found a solution, and we are the first to do so + return nonce; + } else { + // we found a solution, but we are not the first to do so + return 0; } } - solve_error = SolveError.NoSolution; return -1; } export fn solve_target_number_challenge(target_ptr: [*]u8, target_len: usize, salt_ptr: [*]u8, salt_len: usize) i32 { - solve_error = null; - const target_slice = target_ptr[0..target_len]; const salt_slice = salt_ptr[0..salt_len]; @@ -119,7 +117,6 @@ export fn solve_target_number_challenge(target_ptr: [*]u8, target_len: usize, sa const max_digits = std.math.log10(max_nonce_iterations); var input_buffer: []u8 = allocator.alloc(u8, salt_len + max_digits) catch { - solve_error = SolveError.OutOfMemory; return -1; }; defer allocator.free(input_buffer); @@ -133,16 +130,16 @@ export fn solve_target_number_challenge(target_ptr: [*]u8, target_len: usize, sa } const nonce_str = std.fmt.bufPrint(input_buffer[salt_len..], "{d}", .{nonce}) catch { - solve_error = SolveError.InvalidNonce; return -1; }; - const hash_hex_slice = hasher.hash(allocator, input_buffer[0..salt_len], input_buffer[salt_len .. salt_len + nonce_str.len]) catch { - solve_error = SolveError.OutOfMemory; + const argon2_key = argon2.hash(allocator, input_buffer[0..salt_len], input_buffer[salt_len .. salt_len + nonce_str.len]) catch { return -1; }; - if (std.mem.eql(u8, target_slice, hash_hex_slice)) { + const hex_slice = hex_encoder.encode(argon2_key); + + if (std.mem.eql(u8, target_slice, hex_slice)) { // Found a solution! if (__cmpxchg_solution(-1, nonce) == -1) { // we found a solution, and we are the first to do so @@ -154,6 +151,5 @@ export fn solve_target_number_challenge(target_ptr: [*]u8, target_len: usize, sa } } - solve_error = SolveError.NoSolution; return -1; } diff --git a/packages/solver/src/utils.zig b/packages/solver/src/utils.zig new file mode 100644 index 0000000..1fcc140 --- /dev/null +++ b/packages/solver/src/utils.zig @@ -0,0 +1,43 @@ +const std = @import("std"); +const Allocator = std.mem.Allocator; + +pub const HexEncoder = struct { + scratch: [64]u8 = undefined, + scratch_set: bool = false, + + const Self = @This(); + + pub fn encode(self: *Self, bytes: []const u8) []u8 { + self.scratch_set = true; + + const hex_chars = "0123456789abcdef"; + var i: usize = 0; + while (i < bytes.len) : (i += 1) { + self.scratch[i * 2] = hex_chars[(bytes[i] >> 4)]; + self.scratch[i * 2 + 1] = hex_chars[bytes[i] & 0x0F]; + } + + return &self.scratch; + } + + // counts the number of leading hexidecimal zeroes in the scratch buffer + // which is set by encode + pub fn countZeroes(self: *Self, zeroes: usize) bool { + if (!self.scratch_set) { + return false; + } + + if (zeroes > 64 or zeroes == 0) { + return false; + } + + var i: usize = 0; + while (i < zeroes) : (i += 1) { + if (self.scratch[i] != '0') { + return false; + } + } + + return true; + } +}; diff --git a/solver/src/validator.zig b/packages/solver/src/validator.zig similarity index 66% rename from solver/src/validator.zig rename to packages/solver/src/validator.zig index f1ca850..42ea122 100644 --- a/solver/src/validator.zig +++ b/packages/solver/src/validator.zig @@ -1,9 +1,13 @@ const std = @import("std"); const Allocator = std.mem.Allocator; -const hasher = @import("hasher.zig"); +const argon2 = @import("argon2.zig"); +const utils = @import("utils.zig"); -var allocator = std.heap.wasm_allocator; +var gpa: std.heap.GeneralPurposeAllocator(.{}) = .init; +var allocator = gpa.allocator(); + +var hex_encoder = utils.HexEncoder{}; export fn malloc(byte_count: usize) ?*u8 { const ptr = allocator.alloc(u8, byte_count) catch return null; @@ -17,15 +21,6 @@ export fn free(ptr: ?*anyopaque, byte_count: usize) void { } } -fn bytesToHex(bytes: []const u8, buf: []u8) void { - const hex_chars = "0123456789abcdef"; - var i: usize = 0; - while (i < bytes.len) : (i += 1) { - buf[i * 2] = hex_chars[(bytes[i] >> 4)]; - buf[i * 2 + 1] = hex_chars[bytes[i] & 0x0F]; - } -} - export fn validate_leading_zeroes_challenge(challenge_ptr: [*]u8, challenge_len: usize, nonce_ptr: [*]u8, nonce_len: usize, difficulty: u32) i32 { const challenge_slice = challenge_ptr[0..challenge_len]; const nonce_slice = nonce_ptr[0..nonce_len]; @@ -34,12 +29,10 @@ export fn validate_leading_zeroes_challenge(challenge_ptr: [*]u8, challenge_len: return -1; } - var target_prefix_buffer: [64]u8 = @splat('0'); - const target_prefix = target_prefix_buffer[0..difficulty]; + const argon2_key = argon2.hash(allocator, challenge_slice, nonce_slice) catch return -2; - const hash_hex_slice = hasher.hash(allocator, challenge_slice, nonce_slice) catch return -2; - - if (!std.mem.startsWith(u8, hash_hex_slice, target_prefix)) { + _ = hex_encoder.encode(argon2_key); + if (!hex_encoder.countZeroes(difficulty)) { return -3; } @@ -51,9 +44,10 @@ export fn validate_target_number_challenge(target_ptr: [*]u8, target_len: usize, const salt_slice = salt_ptr[0..salt_len]; const nonce_slice = nonce_ptr[0..nonce_len]; - const hash_hex_slice = hasher.hash(allocator, salt_slice, nonce_slice) catch return -2; + const argon2_key = argon2.hash(allocator, salt_slice, nonce_slice) catch return -2; + const hex_slice = hex_encoder.encode(argon2_key); - if (!std.mem.eql(u8, target_slice, hash_hex_slice)) { + if (!std.mem.eql(u8, target_slice, hex_slice)) { return -3; } @@ -63,12 +57,13 @@ export fn validate_target_number_challenge(target_ptr: [*]u8, target_len: usize, export fn hash(challenge_ptr: [*]u8, challenge_len: usize, nonce_ptr: [*]u8, nonce_len: usize) u64 { const challenge = challenge_ptr[0..challenge_len]; const nonce = nonce_ptr[0..nonce_len]; - const hash_slice = hasher.hash(allocator, challenge, nonce) catch return 0; + const argon2_key = argon2.hash(allocator, challenge, nonce) catch return 0; + const hex_slice = hex_encoder.encode(argon2_key); // bs to get the compiler to not whine about hash_slice.len being a u5 annd thus cannot be shifted by 32 - var ret: u64 = hash_slice.len; + var ret: u64 = hex_slice.len; ret <<= 32; - ret |= @intFromPtr(hash_slice.ptr); + ret |= @intFromPtr(hex_slice.ptr); return ret; } diff --git a/packages/widget/package-lock.json b/packages/widget/package-lock.json index ff2b26c..3663bdd 100644 --- a/packages/widget/package-lock.json +++ b/packages/widget/package-lock.json @@ -8,6 +8,9 @@ "name": "@impost/widget", "version": "0.1.0", "license": "BSL-1.0", + "dependencies": { + "comlink": "^4.4.2" + }, "devDependencies": { "@types/node": "^20.11.24", "lit": "^3.1.2", @@ -1433,7 +1436,6 @@ "integrity": "sha512-ZsJzA5thDQMSQO788d7IocwwQbI8B5OPzmqNvpf3NY/+MHDAS759Wo0gd2WQeXYt5AAAQjzcrTVC6SKCuYgoCQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "undici-types": "~6.21.0" } @@ -1655,6 +1657,11 @@ "dev": true, "license": "MIT" }, + "node_modules/comlink": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/comlink/-/comlink-4.4.2.tgz", + "integrity": "sha512-OxGdvBmJuNKSCMO4NTl1L47VRp6xn2wG4F/2hYzB6tiCb709otOxtEYCSvK80PtjODfXXZu8ds+Nw5kVCjqd2g==" + }, "node_modules/compare-versions": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.1.tgz", @@ -2148,7 +2155,6 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -2422,7 +2428,6 @@ "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", - "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -2461,7 +2466,6 @@ "integrity": "sha512-BxAKBWmIbrDgrokdGZH1IgkIk/5mMHDreLDmCJ0qpyJaAteP8NvMhkwr/ZCQNqNH97bw/dANTE9PDzqwJghfMQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", diff --git a/packages/widget/package.json b/packages/widget/package.json index 93d8f69..a8c22cf 100644 --- a/packages/widget/package.json +++ b/packages/widget/package.json @@ -23,6 +23,7 @@ "lit-element": "^3.1.2" }, "devDependencies": { + "@impost/lib": "workspace:*", "@types/node": "^20.11.24", "lit": "^3.1.2", "lit-element": "^3.1.2", @@ -30,6 +31,7 @@ "tslib": "^2.6.2", "typescript": "^5.3.3", "vite": "npm:rolldown-vite@latest", - "vite-plugin-dts": "^4.5.4" + "vite-plugin-dts": "^4.5.4", + "comlink": "^4.4.2" } } \ No newline at end of file diff --git a/packages/widget/src/pow-captcha.ts b/packages/widget/src/captcha.ts similarity index 57% rename from packages/widget/src/pow-captcha.ts rename to packages/widget/src/captcha.ts index d441202..3dc0565 100644 --- a/packages/widget/src/pow-captcha.ts +++ b/packages/widget/src/captcha.ts @@ -1,13 +1,15 @@ import { LitElement, html, css, isServer, type PropertyValues } from 'lit'; import { customElement, property, state } from 'lit/decorators.js'; -import { type ChallengeSolveRequest, type SolutionMessage, WorkerMessageType, type WorkerRequest, WorkerResponseType } from './types/worker'; -import { type Challenge, ChallengeStrategy } from '@impost/lib'; +import { type Challenge } from '@impost/lib'; import { get_wasm_module } from '@impost/lib/solver'; +import * as Comlink from 'comlink'; -import ChallengeWorker from './solver-worker?worker&inline'; +import SolverWorker from './solver-worker?worker&inline'; -@customElement('pow-captcha') -export class PowCaptcha extends LitElement { +type SolverWorkerAPI = Comlink.Remote; + +@customElement('impost-captcha') +export class ImpostCaptcha extends LitElement { static override styles = css` :host { display: block; @@ -63,13 +65,13 @@ export class PowCaptcha extends LitElement { .impost-footer div a:hover { text-decoration: none; } - .hidden { - display: none; - } `; + /// ================================================ + /// Configuration + /// ================================================ + - // one of: "load", "focus", "submit", "off" @property({ type: String }) auto: "onload" | "onfocus" | "onsubmit" | "off" = "off"; @@ -79,24 +81,28 @@ export class PowCaptcha extends LitElement { @property({ type: String }) challengejson: string = ''; + @property({ type: Boolean }) + showHashrate: boolean = false; + + /// ================================================ + /// Internals + /// ================================================ + // needed to allow for multiple widgets on the same page if you wanted to // do that for some reason - uid: string = Math.floor(Math.random() * 100000).toString(); + private uid: string = Math.floor(Math.random() * 100000).toString(); + + private _internals: ElementInternals | null = null; + static formAssociated = true; @state() - private solution: string = ''; - - @state() - private errorMessage: string = ''; + private solution: string | null = null; @state() private challengeData: Challenge | null = null; @state() - private solved: boolean = false; - - @state() - private isSolving: boolean = false; + private status: 'unsolved' | 'solving' | 'solved' | 'error' = 'unsolved'; @state() private disabled: boolean = true; @@ -104,18 +110,23 @@ export class PowCaptcha extends LitElement { @state() private hashRate: number = 0; + // stores the nonce and solution atomics private sab: SharedArrayBuffer = new SharedArrayBuffer(8); - private solverWorkers: Worker[] | null = null; + private solverWorkers: SolverWorkerAPI[] | null = null; + private nativeWorkers: Worker[] | null = null; private solveStartTime: number | null = null; private hashRateInterval: number | null = null; override connectedCallback() { super.connectedCallback(); + this._internals = this.attachInternals(); this.fetchChallenge(); + console.log(this._internals.form); + this.initWorkers(); switch (this.auto) { @@ -127,7 +138,23 @@ export class PowCaptcha extends LitElement { break; case 'onsubmit': if (this.parentElement?.nodeName === 'FORM') { - this.parentElement.addEventListener('submit', () => this.solveChallenge()); + this.parentElement.addEventListener('submit', async (ev) => { + if (this.status === 'solved') { + return; + } + + ev.preventDefault(); + + await this.solveChallenge(); + + const form = this.parentElement as HTMLFormElement; + + if (form.requestSubmit) { + form.requestSubmit(); + } else { + form.submit(); + } + }); } break; } @@ -146,9 +173,10 @@ export class PowCaptcha extends LitElement { this.hashRateInterval = null; } - for (const worker of this.solverWorkers || []) { + for (const worker of this.nativeWorkers || []) { worker.terminate(); this.solverWorkers = null; + this.nativeWorkers = null; } } @@ -157,7 +185,6 @@ export class PowCaptcha extends LitElement { } async fetchChallenge() { - this.errorMessage = ''; if (this.challengeData !== null) { return; } @@ -168,6 +195,7 @@ export class PowCaptcha extends LitElement { return; } + // challenge data must be provided by the user when using SSR if (isServer) { return; } @@ -179,16 +207,21 @@ export class PowCaptcha extends LitElement { }) .catch(error => { console.error('Error fetching challenge:', error); - this.errorMessage = 'Failed to fetch challenge. Please try again.'; + this.status = 'error'; }); } async initWorkers() { this.solverWorkers = []; + this.nativeWorkers = []; const num_workers = navigator.hardwareConcurrency; for (let i = 0; i < num_workers; i++) { - this.solverWorkers.push(new ChallengeWorker()); + const nativeWorker = new SolverWorker(); + const comlinkWorker = Comlink.wrap(nativeWorker); + + this.nativeWorkers.push(nativeWorker); + this.solverWorkers.push(comlinkWorker); } const atomics_view = new Int32Array(this.sab); @@ -198,106 +231,53 @@ export class PowCaptcha extends LitElement { let wasm_module = await get_wasm_module(); let worker_promises: Promise[] = []; for (let i = 0; i < this.solverWorkers.length; i++) { - console.log('Worker', i); - const worker = this.solverWorkers[i]!; - - worker_promises.push(new Promise((resolve, reject) => { - const message_handler = (event: MessageEvent) => { - console.log('Worker', i, 'got message', event.data); - - if (event.data.type === WorkerResponseType.Error) { - console.error("Worker error:", event.data.error); - reject(event.data.error); - } - - if (event.data.type === WorkerResponseType.InitOk) { - resolve(); - } - - reject(new Error("Unexpected message from worker")); - }; - - const error_handler = (error: ErrorEvent) => { - console.error("Worker error:", error); - reject(error); - }; - - worker.addEventListener('message', message_handler); - worker.addEventListener('error', error_handler); - - worker.postMessage({ - type: WorkerMessageType.Init, - module: wasm_module, - sab: this.sab, - } as WorkerRequest); - })); + const solver = this.solverWorkers[i]!; + worker_promises.push(solver.init(wasm_module, this.sab)); // Direct call to exposed `init` method } const timeoutMs = 10 * 1000; let timeout: number; const timeoutPromise = new Promise((_, reject) => { timeout = setTimeout(() => { - this.errorMessage = 'Failed to initialize workers in time. Please refresh the page.'; - reject(new Error(`Function timed out after ${timeoutMs}ms`)); + reject(new Error(`Function timeout after ${timeoutMs}ms`)); }, timeoutMs); }); await Promise.race([ - Promise.allSettled(worker_promises).then(() => { + Promise.allSettled(worker_promises).then(results => { + const failedInits = results.filter(r => r.status === 'rejected'); + if (failedInits.length > 0) { + console.error('Some workers failed to initialize:', failedInits); + // we might want to collect all errors, and if every + // worker fails, we can throw, but carry on if only some + // fail. For now, we'll just throw if any fail. + + throw new Error("One or more workers failed to initialize."); + } + console.log('All workers initialized'); + return; }), timeoutPromise, ]).then(() => { clearTimeout(timeout); - }); - } - - async issueChallengeToWorker(worker: Worker, request: ChallengeSolveRequest): Promise { - return new Promise((resolve, reject) => { - const message_handler = (event: MessageEvent) => { - worker.removeEventListener('message', message_handler); - worker.removeEventListener('error', error_handler); - - resolve(event.data); - }; - - const error_handler = (error: ErrorEvent) => { - worker.removeEventListener('error', error_handler); - worker.removeEventListener('message', message_handler); - console.error("Worker error:", error); - - reject(error); - }; - - worker.addEventListener('message', message_handler); - worker.addEventListener('error', error_handler); - - switch (request.strategy) { - case ChallengeStrategy.LeadingZeroes: - worker.postMessage({ - strategy: ChallengeStrategy.LeadingZeroes, - salt: request.salt, - difficulty: request.difficulty, - } as WorkerRequest); - break; - case ChallengeStrategy.TargetNumber: - worker.postMessage({ - strategy: ChallengeStrategy.TargetNumber, - target: request.target, - salt: request.salt, - } as WorkerRequest); - break; - } + }).catch(error => { + clearTimeout(timeout); + console.error("Worker initialization failed:", error); + this.status = 'error'; + throw error; }); } async solveChallenge() { if (!this.challengeData || this.solverWorkers === null) { - this.errorMessage = 'Captcha is not ready. Please wait or refresh.'; + // in all normal cases, this should be impossible + this.status = 'error'; return; } - if (this.solution !== '') { + if (this.solution !== null || this.status !== 'unsolved') { + // do not solve twice return; } @@ -306,45 +286,23 @@ export class PowCaptcha extends LitElement { const nonce = this.getCurrentWorkingNonce(); this.hashRate = (nonce / ((performance.now() - this.solveStartTime!) / 1000)); + console.log(this.hashRate); }, 250); this.dispatchEvent(new CustomEvent('impost:solve', { - detail: { - solution: this.solution, - }, + detail: { challenge: this.challengeData, }, // empty solution bubbles: true, composed: true, - })) + })); - console.log(this.challengeData); - - this.isSolving = true; - this.errorMessage = ''; - this.solution = ''; + this.status = 'solving'; + this.solution = null; const atomics_view = new Int32Array(this.sab); + // reset atomics Atomics.store(atomics_view, 0, 0); Atomics.store(atomics_view, 1, -1); - let request: ChallengeSolveRequest; - - switch (this.challengeData.strategy) { - case ChallengeStrategy.LeadingZeroes: - request = { - strategy: ChallengeStrategy.LeadingZeroes, - salt: this.challengeData.salt, - difficulty: this.challengeData.difficulty, - }; - break; - case ChallengeStrategy.TargetNumber: - request = { - strategy: ChallengeStrategy.TargetNumber, - target: this.challengeData.target, - salt: this.challengeData.salt, - }; - break; - } - console.log('Sending challenge to workers...'); // TODO: the first response is not always the solution, due to cmpxchg // blocking, some workers may block on the read, and as soon as they @@ -352,40 +310,42 @@ export class PowCaptcha extends LitElement { // // We need to do a better job of tracking solvers, so if one worker // errors out, we only error out if all workers have errored out. - let worker_promises: Promise[] = []; - for (let worker of this.solverWorkers) { - // dispatch to all workers, func is async so it will not block - worker_promises.push(this.issueChallengeToWorker(worker, request)); + let worker_promises: Promise[] = []; + for (let solver of this.solverWorkers) { + worker_promises.push(solver.solve_challenge(this.challengeData as Challenge)); } - let solution = await Promise.race(worker_promises); + try { + await Promise.race(worker_promises); - if (solution.type === WorkerResponseType.Error) { - this.errorMessage = solution.error; - return; + // The true solution is stored in the SharedArrayBuffer. + this.solution = String(Atomics.load(atomics_view, 1)); + this.status = 'solved'; + } catch (error: any) { + console.error("Captcha solving failed:", error); + this.status = 'error'; + } finally { + if (this.hashRateInterval !== null) { + clearInterval(this.hashRateInterval); + this.hashRateInterval = null; + } } - if (solution.type !== WorkerResponseType.Solution) { - this.errorMessage = "Something went wrong, please try again later."; - return; - } - - this.solution = Atomics.load(atomics_view, 1).toString(); - this.isSolving = false; - this.solved = true; - - if (this.hashRateInterval !== null) { - clearInterval(this.hashRateInterval); - this.hashRateInterval = null; - } - - this.dispatchEvent(new CustomEvent('impost:solved', { - detail: { + if (this.status === 'solved') { + this._internals!.setFormValue(JSON.stringify({ + challenge: this.challengeData.salt, solution: this.solution, - }, - bubbles: true, - composed: true, - })) + })); + + this.dispatchEvent(new CustomEvent('impost:solved', { + detail: { + challenge: this.challengeData, + solution: this.solution, + }, + bubbles: true, + composed: true, + })); + } } solvePreventDefault(event: Event) { @@ -400,12 +360,6 @@ export class PowCaptcha extends LitElement { this.challengejson = ''; } - if (this.errorMessage) { - return html` -
${this.errorMessage}
- `; - } - if (this.challengeData === null) { return html`
Loading captcha challenge...
@@ -416,9 +370,9 @@ export class PowCaptcha extends LitElement {
- ${!this.isSolving ? html` - - ` : html` + ${this.status !== 'solving' ? html`${this.status === 'error' ? html`` : html` + + `}` : html` @@ -431,11 +385,11 @@ export class PowCaptcha extends LitElement { `}
- +