From 47c2466fcf1b316f1dcce1bc7a3f117333d43cf8 Mon Sep 17 00:00:00 2001 From: oritwoen <18102267+oritwoen@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:29:32 +0200 Subject: [PATCH 1/2] chore: adopt Vite+ tooling --- README.md | 40 +- build.config.ts | 5 +- package.json | 46 +- packages/omp/extensions/ciphers.ts | 95 +- packages/pi/extensions/ciphers.ts | 284 +++--- pnpm-lock.yaml | 1374 +++++++++++++++++++++++++++- pnpm-workspace.yaml | 4 + src/ciphers/adfgvx.ts | 54 +- src/ciphers/affine.ts | 81 +- src/ciphers/alberti.ts | 48 +- src/ciphers/atbash.ts | 18 +- src/ciphers/bacon.ts | 19 +- src/ciphers/bifid.ts | 61 +- src/ciphers/caesar.ts | 49 +- src/ciphers/columnar.ts | 25 +- src/ciphers/enigma.ts | 79 +- src/ciphers/morse.ts | 86 +- src/ciphers/playfair.ts | 48 +- src/ciphers/polybius.ts | 20 +- src/ciphers/rail-fence.ts | 41 +- src/ciphers/rot13.ts | 18 +- src/ciphers/rot47.ts | 4 +- src/ciphers/tap-code.ts | 18 +- src/ciphers/trithemius.ts | 35 +- src/ciphers/vigenere.ts | 56 +- src/commands/ciphers.ts | 7 +- src/commands/decode.ts | 6 +- src/commands/encode.ts | 6 +- src/commands/frequency.ts | 14 +- src/core/errors.ts | 2 +- src/core/resolve.ts | 2 +- src/core/types.ts | 13 +- src/core/utils.ts | 41 +- test/unit/ciphers.test.ts | 89 +- test/unit/cli-args.test.ts | 28 +- test/unit/columnar-cache.test.ts | 14 +- test/unit/frequency.test.ts | 8 +- test/unit/omp-extension.test.ts | 100 +- test/unit/utils.test.ts | 41 +- vite.config.ts | 14 + 40 files changed, 2503 insertions(+), 490 deletions(-) create mode 100644 vite.config.ts diff --git a/README.md b/README.md index d499176..f2baa95 100644 --- a/README.md +++ b/README.md @@ -4,26 +4,26 @@ ## Ciphers -| Cipher | Family | Self-inverse | Options | -|--------|--------|:---:|---------| -| **caesar** | substitution-shift | ✗ | `--shift` (1-25, default 3) | -| **rot13** | substitution-shift | ✓ | - | -| **rot47** | substitution-shift | ✓ | - | -| **atbash** | substitution-reflection | ✓ | - | -| **vigenere** | polyalphabetic | ✗ | `--key` (required) | -| **trithemius** | polyalphabetic | ✗ | - | -| **alberti** | polyalphabetic | ✗ | `--key`, `--period` (both required) | -| **rail-fence** | transposition | ✗ | `--rails` (default 3) | -| **affine** | substitution-multiplicative | ✗ | `--a` (multiplier), `--b` (shift) | -| **playfair** | digraph | ✗ | `--key` (required) | -| **polybius** | fractionation | ✗ | `--key` (optional) | -| **morse** | fractionation | ✗ | - | -| **bacon** | fractionation | ✗ | - | -| **tap-code** | fractionation | ✗ | - | -| **columnar** | transposition | ✗ | `--key` (required) | -| **adfgvx** | fractionation | ✗ | `--key` (optional) | -| **bifid** | fractionation | ✗ | `--key` (optional), `--period` (default 5) | -| **enigma** | rotor | ✓ | `--positions`, `--rings`, `--plugboard` | +| Cipher | Family | Self-inverse | Options | +| -------------- | --------------------------- | :----------: | ------------------------------------------ | +| **caesar** | substitution-shift | ✗ | `--shift` (1-25, default 3) | +| **rot13** | substitution-shift | ✓ | - | +| **rot47** | substitution-shift | ✓ | - | +| **atbash** | substitution-reflection | ✓ | - | +| **vigenere** | polyalphabetic | ✗ | `--key` (required) | +| **trithemius** | polyalphabetic | ✗ | - | +| **alberti** | polyalphabetic | ✗ | `--key`, `--period` (both required) | +| **rail-fence** | transposition | ✗ | `--rails` (default 3) | +| **affine** | substitution-multiplicative | ✗ | `--a` (multiplier), `--b` (shift) | +| **playfair** | digraph | ✗ | `--key` (required) | +| **polybius** | fractionation | ✗ | `--key` (optional) | +| **morse** | fractionation | ✗ | - | +| **bacon** | fractionation | ✗ | - | +| **tap-code** | fractionation | ✗ | - | +| **columnar** | transposition | ✗ | `--key` (required) | +| **adfgvx** | fractionation | ✗ | `--key` (optional) | +| **bifid** | fractionation | ✗ | `--key` (optional), `--period` (default 5) | +| **enigma** | rotor | ✓ | `--positions`, `--rings`, `--plugboard` | `alberti` implements the reproducible, simplified keyed-disk variant: the inner disk rotates one position after each `period` Latin letters. diff --git a/build.config.ts b/build.config.ts index 4c16402..1fca901 100644 --- a/build.config.ts +++ b/build.config.ts @@ -4,10 +4,7 @@ export default defineBuildConfig({ entries: [ { type: 'bundle', - input: [ - './src/index.ts', - './src/cli.ts', - ], + input: ['./src/index.ts', './src/cli.ts'], }, ], }) diff --git a/package.json b/package.json index 5fbb32c..0c0e6d3 100644 --- a/package.json +++ b/package.json @@ -1,37 +1,27 @@ { "name": "@agntn/ciphers", "version": "0.1.1", - "type": "module", "description": "Unified educational and puzzle cipher library for agents", + "bin": { + "ch": "./dist/cli.mjs" + }, + "type": "module", "exports": { ".": { "types": "./dist/index.d.mts", "import": "./dist/index.mjs" } }, - "bin": { - "ch": "./dist/cli.mjs" - }, - "pi": { - "extensions": [ - "./packages/pi/extensions/*.ts" - ] - }, - "omp": { - "extensions": [ - "./packages/omp/extensions/ciphers.ts" - ] - }, - "engines": { - "node": ">=25" - }, - "packageManager": "pnpm@10.33.4", "scripts": { "build": "obuild", "dev": "obuild --stub", + "check": "vp check", + "lint": "vp lint", + "format": "vp fmt", + "format:check": "vp fmt --check", "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.omp.json", - "test": "vitest", - "test:run": "vitest --run" + "test": "vp test", + "test:run": "vp test" }, "dependencies": { "citty": "^0.2.2", @@ -47,6 +37,20 @@ "tsx": "^4.22.4", "typebox": "^1.1.39", "typescript": "^6.0.3", - "vitest": "^4.1.7" + "vite-plus": "^0.2.9" + }, + "engines": { + "node": ">=25" + }, + "packageManager": "pnpm@10.33.4", + "omp": { + "extensions": [ + "./packages/omp/extensions/ciphers.ts" + ] + }, + "pi": { + "extensions": [ + "./packages/pi/extensions/*.ts" + ] } } diff --git a/packages/omp/extensions/ciphers.ts b/packages/omp/extensions/ciphers.ts index 0ceebca..3b6b27b 100644 --- a/packages/omp/extensions/ciphers.ts +++ b/packages/omp/extensions/ciphers.ts @@ -10,7 +10,6 @@ const MAX_BRUTE_TEXT_LENGTH = 2_000 const MAX_FREQUENCY_TEXT_LENGTH = 100_000 const MAX_KEY_LENGTH = 1_000 - type CipherParams = { cipher: string text: string @@ -34,9 +33,10 @@ let libraryPromise: Promise | undefined /** Load source in a Git checkout, or the built package export after installation. */ function loadLibrary(): Promise { const isCheckout = existsSync(fileURLToPath(checkoutMarker)) - libraryPromise ??= isCheckout && existsSync(fileURLToPath(sourceEntry)) - ? import(sourceEntry.href) - : import('@agntn/ciphers') + libraryPromise ??= + isCheckout && existsSync(fileURLToPath(sourceEntry)) + ? import(sourceEntry.href) + : import('@agntn/ciphers') return libraryPromise } @@ -57,24 +57,64 @@ function buildOptions(params: CipherParams): Record { return options } - /** Register local educational and puzzle-cipher tools in OMP. */ export default function ciphersExtension(omp: ExtensionAPI): void { const { Type } = omp.typebox const cipherParams = Type.Object({ cipher: Type.String({ maxLength: 32, description: 'Exact built-in cipher name' }), text: Type.String({ maxLength: MAX_TRANSFORM_TEXT_LENGTH, description: 'Text to transform' }), - shift: Type.Optional(Type.Integer({ minimum: 1, maximum: 25, description: 'Caesar shift (1-25; default 3)' })), - key: Type.Optional(Type.String({ maxLength: MAX_KEY_LENGTH, description: 'Key for keyed ciphers' })), - rails: Type.Optional(Type.Integer({ minimum: 2, description: 'Rail Fence rails (at least 2; default 3)' })), - a: Type.Optional(Type.Integer({ minimum: 1, maximum: 25, description: 'Affine multiplier, coprime with 26 (default 5)' })), - b: Type.Optional(Type.Integer({ minimum: 0, maximum: 25, description: 'Affine additive shift (0-25; default 8)' })), - period: Type.Optional(Type.Integer({ minimum: 1, description: 'Rotation or fractionation period for Alberti and Bifid' })), - preserveCase: Type.Optional(Type.Boolean({ description: 'Preserve letter case (default true)' })), - stripNonAlpha: Type.Optional(Type.Boolean({ description: 'Remove non-letter characters before processing (default false)' })), - positions: Type.Optional(Type.String({ pattern: '^[A-Za-z]{3}$', description: 'Enigma initial rotor positions (default AAA)' })), - rings: Type.Optional(Type.String({ pattern: '^[A-Za-z]{3}$', description: 'Enigma ring settings (default AAA)' })), - plugboard: Type.Optional(Type.String({ maxLength: 38, description: 'Enigma plugboard pairs, for example \"AV BS CG\"' })), + shift: Type.Optional( + Type.Integer({ minimum: 1, maximum: 25, description: 'Caesar shift (1-25; default 3)' }), + ), + key: Type.Optional( + Type.String({ maxLength: MAX_KEY_LENGTH, description: 'Key for keyed ciphers' }), + ), + rails: Type.Optional( + Type.Integer({ minimum: 2, description: 'Rail Fence rails (at least 2; default 3)' }), + ), + a: Type.Optional( + Type.Integer({ + minimum: 1, + maximum: 25, + description: 'Affine multiplier, coprime with 26 (default 5)', + }), + ), + b: Type.Optional( + Type.Integer({ + minimum: 0, + maximum: 25, + description: 'Affine additive shift (0-25; default 8)', + }), + ), + period: Type.Optional( + Type.Integer({ + minimum: 1, + description: 'Rotation or fractionation period for Alberti and Bifid', + }), + ), + preserveCase: Type.Optional( + Type.Boolean({ description: 'Preserve letter case (default true)' }), + ), + stripNonAlpha: Type.Optional( + Type.Boolean({ + description: 'Remove non-letter characters before processing (default false)', + }), + ), + positions: Type.Optional( + Type.String({ + pattern: '^[A-Za-z]{3}$', + description: 'Enigma initial rotor positions (default AAA)', + }), + ), + rings: Type.Optional( + Type.String({ pattern: '^[A-Za-z]{3}$', description: 'Enigma ring settings (default AAA)' }), + ), + plugboard: Type.Optional( + Type.String({ + maxLength: 38, + description: 'Enigma plugboard pairs, for example "AV BS CG"', + }), + ), }) omp.registerTool({ @@ -116,7 +156,10 @@ export default function ciphersExtension(omp: ExtensionAPI): void { label: 'Brute Force Caesar', description: 'Decode Caesar ciphertext with every shift from 1 through 25.', parameters: Type.Object({ - text: Type.String({ maxLength: MAX_BRUTE_TEXT_LENGTH, description: 'Caesar ciphertext to brute-force' }), + text: Type.String({ + maxLength: MAX_BRUTE_TEXT_LENGTH, + description: 'Caesar ciphertext to brute-force', + }), }), approval: 'read', loadMode: 'essential', @@ -138,10 +181,11 @@ export default function ciphersExtension(omp: ExtensionAPI): void { description: 'Analyze A-Z letter frequencies and compare their order with English or Polish.', parameters: Type.Object({ text: Type.String({ maxLength: MAX_FREQUENCY_TEXT_LENGTH, description: 'Text to analyze' }), - lang: Type.Optional(Type.Union([ - Type.Literal('en'), - Type.Literal('pl'), - ], { description: 'Reference language (default en)' })), + lang: Type.Optional( + Type.Union([Type.Literal('en'), Type.Literal('pl')], { + description: 'Reference language (default en)', + }), + ), }), approval: 'read', loadMode: 'essential', @@ -153,12 +197,17 @@ export default function ciphersExtension(omp: ExtensionAPI): void { } const maximum = analysis.counts[0]?.[1] ?? 1 - const lines = [`Frequency Analysis (${analysis.total} letters, lang=${analysis.language}):`, ''] + const lines = [ + `Frequency Analysis (${analysis.total} letters, lang=${analysis.language}):`, + '', + ] for (const [character, count] of analysis.counts) { const percentage = ((count / analysis.total) * 100).toFixed(1) const bar = '#'.repeat(Math.ceil((count / maximum) * 15)) - lines.push(` ${character} ${String(count).padStart(4)} (${percentage.padStart(5)}%) ${bar}`) + lines.push( + ` ${character} ${String(count).padStart(4)} (${percentage.padStart(5)}%) ${bar}`, + ) } lines.push('', `Expected (${analysis.language}): ${analysis.reference.split('').join(' ')}`) lines.push(`Actual: ${analysis.counts.map(([character]) => character).join(' ')}`) diff --git a/packages/pi/extensions/ciphers.ts b/packages/pi/extensions/ciphers.ts index 833f277..c6eb519 100644 --- a/packages/pi/extensions/ciphers.ts +++ b/packages/pi/extensions/ciphers.ts @@ -15,17 +15,39 @@ async function loadLib() { /** Shared parameter schema for encode/decode tools. */ const cipherParams = Type.Object({ - cipher: Type.String({ description: 'Cipher name: caesar, rot13, rot47, atbash, vigenere, trithemius, alberti, rail-fence, affine, playfair, polybius, morse, bacon, tap-code, columnar, adfgvx, bifid, enigma' }), + cipher: Type.String({ + description: + 'Cipher name: caesar, rot13, rot47, atbash, vigenere, trithemius, alberti, rail-fence, affine, playfair, polybius, morse, bacon, tap-code, columnar, adfgvx, bifid, enigma', + }), text: Type.String({ description: 'Plaintext to encode' }), - shift: Type.Optional(Type.Number({ description: 'Shift value for Caesar cipher (1-25, default 3)' })), + shift: Type.Optional( + Type.Number({ description: 'Shift value for Caesar cipher (1-25, default 3)' }), + ), key: Type.Optional(Type.String({ description: 'Keyword for keyed ciphers' })), rails: Type.Optional(Type.Number({ description: 'Number of rails for Rail Fence (default 3)' })), - period: Type.Optional(Type.Integer({ minimum: 1, description: 'Rotation or fractionation period for Alberti and Bifid' })), + period: Type.Optional( + Type.Integer({ + minimum: 1, + description: 'Rotation or fractionation period for Alberti and Bifid', + }), + ), a: Type.Optional(Type.Number({ description: 'Multiplier for Affine cipher (coprime with 26)' })), b: Type.Optional(Type.Number({ description: 'Additive shift for Affine cipher (0-25)' })), - positions: Type.Optional(Type.String({ pattern: '^[A-Za-z]{3}$', description: 'Initial rotor positions for Enigma (default AAA)' })), - rings: Type.Optional(Type.String({ pattern: '^[A-Za-z]{3}$', description: 'Ring settings for Enigma (default AAA)' })), - plugboard: Type.Optional(Type.String({ maxLength: 38, description: 'Space-separated plugboard pairs for Enigma' })), + positions: Type.Optional( + Type.String({ + pattern: '^[A-Za-z]{3}$', + description: 'Initial rotor positions for Enigma (default AAA)', + }), + ), + rings: Type.Optional( + Type.String({ + pattern: '^[A-Za-z]{3}$', + description: 'Ring settings for Enigma (default AAA)', + }), + ), + plugboard: Type.Optional( + Type.String({ maxLength: 38, description: 'Space-separated plugboard pairs for Enigma' }), + ), }) type CipherParams = Static @@ -46,134 +68,156 @@ function buildOpts(params: CipherParams): Record { } export default function ciphersExtension(pi: ExtensionAPI) { - pi.registerTool(defineTool({ - name: 'cipher_encode', - label: 'Cipher Encode', - description: 'Encode plaintext with a local cipher (caesar, vigenere, enigma, etc.)', - promptSnippet: 'Use cipher_encode to encode text with local educational and puzzle ciphers.', - promptGuidelines: [ - 'Specify the cipher name and the text to encode.', - 'Caesar needs --shift (default 3), Vigenère/Playfair need --key, Alberti needs --key and --period, Rail Fence needs --rails, Affine needs --a and --b.', - ], - parameters: cipherParams, - renderCall(args, _theme) { - return new Text(`🔐 encode ${args.cipher}: "${args.text}"`, 0, 0) - }, - async execute(_toolCallId, params): Promise { - try { - const lib = await loadLib() - const cipher = lib.resolveCipher(params.cipher) - const result = cipher.encode(params.text, buildOpts(params)) - return { - content: [{ type: 'text', text: result.text }], - details: { cipher: result.cipher, operation: result.operation, options: result.options }, + pi.registerTool( + defineTool({ + name: 'cipher_encode', + label: 'Cipher Encode', + description: 'Encode plaintext with a local cipher (caesar, vigenere, enigma, etc.)', + promptSnippet: 'Use cipher_encode to encode text with local educational and puzzle ciphers.', + promptGuidelines: [ + 'Specify the cipher name and the text to encode.', + 'Caesar needs --shift (default 3), Vigenère/Playfair need --key, Alberti needs --key and --period, Rail Fence needs --rails, Affine needs --a and --b.', + ], + parameters: cipherParams, + renderCall(args, _theme) { + return new Text(`🔐 encode ${args.cipher}: "${args.text}"`, 0, 0) + }, + async execute(_toolCallId, params): Promise { + try { + const lib = await loadLib() + const cipher = lib.resolveCipher(params.cipher) + const result = cipher.encode(params.text, buildOpts(params)) + return { + content: [{ type: 'text', text: result.text }], + details: { + cipher: result.cipher, + operation: result.operation, + options: result.options, + }, + } + } catch (e: unknown) { + const msg = e instanceof Error ? e.message : String(e) + return { content: [{ type: 'text', text: `Error: ${msg}` }] } } - } catch (e: unknown) { - const msg = e instanceof Error ? e.message : String(e) - return { content: [{ type: 'text', text: `Error: ${msg}` }] } - } - }, - })) + }, + }), + ) - pi.registerTool(defineTool({ - name: 'cipher_decode', - label: 'Cipher Decode', - description: 'Decode ciphertext with a local educational or puzzle cipher', - promptSnippet: 'Use cipher_decode to decode text encoded with local educational and puzzle ciphers.', - promptGuidelines: [ - 'Specify the cipher name and the ciphertext.', - 'Same options as cipher_encode.', - ], - parameters: cipherParams, - renderCall(args, _theme) { - return new Text(`🔓 decode ${args.cipher}: "${args.text}"`, 0, 0) - }, - async execute(_toolCallId, params): Promise { - try { - const lib = await loadLib() - const cipher = lib.resolveCipher(params.cipher) - const result = cipher.decode(params.text, buildOpts(params)) - return { - content: [{ type: 'text', text: result.text }], - details: { cipher: result.cipher, operation: result.operation, options: result.options }, + pi.registerTool( + defineTool({ + name: 'cipher_decode', + label: 'Cipher Decode', + description: 'Decode ciphertext with a local educational or puzzle cipher', + promptSnippet: + 'Use cipher_decode to decode text encoded with local educational and puzzle ciphers.', + promptGuidelines: [ + 'Specify the cipher name and the ciphertext.', + 'Same options as cipher_encode.', + ], + parameters: cipherParams, + renderCall(args, _theme) { + return new Text(`🔓 decode ${args.cipher}: "${args.text}"`, 0, 0) + }, + async execute(_toolCallId, params): Promise { + try { + const lib = await loadLib() + const cipher = lib.resolveCipher(params.cipher) + const result = cipher.decode(params.text, buildOpts(params)) + return { + content: [{ type: 'text', text: result.text }], + details: { + cipher: result.cipher, + operation: result.operation, + options: result.options, + }, + } + } catch (e: unknown) { + const msg = e instanceof Error ? e.message : String(e) + return { content: [{ type: 'text', text: `Error: ${msg}` }] } } - } catch (e: unknown) { - const msg = e instanceof Error ? e.message : String(e) - return { content: [{ type: 'text', text: `Error: ${msg}` }] } - } - }, - })) + }, + }), + ) const bruteParams = Type.Object({ text: Type.String({ description: 'Ciphertext to brute-force' }), }) - pi.registerTool(defineTool({ - name: 'cipher_brute_caesar', - label: 'Brute Force Caesar', - description: 'Try all 25 Caesar shifts and show results', - promptSnippet: 'Use cipher_brute_caesar to brute-force an unknown Caesar shift.', - promptGuidelines: [ - 'Input is ciphertext. Shows all 25 possible decodings.', - 'Look for readable English or Polish plaintext in results.', - ], - parameters: bruteParams, - renderCall(args, _theme) { - return new Text(`🔍 brute caesar: "${args.text}"`, 0, 0) - }, - async execute(_toolCallId, params): Promise { - try { - const lib = await loadLib() - const cipher = lib.create('caesar') - const lines: string[] = [] - for (let shift = 1; shift <= 25; shift++) { - const result = cipher.decode(params.text, { shift }) - lines.push(`shift=${String(shift).padStart(2)} → ${result.text}`) + pi.registerTool( + defineTool({ + name: 'cipher_brute_caesar', + label: 'Brute Force Caesar', + description: 'Try all 25 Caesar shifts and show results', + promptSnippet: 'Use cipher_brute_caesar to brute-force an unknown Caesar shift.', + promptGuidelines: [ + 'Input is ciphertext. Shows all 25 possible decodings.', + 'Look for readable English or Polish plaintext in results.', + ], + parameters: bruteParams, + renderCall(args, _theme) { + return new Text(`🔍 brute caesar: "${args.text}"`, 0, 0) + }, + async execute(_toolCallId, params): Promise { + try { + const lib = await loadLib() + const cipher = lib.create('caesar') + const lines: string[] = [] + for (let shift = 1; shift <= 25; shift++) { + const result = cipher.decode(params.text, { shift }) + lines.push(`shift=${String(shift).padStart(2)} → ${result.text}`) + } + return { content: [{ type: 'text', text: lines.join('\n') }] } + } catch (e: unknown) { + const msg = e instanceof Error ? e.message : String(e) + return { content: [{ type: 'text', text: `Error: ${msg}` }] } } - return { content: [{ type: 'text', text: lines.join('\n') }] } - } catch (e: unknown) { - const msg = e instanceof Error ? e.message : String(e) - return { content: [{ type: 'text', text: `Error: ${msg}` }] } - } - }, - })) + }, + }), + ) const freqParams = Type.Object({ text: Type.String({ description: 'Text to analyze' }), lang: Type.Optional(Type.String({ description: 'Reference language: en (default) or pl' })), }) - pi.registerTool(defineTool({ - name: 'cipher_frequency', - label: 'Frequency Analysis', - description: 'Analyze letter frequency in text for cryptanalysis', - promptSnippet: 'Use cipher_frequency to analyze letter distribution for cipher identification.', - promptGuidelines: [ - 'Useful for identifying substitution ciphers (frequency distribution preserved).', - 'Compare actual frequency order with expected language order (EN: ETAOIN...).', - ], - parameters: freqParams, - renderCall(args, _theme) { - return new Text(`📊 frequency: "${args.text.slice(0, 40)}..."`, 0, 0) - }, - async execute(_toolCallId, params): Promise { - const lib = await loadLib() - const language = params.lang === 'pl' ? 'pl' : 'en' - const analysis = lib.analyzeFrequency(params.text, language) - if (analysis === undefined) { - return { content: [{ type: 'text', text: 'No letters found in input.' }] } - } + pi.registerTool( + defineTool({ + name: 'cipher_frequency', + label: 'Frequency Analysis', + description: 'Analyze letter frequency in text for cryptanalysis', + promptSnippet: + 'Use cipher_frequency to analyze letter distribution for cipher identification.', + promptGuidelines: [ + 'Useful for identifying substitution ciphers (frequency distribution preserved).', + 'Compare actual frequency order with expected language order (EN: ETAOIN...).', + ], + parameters: freqParams, + renderCall(args, _theme) { + return new Text(`📊 frequency: "${args.text.slice(0, 40)}..."`, 0, 0) + }, + async execute(_toolCallId, params): Promise { + const lib = await loadLib() + const language = params.lang === 'pl' ? 'pl' : 'en' + const analysis = lib.analyzeFrequency(params.text, language) + if (analysis === undefined) { + return { content: [{ type: 'text', text: 'No letters found in input.' }] } + } - const maximum = analysis.counts[0]?.[1] ?? 1 - const lines = [`Frequency Analysis (${analysis.total} letters, lang=${analysis.language}):\n`] - for (const [character, count] of analysis.counts) { - const percentage = ((count / analysis.total) * 100).toFixed(1) - const bar = '█'.repeat(Math.ceil((count / maximum) * 15)) - lines.push(` ${character} ${String(count).padStart(4)} (${percentage.padStart(5)}%) ${bar}`) - } - lines.push(`\nExpected (${analysis.language}): ${analysis.reference.split('').join(' ')}`) - lines.push(`Actual: ${analysis.counts.map(([character]) => character).join(' ')}`) - return { content: [{ type: 'text', text: lines.join('\n') }] } - }, - })) + const maximum = analysis.counts[0]?.[1] ?? 1 + const lines = [ + `Frequency Analysis (${analysis.total} letters, lang=${analysis.language}):\n`, + ] + for (const [character, count] of analysis.counts) { + const percentage = ((count / analysis.total) * 100).toFixed(1) + const bar = '█'.repeat(Math.ceil((count / maximum) * 15)) + lines.push( + ` ${character} ${String(count).padStart(4)} (${percentage.padStart(5)}%) ${bar}`, + ) + } + lines.push(`\nExpected (${analysis.language}): ${analysis.reference.split('').join(' ')}`) + lines.push(`Actual: ${analysis.counts.map(([character]) => character).join(' ')}`) + return { content: [{ type: 'text', text: lines.join('\n') }] } + }, + }), + ) } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 651f5ec..1b9d310 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,10 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + vite: npm:@voidzero-dev/vite-plus-core@latest + vitest: 4.1.10 + importers: .: @@ -42,9 +46,9 @@ importers: typescript: specifier: ^6.0.3 version: 6.0.3 - vitest: - specifier: ^4.1.7 - version: 4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + vite-plus: + specifier: ^0.2.9 + version: 0.2.9(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(yaml@2.9.0) packages: @@ -158,6 +162,10 @@ packages: resolution: {integrity: sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==} engines: {node: '>=18.0.0'} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} + engines: {node: '>=6.9.0'} + '@babel/generator@8.0.0-rc.6': resolution: {integrity: sha512-6mIzgVK8DgEzvIapoQwhXTMnnkuE4STQmVv9H03i/tZ2ml8oev3TRvZJgTenK2Bsq0YWNtzOrFdTyNzCMFtjJQ==} engines: {node: ^22.18.0 || >=24.11.0} @@ -200,6 +208,9 @@ packages: resolution: {integrity: sha512-p7/ABylAYlexb31wtRdIfH9L9A0Z2T/9H6zAqzqndkY2PLkvNNc580wGhp/gGKN4Sp9sQvSkhc6Oga8/O+wTyw==} engines: {node: ^22.18.0 || >=24.11.0} + '@blazediff/core@1.9.1': + resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==} + '@bufbuild/protobuf@2.13.0': resolution: {integrity: sha512-acq7c49vxfm1ggJ95P70TX7ABDM0vxr1SYD3BB0o0jnBLB4OAqeHyKuN+cD3w80gXEDQ2zxHpR6CUeA+O/aU9g==} @@ -870,12 +881,300 @@ packages: resolution: {integrity: sha512-eSYWTm620tTk45EKSedaUL8MFYI8hW164hIXsgIHyxu3VobUB3fFCu5t0hQby6OoWRPsG1KkKUG2M5UadiLiVg==} engines: {node: '>=14'} + '@oxc-project/runtime@0.143.0': + resolution: {integrity: sha512-zIuXUf+YGIgsPk0xlQmzTY8NCSc8jE/pSfDodlQ9H3EGZABmr+AtIjXRrnpQAXuXzhDSNqZz9cuhud8hDDLvpg==} + engines: {node: ^20.19.0 || >=22.12.0} + '@oxc-project/types@0.132.0': resolution: {integrity: sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==} '@oxc-project/types@0.133.0': resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} + '@oxc-project/types@0.143.0': + resolution: {integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==} + + '@oxfmt/binding-android-arm-eabi@0.62.0': + resolution: {integrity: sha512-pdsv0C4gPjJ8H1+sd8u0BDx+yLACTL+rgeMIOL1ln4ihSnhw8CWXtYWgvcSkyTfgGBIzFKab+d8rx9Xl4en/Kw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxfmt/binding-android-arm64@0.62.0': + resolution: {integrity: sha512-WC3YQ7uS/KtDrjmqwBviwFKe9qeoi+eXx8aX1z/ffG23Md75myjrJaQqTuJvdOLPoa4EYTjDWH0dHXfwulCVog==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxfmt/binding-darwin-arm64@0.62.0': + resolution: {integrity: sha512-GM8Yf3LjjaR1I8PD0SfeoIlwhsh9GvSF+cQ8sf624Yxnjsyumn95aFzYfKJVefblfDIiOAnZ7QVm2sa21Er/0Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxfmt/binding-darwin-x64@0.62.0': + resolution: {integrity: sha512-d5THp7F8bCxLqNogEXDORRsQD6dosf3EyFtnXfBer6v+8tGdcWIjoDX9WaXrrF/26zOmL8qHpPTKCEvpBDmZkQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxfmt/binding-freebsd-x64@0.62.0': + resolution: {integrity: sha512-1DnrtXGZooOZ0fHgAXZUaDQzBVh1CM2MNW4oBXyQ2aWKvCHjyljvT9fgBkOM0fEOb96X5eqtcfJ0YUVt9jj66g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxfmt/binding-linux-arm-gnueabihf@0.62.0': + resolution: {integrity: sha512-4pQDHOYRH+Huqe0StIaWyvk2CVl/aTaqSrbZpA3/pLS2xH24ME7lBgYprhQF2fRkHBzhGGGKliwxFsDdHwx59g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm-musleabihf@0.62.0': + resolution: {integrity: sha512-X0jAaZJFMCVKhB6YyWVTQ/wN2DLsBcZKSMqTS76bF6riT+XZdtg2FPEdjDvdVbunO9cG+tWiVaEs4Zs38lxYog==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxfmt/binding-linux-arm64-gnu@0.62.0': + resolution: {integrity: sha512-682Z8T5s8T5ATArYtsejKvbIfd8LEAXyyDkKkoZVq8HND7Vx8TYLlrDjDSeYfodMeVwHOgkj13lJYR8cj6vUSg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-arm64-musl@0.62.0': + resolution: {integrity: sha512-lk25fAl7KWaLWVJcW0CHEXB7QlQZtx5eDkjpaGMK0hzXTjUe0Wmlu8IKuFHoviSOcEJedRTs4VE/506VqGxGew==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-ppc64-gnu@0.62.0': + resolution: {integrity: sha512-SFyNqHQLwySceWNLhiSldx7wPXRAzP0L0WcW9GegP3uWrpZGJiZlQO85NbHAFPEfxR9PhZ9qSnZryEh7+v+4Gw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-gnu@0.62.0': + resolution: {integrity: sha512-KYj55C1ywJfHo6+aKDuEmUtVEdJALsC5GwayDGsI6FGz2GxFqNr/mA8nxVsNbJzm7sE5MRqTQ9ziImSzhYXysA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-riscv64-musl@0.62.0': + resolution: {integrity: sha512-BhZDNo5GOU5nC378RhD0/XpvaEBHsH3HLgJp8YZX3A0InC7oivzA63HsRmiXFLtLSHAstEVrDf6fbC7Rs8Jh/A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-linux-s390x-gnu@0.62.0': + resolution: {integrity: sha512-UyAFmyHkgSgUJ/wOM4p3U8AC2yAFvRH5PNBs7TnK0fObTT/XSWcdr/lAzPSWaekHaZFaMeFZyk9n93Joq3J93A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-gnu@0.62.0': + resolution: {integrity: sha512-1iYMP0leytWazFubD/WnINJuIrzRPuoL1aWEJdlGezEzDbTxcd29R4r8IUzP2oWeKst5V02uMJgR2NILlPlG6w==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxfmt/binding-linux-x64-musl@0.62.0': + resolution: {integrity: sha512-4rA/URtJSTVNVAQz6Q8wf7SaRvOXVy+TizriT9hs/Y1XhLR/R+92uWKRQG8yFWRAIEBbFHJ6WevQcl/G9SXEfw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxfmt/binding-openharmony-arm64@0.62.0': + resolution: {integrity: sha512-mSZuFHU2ar1KLUjXpI2QBQcJ1VsOB3mOCgQXuXCpKs19dgh4u+OaovNfrWDfiJb+ihJ2+f7YFcaO9bS2dlTCXA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxfmt/binding-win32-arm64-msvc@0.62.0': + resolution: {integrity: sha512-OfwuhkcjDlqC4EgDojtiV9mzpLqeB9KqTOWPOjLEYBVdDCVSxqW3qzp/xcIxsbtI0UgGCnKvAqYKyY25kf5JZw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxfmt/binding-win32-ia32-msvc@0.62.0': + resolution: {integrity: sha512-P9uDDNFRzghO3X8QAzhkjKhK7JvtABsVn8UYtFX7uor12IAnwNt8nNIctvfWj1JkQU/kE+fmLRPiw7XlrIHsZw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxfmt/binding-win32-x64-msvc@0.62.0': + resolution: {integrity: sha512-dlI5SY7XYQCiCBafntWagCR6HcAJB/NpsLtdlPx8x08+Osz8Ok1HHz1GZuusegCe/VoJ6pAnF5a4pd5OZAq7qQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxlint-tsgolint/darwin-arm64@7.0.2001': + resolution: {integrity: sha512-CUJEdbSZ54+Xy9OXqOhWLTKZKV0BBiV7C2i/ygyVmXtkUNXx5YCzN8DpSSshTAKktoL7S+tnQ/ftFG/i7X896w==} + cpu: [arm64] + os: [darwin] + + '@oxlint-tsgolint/darwin-x64@7.0.2001': + resolution: {integrity: sha512-pXfBb5BqONCcgrXQNUZWXgiYmRSWJzd97S8i41VVOh6ut0tyo+cJ5FKFpczDHxiVNfj/3e7c9B4MtztNdpIVCw==} + cpu: [x64] + os: [darwin] + + '@oxlint-tsgolint/linux-arm64@7.0.2001': + resolution: {integrity: sha512-roP7zujb/QDPzDwEKsFFpzNHHy91/Y7oX9vQXk78ekyZtcQj1QXDIMH33gjDdHBfRl4K9pZ36xhRgrP4Zr+R8A==} + cpu: [arm64] + os: [linux] + + '@oxlint-tsgolint/linux-x64@7.0.2001': + resolution: {integrity: sha512-UDezNqdECVmngu2TPnjaS1YoAmcTaBoI5lV9vk3VahBxoi+I5r9k3iJTT7qZoYWOXTD/7T7bNcwRgrocR6BscQ==} + cpu: [x64] + os: [linux] + + '@oxlint-tsgolint/win32-arm64@7.0.2001': + resolution: {integrity: sha512-uJZhqB6pdXLuN+AD1F5082byyQti/NPmJA77GtcFlmT2HzRelqbNls3SaIqxpjdFgvSBF9g0yOKGBkGFg7kX8Q==} + cpu: [arm64] + os: [win32] + + '@oxlint-tsgolint/win32-x64@7.0.2001': + resolution: {integrity: sha512-FkDRm8hx9OwzGQqyWG1tO5QrTLRApff9DzSgpz9QZau37BR8d1VYKOxMLGf6shPZntJFoTwIIJYT68VndYDCog==} + cpu: [x64] + os: [win32] + + '@oxlint/binding-android-arm-eabi@1.77.0': + resolution: {integrity: sha512-E06sKWS6PiI6HRxS1wyQg22HvApt01hI7fV+T3wUk3OSbaaP4a3hYGY/MIQDmASqCiRjBdpRQYkgMkqH82cWmQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@oxlint/binding-android-arm64@1.77.0': + resolution: {integrity: sha512-NvsKz0KZxTp9cYWPLf+FXaSZwB3oO3peAjtukpOMBgse2vhQSoIIVqeO1yR0lEo/UcdZIDL18uq+kL0LzQ0ytA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@oxlint/binding-darwin-arm64@1.77.0': + resolution: {integrity: sha512-bgjTn6nW4bQCFBvSvuHCpDD+sONvmpo4lGI4PxzMt1quBA+xYxhczk6RiCn3GZ9gY8uhaBbwhj9MdKGfu6T9DA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@oxlint/binding-darwin-x64@1.77.0': + resolution: {integrity: sha512-aotaIttH1R6j1Rwhx0M0htgeZyGtVQqYNTVEYMN/UcgHPquGA6kmk9OyuDc3a2GKUQBC+3C3GVQCcrRPMYqAFA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@oxlint/binding-freebsd-x64@1.77.0': + resolution: {integrity: sha512-nNx/wta7ksRAdYvq+l4AWjXkLxEXHALhENxjj2cYbQAIR4ybaA5L+hCbE63HOmft5czQ6ks+hb8vmEAnn7YGPg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@oxlint/binding-linux-arm-gnueabihf@1.77.0': + resolution: {integrity: sha512-tMLLjM7xXtzXisVCzkOTXNCy9bZVId2wteNwjohlFDR/jY6WagpEDA1c1wu4xRc20Hojaxj+V6DSR7gbKxijWA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm-musleabihf@1.77.0': + resolution: {integrity: sha512-MiAFDFaqR0tmHTAyo0YDcZ5hyLREdYw/RQhc2R3cbT+8O3tB+zqPM2th9TTQ+Uo3jn/embS+DO+HyX9ztCPkOQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@oxlint/binding-linux-arm64-gnu@1.77.0': + resolution: {integrity: sha512-/xqQ3B16i1T4cyt/9Mn+4CpzhUXoBXp7kVpIwzOXNFLj5JmK1bIjsbSnX296Gg8A/o7oDtKWikFgBx0SLwztkw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-arm64-musl@1.77.0': + resolution: {integrity: sha512-LSbwuRKiNCenPDcbARqAZ5RfBy7gmj7vOvfJRLeCDU3gFtSxWbhv/+VTlaUqzUhNj1gFLHB8h7ALnxa/Az6z6g==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@oxlint/binding-linux-ppc64-gnu@1.77.0': + resolution: {integrity: sha512-QWdcH31mXEUe5Nq1s0CfCpceaKjIo9uZtwDjAuL681g1axf+5x8xrg/eXWaw//4NCxYZ4V4e5Hu5tvdR+pTBlg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-riscv64-gnu@1.77.0': + resolution: {integrity: sha512-GnOfYgJxbcElOiPZaDFDl406ONddwvOWk2jvAAAEjwAl4GofNoHF+/HHUIBYa6bFCArlcGPi0XjC4cU1pkgF/Q==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-riscv64-musl@1.77.0': + resolution: {integrity: sha512-AyEMTUCf0xY+hHF+IxqXFQIX0yQOIR8ykpY0lJNOw9xYqOzUX8dyZfRvlG0RfXwuQn2eonf/8NrMmDSZJjdqsA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [riscv64] + os: [linux] + libc: [musl] + + '@oxlint/binding-linux-s390x-gnu@1.77.0': + resolution: {integrity: sha512-sPLzEcNvxd/oyVQ5oZo92CiHkFkpBeRop13E/P3TPY+hZfXHKCOWKI70TE2RYwMKFJDc20EMjH16L7NZICtKTw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-x64-gnu@1.77.0': + resolution: {integrity: sha512-1Oh2ssH2L7lwyvkdSqaMUfsGfwU2Wfvew+obBUYjRVqhpBcUpwnsPSEr1IzVi9XqkuY10geiLsNKecqaZC34Dw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@oxlint/binding-linux-x64-musl@1.77.0': + resolution: {integrity: sha512-0j/2wRgNGO+Qj/M1uu/p57h/hFTTWWcfie0ufkbabeus2s5+/QqkCflnMOwLLN5m2GsNeWp4xdl4cPa4n7QCOQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + libc: [musl] + + '@oxlint/binding-openharmony-arm64@1.77.0': + resolution: {integrity: sha512-BJ/j54qS0usEnyDkLYURMj2iiD9h5Cyy+ppzeMSXBGRXaGRNWnj1Mw14NqWMR5E/PzdgB30OOCCzLzbRoduafw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@oxlint/binding-win32-arm64-msvc@1.77.0': + resolution: {integrity: sha512-Yh8w+g2Lpx7StrvtYkoz9JJvXjB9wxgFChFNb85nrXm/wj/XTwGWS1hve9+900HL7llrntYB3YP+y32E3tRqzA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@oxlint/binding-win32-ia32-msvc@1.77.0': + resolution: {integrity: sha512-zja5b7+6a7UsRFgAQSrnax5vrzliEyNPLCjfXONu/vTWswaIVZGFajJZptaeRvPE4LghtFdAzVFlexTm7MVTGA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ia32] + os: [win32] + + '@oxlint/binding-win32-x64-msvc@1.77.0': + resolution: {integrity: sha512-+teyvPDZ2RjUvo+SuCqS/UhaJl1QtdW5fWT5NJTV61V5MIuIS90Db9LixmtEGvXixyttiK62P96MSu3UlpviBw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@oxlint/plugins@1.73.0': + resolution: {integrity: sha512-OhgMQeMmZA0dcFcX4/priaJZWdFECxiClgq6mRX6aatZEcV9PbKC3P3/v8U1hVjviT1i5U+vR8lAtBV6m4FXAA==} + engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} + + '@polka/url@1.0.0-next.29': + resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} + '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} @@ -1161,9 +1460,22 @@ packages: '@tailwindcss/node@4.3.3': resolution: {integrity: sha512-/T8IKEsf9VTU6tLjgC7+sv2mOPtQxzE2jMw7u4Tt40Tx+QSZxpzh95/H6cMKoja9XuW7iMdLJYBB0o9G1CaAgg==} + '@testing-library/dom@10.4.1': + resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} + engines: {node: '>=18'} + + '@testing-library/user-event@14.6.4': + resolution: {integrity: sha512-QCGwP6QrjypBLwyj5cuyfVamkaIEy/XGY+1VDehbtbQqOggYmTFpFOdWR5mPz14vX8vXLMVjDHlRNBcClyO9ew==} + engines: {node: '>=12', npm: '>=6'} + peerDependencies: + '@testing-library/dom': '>=7.21.4' + '@tybys/wasm-util@0.10.2': resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} + '@types/aria-query@5.0.4': + resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} + '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} @@ -1182,11 +1494,21 @@ packages: '@types/retry@0.12.0': resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} - '@vitest/expect@4.1.7': - resolution: {integrity: sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==} + '@vitest/browser-preview@4.1.10': + resolution: {integrity: sha512-14MJrL59ZFkqXLjwfSk6RzTDy5Czf9UG4+8q8L6Gxjs2aPjEce/cVNYV14bXAc2BvMjUNu904+ZEZA1Xc1wtvQ==} + peerDependencies: + vitest: 4.1.10 + + '@vitest/browser@4.1.10': + resolution: {integrity: sha512-UDwuWGwXj646CBx/bQHOaJSX7np0I8JL/UKQYa1e4QrVHH8VdWtx8eaOuf8sy0ShwDgR6NjJAsp5eF6vjF6qng==} + peerDependencies: + vitest: 4.1.10 + + '@vitest/expect@4.1.10': + resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} - '@vitest/mocker@4.1.7': - resolution: {integrity: sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==} + '@vitest/mocker@4.1.10': + resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -1196,20 +1518,254 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.7': - resolution: {integrity: sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==} + '@vitest/pretty-format@4.1.10': + resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} + + '@vitest/runner@4.1.10': + resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==} + + '@vitest/snapshot@4.1.10': + resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==} + + '@vitest/spy@4.1.10': + resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==} + + '@vitest/utils@4.1.10': + resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} + + '@voidzero-dev/vite-plus-core@0.2.9': + resolution: {integrity: sha512-dWqScAAwa8h/i9jCiGAMs7YarzQWInHZ5gCJNbQkHXA6Zp6A2T2anN9YMFVPpb7CwVFwkI2iPF5yl/DXtq+zUA==} + engines: {node: ^20.19.0 || ^22.18.0 || >=24.11.0} + peerDependencies: + '@arethetypeswrong/core': ^0.18.1 + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.4.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + publint: ^0.3.8 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + typescript: ^5.0.0 || ^6.0.0 || ^7.0.0 + unplugin-unused: ^0.5.0 + unrun: '*' + yaml: ^2.4.2 + peerDependenciesMeta: + '@arethetypeswrong/core': + optional: true + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + publint: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + typescript: + optional: true + unplugin-unused: + optional: true + unrun: + optional: true + yaml: + optional: true + + '@voidzero-dev/vite-plus-darwin-arm64@0.2.9': + resolution: {integrity: sha512-/qJHqMfyy/LiCJk4UYZfFW6Comsfm8zDuy4P8UFWnFqRjmefYvZbMK4ThYNM87tKNWYWjsnClzssjJOmDTAc8w==} + engines: {node: '>=20.0.0'} + cpu: [arm64] + os: [darwin] + + '@voidzero-dev/vite-plus-darwin-x64@0.2.9': + resolution: {integrity: sha512-3MGnNeazgYAqQTaC7JIbZyrTHmxSXTWFr9G1yAdeItKasB1R8HKIkCS1Qnr49Ge4S/cyOODivdbcpqFkrT93ng==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [darwin] + + '@voidzero-dev/vite-plus-linux-arm64-gnu@0.2.9': + resolution: {integrity: sha512-6LmukER8qD4UBIRqMNv4Ilq7CxfRhngLUXlMv8vbTupeLRWPJSKvKEHyRxCwr6JP57Gxfr8KrX1ye42WzcZF0g==} + engines: {node: '>=20.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@voidzero-dev/vite-plus-linux-arm64-musl@0.2.9': + resolution: {integrity: sha512-cBs626GWkyJlwKP0nsdHlMWpuTl9xOWRxAUoqtxXPtw80bVy4WM5eNS4SXPC5pX10jR7DRIkRpzNAsy7fv8Faw==} + engines: {node: '>=20.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@voidzero-dev/vite-plus-linux-x64-gnu@0.2.9': + resolution: {integrity: sha512-2Iy8x4PCPMNzXeu3pREevlggoeK8PwtdUiCpoSybAZBtR/aqMxsJREyt/eKv45F8lsiNlA8PbIWEvPxLSgzFLQ==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@voidzero-dev/vite-plus-linux-x64-musl@0.2.9': + resolution: {integrity: sha512-zuGx+eRotWPd9cmh1X9AfsC2tN/Ad9Hk6LAzlxoKJUjEkTsY3WjVJ6Da0z48SAUFuenI0JkdqXnMCrePtGvWHg==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + + '@voidzero-dev/vite-plus-win32-arm64-msvc@0.2.9': + resolution: {integrity: sha512-kaKb5Q8ReYTBfvLgUhdpLJG3aoNF8HOVJpf8qBm+THsd4WRrkRwsBHp2ITsU8oXl2gnDjFGhPDaURegd/z6Wxw==} + engines: {node: '>=20.0.0'} + cpu: [arm64] + os: [win32] + + '@voidzero-dev/vite-plus-win32-x64-msvc@0.2.9': + resolution: {integrity: sha512-/fEk3gbQTJknCiYM/GTL/L++Azsav8rCAjmtKrjmCbqEif5IMzqTfvM68n+PiLB3JVoQJGF/mg2niODr0IE/2w==} + engines: {node: '>=20.0.0'} + cpu: [x64] + os: [win32] + + '@yuku-codegen/binding-darwin-arm64@0.5.48': + resolution: {integrity: sha512-yo96Oef12WzqnphInfz/eexVse3+kWgfGS5g2S3rFS3dcGn1ENW9xLFDZUP9rh+yP76DOq38wBoFi1+I9+6qBg==} + cpu: [arm64] + os: [darwin] + + '@yuku-codegen/binding-darwin-x64@0.5.48': + resolution: {integrity: sha512-aRCTw0EZC4bVosmw//0OMYP5tGWFE0Cu5yUBFkUbhXx/iBzvORcJ2xPNlOp/vtCCo9Ys4vp8b0DigJV6uOVb2g==} + cpu: [x64] + os: [darwin] + + '@yuku-codegen/binding-freebsd-x64@0.5.48': + resolution: {integrity: sha512-CA0AQAEApDkbw51PdLWMtKPJ41/7rvXsS3SJs+phG7fHJI+MuFzWuLbkucZfZoEOiDscmcsfYIdgL8BsfuyKKQ==} + cpu: [x64] + os: [freebsd] + + '@yuku-codegen/binding-linux-arm-gnu@0.5.48': + resolution: {integrity: sha512-DuSQlk8bH4gpmW3/00P0NLagAcMv8jOxjT40cQmxKRkktr+SUOALCfkT89tdDq3qtY95NR2GXOZ7AjNh7KKqCw==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@yuku-codegen/binding-linux-arm-musl@0.5.48': + resolution: {integrity: sha512-bxj4Ee+wlaJcWJwft2ReJXWw5sfl1qavDz6+dlRdU1xfTEtjPSNiAWhiCHnJR0R4Ygd57DnzSQmAVGvFv6RcGw==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@yuku-codegen/binding-linux-arm64-gnu@0.5.48': + resolution: {integrity: sha512-mk5JVWh+0JOe5ue8k17kbYX8uGBoKt3ZqoCyxNh4nYAAcX7+X1tFUiU7jbjctu4vHeejCBFSTdQ021+V31cUCQ==} + cpu: [arm64] + os: [linux] + libc: [glibc] + + '@yuku-codegen/binding-linux-arm64-musl@0.5.48': + resolution: {integrity: sha512-4q3vkrNghbllyxOm2KesFLxCPKHF7r3JyQ7BWZccY1j2Y05yKoIFhoWCqIuQ2W/dpte9RI0+OVfwyxnrKg6fkA==} + cpu: [arm64] + os: [linux] + libc: [musl] + + '@yuku-codegen/binding-linux-x64-gnu@0.5.48': + resolution: {integrity: sha512-csd4M1EVrGaohM8acM6gq1zpUA/Rwe2ulUMBKUcwQXm/k6n7cq1A++qdew78SOVb4do3JH1WE+WFwoGQAcWc1w==} + cpu: [x64] + os: [linux] + libc: [glibc] + + '@yuku-codegen/binding-linux-x64-musl@0.5.48': + resolution: {integrity: sha512-KcDuEOT+GFoVKdvAWOv1v9iYjwnmvMZlO+j1Rw+5PYdeFLGWGzv/DD11y4SAAdwXIFcil4T0hibeIaF82WStMg==} + cpu: [x64] + os: [linux] + libc: [musl] + + '@yuku-codegen/binding-win32-arm64@0.5.48': + resolution: {integrity: sha512-HI8qNrI8dWM5BuqIMKsqornRvTNFrE6sm5zToIJ9YIa9zt5+29P7fJ7Nr39EVf6dAWSb6q7JSpScJnRsQ+FgZA==} + cpu: [arm64] + os: [win32] + + '@yuku-codegen/binding-win32-x64@0.5.48': + resolution: {integrity: sha512-X5YWJLO6EfBZpeBqO0AYESnUizbpFDWArcvVD61w0PEWQ3CaFRLnbQXs+kpM4ZZfGMfIE22zfA08QSY67q7TNQ==} + cpu: [x64] + os: [win32] + + '@yuku-parser/binding-darwin-arm64@0.5.48': + resolution: {integrity: sha512-If8mb7HH3vqghJ2NNZ8SuHfhsnjVzOxJpB8xcNOXS5WjYrs2mUhHIh5KOIvK13hDOzh0htGeGK3A6MsiEqE7HQ==} + cpu: [arm64] + os: [darwin] + + '@yuku-parser/binding-darwin-x64@0.5.48': + resolution: {integrity: sha512-EimvPXfspzxf1K11eB6tCW5oiQEXB8g84T2wP1TwzQagdDKo33bkmmVF0B32vTIpXnk/Ifu5IB61izZ1MylljA==} + cpu: [x64] + os: [darwin] + + '@yuku-parser/binding-freebsd-x64@0.5.48': + resolution: {integrity: sha512-0GcUMrumLHheThY9r5Tp46gaZYzn0irWPS1Zba6WY+vVQfhUtzGiWgXxI6tuXX0N32kEaaEVRpkKctvo6Kx3aQ==} + cpu: [x64] + os: [freebsd] + + '@yuku-parser/binding-linux-arm-gnu@0.5.48': + resolution: {integrity: sha512-8S5T5wjCC73dmmpQeZ49aYsSunIUM3D4Fc6rdK96c+Ayg/p3FmeSPF3xuLZHejcTmqJIIvnbfPlUF+rB6DITjQ==} + cpu: [arm] + os: [linux] + libc: [glibc] + + '@yuku-parser/binding-linux-arm-musl@0.5.48': + resolution: {integrity: sha512-tTmbxvnUHcK2/crS9547vk2SMmsajH1yqJ8ltXhIuHJgqR1v+d9n9KT+kSayo/5CS76LegeYxhMFjEivBH2hFA==} + cpu: [arm] + os: [linux] + libc: [musl] + + '@yuku-parser/binding-linux-arm64-gnu@0.5.48': + resolution: {integrity: sha512-KGYCBMqI2zfwyhgq5tpPVNe7jpUeYTBm8DhjdS+zqWNumde/PEC170QE5RHxcOAlsirIDeIUk0jqx+r/axoFSw==} + cpu: [arm64] + os: [linux] + libc: [glibc] - '@vitest/runner@4.1.7': - resolution: {integrity: sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==} + '@yuku-parser/binding-linux-arm64-musl@0.5.48': + resolution: {integrity: sha512-2wTSMsCSXLTc2lZUjMAuU5X4cje55u205WJqfV5NWNF6j9pW/tXyxr15dJeekj8ziLqBXzIsj4DbRh4sY/WcjA==} + cpu: [arm64] + os: [linux] + libc: [musl] - '@vitest/snapshot@4.1.7': - resolution: {integrity: sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==} + '@yuku-parser/binding-linux-x64-gnu@0.5.48': + resolution: {integrity: sha512-d/6v9UnGglVu1WC2JQyv/5aWSi5fXZeGSlidCfmHp4+N65N1GDKUnFtys5MK5eAPeAjTgSHGGtOc/yCcKTlv3A==} + cpu: [x64] + os: [linux] + libc: [glibc] - '@vitest/spy@4.1.7': - resolution: {integrity: sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==} + '@yuku-parser/binding-linux-x64-musl@0.5.48': + resolution: {integrity: sha512-gX19gw6u4ApPy7SYMPKfFlEkrtj6WlORvrTKK3sBQqjyV+8+mUAkQgxXNjHw4RnOiAmVYg7TOlZcg8d+Qqod9A==} + cpu: [x64] + os: [linux] + libc: [musl] - '@vitest/utils@4.1.7': - resolution: {integrity: sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==} + '@yuku-parser/binding-win32-arm64@0.5.48': + resolution: {integrity: sha512-w6cQQLbqj3Jcom5Q7ifm103NUOQ9d+Cb4VU5lkrZDjMnwVJ9Hzzg1vCQR7miJuF44vhCXldbme5UryE3giEKlA==} + cpu: [arm64] + os: [win32] + + '@yuku-parser/binding-win32-x64@0.5.48': + resolution: {integrity: sha512-4gO0HmG7fzFxrw1rs0dUdnnaY9YgennjETqDWrTSp7x9fmTUOAoN4VsMfP7YyliQeG1WJJHc55O+rOhmsLppow==} + cpu: [x64] + os: [win32] + + '@yuku-toolchain/types@0.5.43': + resolution: {integrity: sha512-kSpvPntnXw5+lYjO71ffBEnQ5ycQ74KGIYknh0TS4xeyCuBkOqxyJumxZkMhLBBUCLjDAbx2+Icnr3Zh4ftjpQ==} adm-zip@0.5.18: resolution: {integrity: sha512-ufJnssQGbxzLNS1Ho9bCtX4rQKCCvoVuDLHoJyc3F9dOGDB4BkWs2Ci0kv53lqocAEQ/Cbi+I2XCsNYGqVYqng==} @@ -1219,14 +1775,25 @@ packages: resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} + ansi-regex@5.0.1: + resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} + engines: {node: '>=8'} + ansi-regex@6.2.2: resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} + ansi-styles@5.2.0: + resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} + engines: {node: '>=10'} + ansi-styles@6.2.3: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} + aria-query@5.3.0: + resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} + assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} @@ -1345,6 +1912,10 @@ packages: defu@6.1.7: resolution: {integrity: sha512-7z22QmUWiQ/2d0KkdYmANbRUVABpZ9SNYyH5vx6PZ+nE5bcC0l7uFvEfHlyld/HcGBFTL536ClDt3DEcSlEJAQ==} + dequal@2.0.3: + resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} + engines: {node: '>=6'} + destr@2.0.5: resolution: {integrity: sha512-ugFTXCtDZunbzasqBxrK93Ik/DRYsO6S/fedkWEMKqt04xZ4csmnmwGDBAb07QWNaGMAmnTIemsYZCksjATwsA==} @@ -1362,6 +1933,9 @@ packages: resolution: {integrity: sha512-DPi0FmjiSU5EvQV0++GFDOJ9ASQUVFh5kD+OzOnYdi7n3Wpm9hWWGfB/O2blfHcMVTL5WkQXSnRiK9makhrcnw==} engines: {node: '>=0.3.1'} + dom-accessibility-api@0.5.16: + resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} + dts-resolver@3.0.0: resolution: {integrity: sha512-1T1f+z+4tl9XD+m+0HBgWoL/nm0bOIffyWaUuUSBlFg/86IWvfx+wjNaO/ybU0AJzG9/Mi5hBUgGV6zCmWEN7Q==} engines: {node: ^22.18.0 || >=24.0.0} @@ -1532,6 +2106,9 @@ packages: resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} hasBin: true + js-tokens@4.0.0: + resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} + jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} @@ -1559,30 +2136,60 @@ packages: cpu: [arm64] os: [android] + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + lightningcss-darwin-arm64@1.32.0: resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + lightningcss-darwin-x64@1.32.0: resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + lightningcss-freebsd-x64@1.32.0: resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + lightningcss-linux-arm-gnueabihf@1.32.0: resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + lightningcss-linux-arm64-gnu@1.32.0: resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} engines: {node: '>= 12.0.0'} @@ -1590,6 +2197,13 @@ packages: os: [linux] libc: [glibc] + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [glibc] + lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} @@ -1597,6 +2211,13 @@ packages: os: [linux] libc: [musl] + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + libc: [musl] + lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} @@ -1604,6 +2225,13 @@ packages: os: [linux] libc: [glibc] + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [glibc] + lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} @@ -1611,22 +2239,45 @@ packages: os: [linux] libc: [musl] + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + libc: [musl] + lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + lightningcss-win32-x64-msvc@1.32.0: resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + lightningcss@1.32.0: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + long@5.3.2: resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} @@ -1639,6 +2290,10 @@ packages: peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 + lz-string@1.5.0: + resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} + hasBin: true + magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} @@ -1666,6 +2321,10 @@ packages: resolution: {integrity: sha512-t9VmxaqrmANnEOBhpSDI6HD192Ge48k8vmWqQQL7hSFEqHEYwZbbsu49+aKLWZeRvFs3j1pMhXOqqF4kPlvjkQ==} engines: {node: '>=18.0.0'} + mrmime@2.0.1: + resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} + engines: {node: '>=10'} + ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -1722,6 +2381,36 @@ packages: zod: optional: true + oxfmt@0.62.0: + resolution: {integrity: sha512-vxgGHTmnDU9j4CX7dDBLzxgmHxfda/yPcgJkGCMUSCwRmz+euo/V08xXLNgXTeqAB9Fhf3Pe2nO1RNKLCVgphQ==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + svelte: ^5.0.0 + vite-plus: '*' + peerDependenciesMeta: + svelte: + optional: true + vite-plus: + optional: true + + oxlint-tsgolint@7.0.2001: + resolution: {integrity: sha512-KjK/XLcXr1DSyonKhsuFqJRiuKqcyG9j3LJ8nkOsrLzGvodBPqzHOKauy10asLMDI0sUpvb+1sxlzff3udZvfg==} + hasBin: true + + oxlint@1.77.0: + resolution: {integrity: sha512-qnGh8XJHaQ0dprrDXNQZgS0FgjI6v+V3+X8DwmaV++5Aamy6jGKfDdQ1TUvhUxtmKFAbEf4/WeO5QZX+5WSngg==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + oxlint-tsgolint: '>=7.0.2001' + vite-plus: '*' + peerDependenciesMeta: + oxlint-tsgolint: + optional: true + vite-plus: + optional: true + p-retry@4.6.2: resolution: {integrity: sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==} engines: {node: '>=8'} @@ -1757,10 +2446,18 @@ packages: platform@1.3.6: resolution: {integrity: sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg==} + pngjs@7.0.0: + resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} + engines: {node: '>=14.19.0'} + postcss@8.5.15: resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} + pretty-format@27.5.1: + resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} + engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} + proper-lockfile@4.1.2: resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} @@ -1786,6 +2483,9 @@ packages: peerDependencies: react: ^19.2.7 + react-is@17.0.2: + resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} + react@19.2.7: resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} @@ -1903,6 +2603,10 @@ packages: signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} + sirv@3.0.2: + resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} + engines: {node: '>=18'} + source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} @@ -1949,10 +2653,18 @@ packages: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} + tinypool@2.1.0: + resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} + engines: {node: ^20.0.0 || >=22.0.0} + tinyrainbow@3.1.0: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} + totalist@3.0.1: + resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} + engines: {node: '>=6'} + ts-algebra@2.0.0: resolution: {integrity: sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==} @@ -1989,6 +2701,19 @@ packages: resolution: {integrity: sha512-TkUDgb6tl7KOGZ+7e8E3d2FYgUQgF6z5YypqjWmixVQSQERFcVrVg0ySADm2LVLRh5ljAaHTCR5Fmz3Q34rB7Q==} engines: {node: '>=22.19.0'} + vite-plus@0.2.9: + resolution: {integrity: sha512-8uRNqAxh9no3AU4Lep8BEYhkim07+3NO+mhuxTWiN0k30syGT/2+ue/DtWYhtzQ7yi2f2WKpjOhoI4/QkWWbUg==} + engines: {node: ^20.19.0 || ^22.18.0 || >=24.11.0} + hasBin: true + peerDependencies: + '@vitest/browser-playwright': 4.1.10 + '@vitest/browser-webdriverio': 4.1.10 + peerDependenciesMeta: + '@vitest/browser-playwright': + optional: true + '@vitest/browser-webdriverio': + optional: true + vite@8.0.14: resolution: {integrity: sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2032,20 +2757,20 @@ packages: yaml: optional: true - vitest@4.1.7: - resolution: {integrity: sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==} + vitest@4.1.10: + resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 - '@vitest/browser-playwright': 4.1.7 - '@vitest/browser-preview': 4.1.7 - '@vitest/browser-webdriverio': 4.1.7 - '@vitest/coverage-istanbul': 4.1.7 - '@vitest/coverage-v8': 4.1.7 - '@vitest/ui': 4.1.7 + '@vitest/browser-playwright': 4.1.10 + '@vitest/browser-preview': 4.1.10 + '@vitest/browser-webdriverio': 4.1.10 + '@vitest/coverage-istanbul': 4.1.10 + '@vitest/coverage-v8': 4.1.10 + '@vitest/ui': 4.1.10 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 @@ -2127,6 +2852,12 @@ packages: resolution: {integrity: sha512-2rAgRKu54VsHkqI0/tYkmluGXHD4KW7yZoycuqDQ15QOTnc2VVfy0nN/1eMhnQLO00A+dwtK20xuCnc1YGeUyg==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} + yuku-codegen@0.5.48: + resolution: {integrity: sha512-p7HxD5Xl4jzDzqMrGePAOeSHmRY4g58h4HuGq15weQFPxuPWd/W6e7nqp/+Lea6JfpOdBwJOAyXFqIZ/J9Zfnw==} + + yuku-parser@0.5.48: + resolution: {integrity: sha512-OWBfhrpgK9+/4+IXG9oT8Bao4AhViQA7vdyNNH7EUg8dQYgwa70XtIBWTpCEme1P1ECyoDNYkn0wT63f8XRcVA==} + zod-to-json-schema@3.25.2: resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} peerDependencies: @@ -2369,6 +3100,12 @@ snapshots: '@aws/lambda-invoke-store@0.2.4': {} + '@babel/code-frame@7.29.7': + dependencies: + '@babel/helper-validator-identifier': 7.29.7 + js-tokens: 4.0.0 + picocolors: 1.1.1 + '@babel/generator@8.0.0-rc.6': dependencies: '@babel/parser': 8.0.0-rc.6 @@ -2406,6 +3143,8 @@ snapshots: '@babel/helper-string-parser': 8.0.0-rc.6 '@babel/helper-validator-identifier': 8.0.0-rc.6 + '@blazediff/core@1.9.1': {} + '@bufbuild/protobuf@2.13.0': {} '@earendil-works/pi-agent-core@0.78.0(ws@8.21.0)(zod@4.4.3)': @@ -3041,10 +3780,150 @@ snapshots: '@opentelemetry/semantic-conventions@1.43.0': {} + '@oxc-project/runtime@0.143.0': {} + '@oxc-project/types@0.132.0': {} '@oxc-project/types@0.133.0': {} + '@oxc-project/types@0.143.0': {} + + '@oxfmt/binding-android-arm-eabi@0.62.0': + optional: true + + '@oxfmt/binding-android-arm64@0.62.0': + optional: true + + '@oxfmt/binding-darwin-arm64@0.62.0': + optional: true + + '@oxfmt/binding-darwin-x64@0.62.0': + optional: true + + '@oxfmt/binding-freebsd-x64@0.62.0': + optional: true + + '@oxfmt/binding-linux-arm-gnueabihf@0.62.0': + optional: true + + '@oxfmt/binding-linux-arm-musleabihf@0.62.0': + optional: true + + '@oxfmt/binding-linux-arm64-gnu@0.62.0': + optional: true + + '@oxfmt/binding-linux-arm64-musl@0.62.0': + optional: true + + '@oxfmt/binding-linux-ppc64-gnu@0.62.0': + optional: true + + '@oxfmt/binding-linux-riscv64-gnu@0.62.0': + optional: true + + '@oxfmt/binding-linux-riscv64-musl@0.62.0': + optional: true + + '@oxfmt/binding-linux-s390x-gnu@0.62.0': + optional: true + + '@oxfmt/binding-linux-x64-gnu@0.62.0': + optional: true + + '@oxfmt/binding-linux-x64-musl@0.62.0': + optional: true + + '@oxfmt/binding-openharmony-arm64@0.62.0': + optional: true + + '@oxfmt/binding-win32-arm64-msvc@0.62.0': + optional: true + + '@oxfmt/binding-win32-ia32-msvc@0.62.0': + optional: true + + '@oxfmt/binding-win32-x64-msvc@0.62.0': + optional: true + + '@oxlint-tsgolint/darwin-arm64@7.0.2001': + optional: true + + '@oxlint-tsgolint/darwin-x64@7.0.2001': + optional: true + + '@oxlint-tsgolint/linux-arm64@7.0.2001': + optional: true + + '@oxlint-tsgolint/linux-x64@7.0.2001': + optional: true + + '@oxlint-tsgolint/win32-arm64@7.0.2001': + optional: true + + '@oxlint-tsgolint/win32-x64@7.0.2001': + optional: true + + '@oxlint/binding-android-arm-eabi@1.77.0': + optional: true + + '@oxlint/binding-android-arm64@1.77.0': + optional: true + + '@oxlint/binding-darwin-arm64@1.77.0': + optional: true + + '@oxlint/binding-darwin-x64@1.77.0': + optional: true + + '@oxlint/binding-freebsd-x64@1.77.0': + optional: true + + '@oxlint/binding-linux-arm-gnueabihf@1.77.0': + optional: true + + '@oxlint/binding-linux-arm-musleabihf@1.77.0': + optional: true + + '@oxlint/binding-linux-arm64-gnu@1.77.0': + optional: true + + '@oxlint/binding-linux-arm64-musl@1.77.0': + optional: true + + '@oxlint/binding-linux-ppc64-gnu@1.77.0': + optional: true + + '@oxlint/binding-linux-riscv64-gnu@1.77.0': + optional: true + + '@oxlint/binding-linux-riscv64-musl@1.77.0': + optional: true + + '@oxlint/binding-linux-s390x-gnu@1.77.0': + optional: true + + '@oxlint/binding-linux-x64-gnu@1.77.0': + optional: true + + '@oxlint/binding-linux-x64-musl@1.77.0': + optional: true + + '@oxlint/binding-openharmony-arm64@1.77.0': + optional: true + + '@oxlint/binding-win32-arm64-msvc@1.77.0': + optional: true + + '@oxlint/binding-win32-ia32-msvc@1.77.0': + optional: true + + '@oxlint/binding-win32-x64-msvc@1.77.0': + optional: true + + '@oxlint/plugins@1.73.0': {} + + '@polka/url@1.0.0-next.29': {} + '@protobufjs/aspromise@1.1.2': {} '@protobufjs/base64@1.1.2': {} @@ -3240,11 +4119,28 @@ snapshots: source-map-js: 1.2.1 tailwindcss: 4.3.3 + '@testing-library/dom@10.4.1': + dependencies: + '@babel/code-frame': 7.29.7 + '@babel/runtime': 7.29.7 + '@types/aria-query': 5.0.4 + aria-query: 5.3.0 + dom-accessibility-api: 0.5.16 + lz-string: 1.5.0 + picocolors: 1.1.1 + pretty-format: 27.5.1 + + '@testing-library/user-event@14.6.4(@testing-library/dom@10.4.1)': + dependencies: + '@testing-library/dom': 10.4.1 + '@tybys/wasm-util@0.10.2': dependencies: tslib: 2.8.1 optional: true + '@types/aria-query@5.0.4': {} + '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 @@ -3262,56 +4158,210 @@ snapshots: '@types/retry@0.12.0': {} - '@vitest/expect@4.1.7': + '@vitest/browser-preview@4.1.10(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10)': + dependencies: + '@testing-library/dom': 10.4.1 + '@testing-library/user-event': 14.6.4(@testing-library/dom@10.4.1) + '@vitest/browser': 4.1.10(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10) + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-preview@4.1.10)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/browser@4.1.10(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10)': + dependencies: + '@blazediff/core': 1.9.1 + '@vitest/mocker': 4.1.10(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + '@vitest/utils': 4.1.10 + magic-string: 0.30.21 + pngjs: 7.0.0 + sirv: 3.0.2 + tinyrainbow: 3.1.0 + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-preview@4.1.10)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + ws: 8.21.0 + transitivePeerDependencies: + - bufferutil + - msw + - utf-8-validate + - vite + + '@vitest/expect@4.1.10': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.7 - '@vitest/utils': 4.1.7 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.7(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': + '@vitest/mocker@4.1.10(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))': dependencies: - '@vitest/spy': 4.1.7 + '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: vite: 8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0) - '@vitest/pretty-format@4.1.7': + '@vitest/pretty-format@4.1.10': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.7': + '@vitest/runner@4.1.10': dependencies: - '@vitest/utils': 4.1.7 + '@vitest/utils': 4.1.10 pathe: 2.0.3 - '@vitest/snapshot@4.1.7': + '@vitest/snapshot@4.1.10': dependencies: - '@vitest/pretty-format': 4.1.7 - '@vitest/utils': 4.1.7 + '@vitest/pretty-format': 4.1.10 + '@vitest/utils': 4.1.10 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.7': {} + '@vitest/spy@4.1.10': {} - '@vitest/utils@4.1.7': + '@vitest/utils@4.1.10': dependencies: - '@vitest/pretty-format': 4.1.7 + '@vitest/pretty-format': 4.1.10 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 + '@voidzero-dev/vite-plus-core@0.2.9(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(typescript@6.0.3)(yaml@2.9.0)': + dependencies: + '@oxc-project/runtime': 0.143.0 + '@oxc-project/types': 0.143.0 + lightningcss: 1.33.0 + postcss: 8.5.15 + yuku-codegen: 0.5.48 + yuku-parser: 0.5.48 + optionalDependencies: + '@types/node': 25.9.1 + '@voidzero-dev/vite-plus-darwin-arm64': 0.2.9 + '@voidzero-dev/vite-plus-darwin-x64': 0.2.9 + '@voidzero-dev/vite-plus-linux-arm64-gnu': 0.2.9 + '@voidzero-dev/vite-plus-linux-arm64-musl': 0.2.9 + '@voidzero-dev/vite-plus-linux-x64-gnu': 0.2.9 + '@voidzero-dev/vite-plus-linux-x64-musl': 0.2.9 + '@voidzero-dev/vite-plus-win32-arm64-msvc': 0.2.9 + '@voidzero-dev/vite-plus-win32-x64-msvc': 0.2.9 + esbuild: 0.28.0 + fsevents: 2.3.3 + jiti: 2.7.0 + tsx: 4.22.4 + typescript: 6.0.3 + yaml: 2.9.0 + + '@voidzero-dev/vite-plus-darwin-arm64@0.2.9': + optional: true + + '@voidzero-dev/vite-plus-darwin-x64@0.2.9': + optional: true + + '@voidzero-dev/vite-plus-linux-arm64-gnu@0.2.9': + optional: true + + '@voidzero-dev/vite-plus-linux-arm64-musl@0.2.9': + optional: true + + '@voidzero-dev/vite-plus-linux-x64-gnu@0.2.9': + optional: true + + '@voidzero-dev/vite-plus-linux-x64-musl@0.2.9': + optional: true + + '@voidzero-dev/vite-plus-win32-arm64-msvc@0.2.9': + optional: true + + '@voidzero-dev/vite-plus-win32-x64-msvc@0.2.9': + optional: true + + '@yuku-codegen/binding-darwin-arm64@0.5.48': + optional: true + + '@yuku-codegen/binding-darwin-x64@0.5.48': + optional: true + + '@yuku-codegen/binding-freebsd-x64@0.5.48': + optional: true + + '@yuku-codegen/binding-linux-arm-gnu@0.5.48': + optional: true + + '@yuku-codegen/binding-linux-arm-musl@0.5.48': + optional: true + + '@yuku-codegen/binding-linux-arm64-gnu@0.5.48': + optional: true + + '@yuku-codegen/binding-linux-arm64-musl@0.5.48': + optional: true + + '@yuku-codegen/binding-linux-x64-gnu@0.5.48': + optional: true + + '@yuku-codegen/binding-linux-x64-musl@0.5.48': + optional: true + + '@yuku-codegen/binding-win32-arm64@0.5.48': + optional: true + + '@yuku-codegen/binding-win32-x64@0.5.48': + optional: true + + '@yuku-parser/binding-darwin-arm64@0.5.48': + optional: true + + '@yuku-parser/binding-darwin-x64@0.5.48': + optional: true + + '@yuku-parser/binding-freebsd-x64@0.5.48': + optional: true + + '@yuku-parser/binding-linux-arm-gnu@0.5.48': + optional: true + + '@yuku-parser/binding-linux-arm-musl@0.5.48': + optional: true + + '@yuku-parser/binding-linux-arm64-gnu@0.5.48': + optional: true + + '@yuku-parser/binding-linux-arm64-musl@0.5.48': + optional: true + + '@yuku-parser/binding-linux-x64-gnu@0.5.48': + optional: true + + '@yuku-parser/binding-linux-x64-musl@0.5.48': + optional: true + + '@yuku-parser/binding-win32-arm64@0.5.48': + optional: true + + '@yuku-parser/binding-win32-x64@0.5.48': + optional: true + + '@yuku-toolchain/types@0.5.43': {} + adm-zip@0.5.18: optional: true agent-base@7.1.4: {} + ansi-regex@5.0.1: {} + ansi-regex@6.2.2: {} + ansi-styles@5.2.0: {} + ansi-styles@6.2.3: {} + aria-query@5.3.0: + dependencies: + dequal: 2.0.3 + assertion-error@2.0.1: {} ast-kit@3.0.0-beta.1: @@ -3406,6 +4456,8 @@ snapshots: defu@6.1.7: {} + dequal@2.0.3: {} + destr@2.0.5: {} detect-libc@2.1.2: {} @@ -3417,6 +4469,8 @@ snapshots: diff@8.0.4: {} + dom-accessibility-api@0.5.16: {} + dts-resolver@3.0.0: {} ecdsa-sig-formatter@1.0.11: @@ -3614,6 +4668,8 @@ snapshots: jiti@2.7.0: {} + js-tokens@4.0.0: {} + jsesc@3.1.0: {} json-bigint@1.0.0: @@ -3642,36 +4698,69 @@ snapshots: lightningcss-android-arm64@1.32.0: optional: true + lightningcss-android-arm64@1.33.0: + optional: true + lightningcss-darwin-arm64@1.32.0: optional: true + lightningcss-darwin-arm64@1.33.0: + optional: true + lightningcss-darwin-x64@1.32.0: optional: true + lightningcss-darwin-x64@1.33.0: + optional: true + lightningcss-freebsd-x64@1.32.0: optional: true + lightningcss-freebsd-x64@1.33.0: + optional: true + lightningcss-linux-arm-gnueabihf@1.32.0: optional: true + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + lightningcss-linux-arm64-gnu@1.32.0: optional: true + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + lightningcss-linux-arm64-musl@1.32.0: optional: true + lightningcss-linux-arm64-musl@1.33.0: + optional: true + lightningcss-linux-x64-gnu@1.32.0: optional: true + lightningcss-linux-x64-gnu@1.33.0: + optional: true + lightningcss-linux-x64-musl@1.32.0: optional: true + lightningcss-linux-x64-musl@1.33.0: + optional: true + lightningcss-win32-arm64-msvc@1.32.0: optional: true + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + lightningcss-win32-x64-msvc@1.32.0: optional: true + lightningcss-win32-x64-msvc@1.33.0: + optional: true + lightningcss@1.32.0: dependencies: detect-libc: 2.1.2 @@ -3688,6 +4777,22 @@ snapshots: lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + long@5.3.2: {} lru-cache@11.5.1: {} @@ -3696,6 +4801,8 @@ snapshots: dependencies: react: 19.2.7 + lz-string@1.5.0: {} + magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 @@ -3717,6 +4824,8 @@ snapshots: modern-tar@0.7.7: {} + mrmime@2.0.1: {} + ms@2.1.3: {} mupdf@1.28.0: {} @@ -3787,6 +4896,64 @@ snapshots: ws: 8.21.0 zod: 4.4.3 + oxfmt@0.62.0(vite-plus@0.2.9(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(yaml@2.9.0)): + dependencies: + tinypool: 2.1.0 + optionalDependencies: + '@oxfmt/binding-android-arm-eabi': 0.62.0 + '@oxfmt/binding-android-arm64': 0.62.0 + '@oxfmt/binding-darwin-arm64': 0.62.0 + '@oxfmt/binding-darwin-x64': 0.62.0 + '@oxfmt/binding-freebsd-x64': 0.62.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.62.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.62.0 + '@oxfmt/binding-linux-arm64-gnu': 0.62.0 + '@oxfmt/binding-linux-arm64-musl': 0.62.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.62.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.62.0 + '@oxfmt/binding-linux-riscv64-musl': 0.62.0 + '@oxfmt/binding-linux-s390x-gnu': 0.62.0 + '@oxfmt/binding-linux-x64-gnu': 0.62.0 + '@oxfmt/binding-linux-x64-musl': 0.62.0 + '@oxfmt/binding-openharmony-arm64': 0.62.0 + '@oxfmt/binding-win32-arm64-msvc': 0.62.0 + '@oxfmt/binding-win32-ia32-msvc': 0.62.0 + '@oxfmt/binding-win32-x64-msvc': 0.62.0 + vite-plus: 0.2.9(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(yaml@2.9.0) + + oxlint-tsgolint@7.0.2001: + optionalDependencies: + '@oxlint-tsgolint/darwin-arm64': 7.0.2001 + '@oxlint-tsgolint/darwin-x64': 7.0.2001 + '@oxlint-tsgolint/linux-arm64': 7.0.2001 + '@oxlint-tsgolint/linux-x64': 7.0.2001 + '@oxlint-tsgolint/win32-arm64': 7.0.2001 + '@oxlint-tsgolint/win32-x64': 7.0.2001 + + oxlint@1.77.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.2.9(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(yaml@2.9.0)): + optionalDependencies: + '@oxlint/binding-android-arm-eabi': 1.77.0 + '@oxlint/binding-android-arm64': 1.77.0 + '@oxlint/binding-darwin-arm64': 1.77.0 + '@oxlint/binding-darwin-x64': 1.77.0 + '@oxlint/binding-freebsd-x64': 1.77.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.77.0 + '@oxlint/binding-linux-arm-musleabihf': 1.77.0 + '@oxlint/binding-linux-arm64-gnu': 1.77.0 + '@oxlint/binding-linux-arm64-musl': 1.77.0 + '@oxlint/binding-linux-ppc64-gnu': 1.77.0 + '@oxlint/binding-linux-riscv64-gnu': 1.77.0 + '@oxlint/binding-linux-riscv64-musl': 1.77.0 + '@oxlint/binding-linux-s390x-gnu': 1.77.0 + '@oxlint/binding-linux-x64-gnu': 1.77.0 + '@oxlint/binding-linux-x64-musl': 1.77.0 + '@oxlint/binding-openharmony-arm64': 1.77.0 + '@oxlint/binding-win32-arm64-msvc': 1.77.0 + '@oxlint/binding-win32-ia32-msvc': 1.77.0 + '@oxlint/binding-win32-x64-msvc': 1.77.0 + oxlint-tsgolint: 7.0.2001 + vite-plus: 0.2.9(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(yaml@2.9.0) + p-retry@4.6.2: dependencies: '@types/retry': 0.12.0 @@ -3818,12 +4985,20 @@ snapshots: platform@1.3.6: optional: true + pngjs@7.0.0: {} + postcss@8.5.15: dependencies: nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 + pretty-format@27.5.1: + dependencies: + ansi-regex: 5.0.1 + ansi-styles: 5.2.0 + react-is: 17.0.2 + proper-lockfile@4.1.2: dependencies: graceful-fs: 4.2.11 @@ -3874,6 +5049,8 @@ snapshots: react: 19.2.7 scheduler: 0.27.0 + react-is@17.0.2: {} + react@19.2.7: {} resolve-pkg-maps@1.0.0: {} @@ -4035,6 +5212,12 @@ snapshots: signal-exit@3.0.7: {} + sirv@3.0.2: + dependencies: + '@polka/url': 1.0.0-next.29 + mrmime: 2.0.1 + totalist: 3.0.1 + source-map-js@1.2.1: {} sprintf-js@1.1.3: @@ -4074,8 +5257,12 @@ snapshots: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 + tinypool@2.1.0: {} + tinyrainbow@3.1.0: {} + totalist@3.0.1: {} + ts-algebra@2.0.0: {} tslib@2.8.1: {} @@ -4101,9 +5288,67 @@ snapshots: undici@8.3.0: {} + vite-plus@0.2.9(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(yaml@2.9.0): + dependencies: + '@oxc-project/types': 0.143.0 + '@oxlint/plugins': 1.73.0 + '@vitest/browser': 4.1.10(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10) + '@vitest/browser-preview': 4.1.10(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10) + '@vitest/expect': 4.1.10 + '@vitest/mocker': 4.1.10(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.10 + '@vitest/runner': 4.1.10 + '@vitest/snapshot': 4.1.10 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 + '@voidzero-dev/vite-plus-core': 0.2.9(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(typescript@6.0.3)(yaml@2.9.0) + oxfmt: 0.62.0(vite-plus@0.2.9(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(yaml@2.9.0)) + oxlint: 1.77.0(oxlint-tsgolint@7.0.2001)(vite-plus@0.2.9(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(yaml@2.9.0)) + oxlint-tsgolint: 7.0.2001 + vitest: 4.1.10(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-preview@4.1.10)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + optionalDependencies: + '@voidzero-dev/vite-plus-darwin-arm64': 0.2.9 + '@voidzero-dev/vite-plus-darwin-x64': 0.2.9 + '@voidzero-dev/vite-plus-linux-arm64-gnu': 0.2.9 + '@voidzero-dev/vite-plus-linux-arm64-musl': 0.2.9 + '@voidzero-dev/vite-plus-linux-x64-gnu': 0.2.9 + '@voidzero-dev/vite-plus-linux-x64-musl': 0.2.9 + '@voidzero-dev/vite-plus-win32-arm64-msvc': 0.2.9 + '@voidzero-dev/vite-plus-win32-x64-msvc': 0.2.9 + transitivePeerDependencies: + - '@arethetypeswrong/core' + - '@edge-runtime/vm' + - '@opentelemetry/api' + - '@types/node' + - '@vitejs/devtools' + - '@vitest/coverage-istanbul' + - '@vitest/coverage-v8' + - '@vitest/ui' + - bufferutil + - esbuild + - happy-dom + - jiti + - jsdom + - less + - msw + - publint + - sass + - sass-embedded + - stylus + - sugarss + - svelte + - terser + - tsx + - typescript + - unplugin-unused + - unrun + - utf-8-validate + - vite + - yaml + vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0): dependencies: - lightningcss: 1.32.0 + lightningcss: 1.33.0 picomatch: 4.0.4 postcss: 8.5.15 rolldown: 1.0.2 @@ -4116,15 +5361,15 @@ snapshots: tsx: 4.22.4 yaml: 2.9.0 - vitest@4.1.7(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)): + vitest@4.1.10(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(@vitest/browser-preview@4.1.10)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)): dependencies: - '@vitest/expect': 4.1.7 - '@vitest/mocker': 4.1.7(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) - '@vitest/pretty-format': 4.1.7 - '@vitest/runner': 4.1.7 - '@vitest/snapshot': 4.1.7 - '@vitest/spy': 4.1.7 - '@vitest/utils': 4.1.7 + '@vitest/expect': 4.1.10 + '@vitest/mocker': 4.1.10(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0)) + '@vitest/pretty-format': 4.1.10 + '@vitest/runner': 4.1.10 + '@vitest/snapshot': 4.1.10 + '@vitest/spy': 4.1.10 + '@vitest/utils': 4.1.10 es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 @@ -4141,6 +5386,7 @@ snapshots: optionalDependencies: '@opentelemetry/api': 1.9.1 '@types/node': 25.9.1 + '@vitest/browser-preview': 4.1.10(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(vitest@4.1.10) transitivePeerDependencies: - msw @@ -4182,6 +5428,38 @@ snapshots: y18n: 5.0.8 yargs-parser: 22.0.0 + yuku-codegen@0.5.48: + dependencies: + '@yuku-toolchain/types': 0.5.43 + optionalDependencies: + '@yuku-codegen/binding-darwin-arm64': 0.5.48 + '@yuku-codegen/binding-darwin-x64': 0.5.48 + '@yuku-codegen/binding-freebsd-x64': 0.5.48 + '@yuku-codegen/binding-linux-arm-gnu': 0.5.48 + '@yuku-codegen/binding-linux-arm-musl': 0.5.48 + '@yuku-codegen/binding-linux-arm64-gnu': 0.5.48 + '@yuku-codegen/binding-linux-arm64-musl': 0.5.48 + '@yuku-codegen/binding-linux-x64-gnu': 0.5.48 + '@yuku-codegen/binding-linux-x64-musl': 0.5.48 + '@yuku-codegen/binding-win32-arm64': 0.5.48 + '@yuku-codegen/binding-win32-x64': 0.5.48 + + yuku-parser@0.5.48: + dependencies: + '@yuku-toolchain/types': 0.5.43 + optionalDependencies: + '@yuku-parser/binding-darwin-arm64': 0.5.48 + '@yuku-parser/binding-darwin-x64': 0.5.48 + '@yuku-parser/binding-freebsd-x64': 0.5.48 + '@yuku-parser/binding-linux-arm-gnu': 0.5.48 + '@yuku-parser/binding-linux-arm-musl': 0.5.48 + '@yuku-parser/binding-linux-arm64-gnu': 0.5.48 + '@yuku-parser/binding-linux-arm64-musl': 0.5.48 + '@yuku-parser/binding-linux-x64-gnu': 0.5.48 + '@yuku-parser/binding-linux-x64-musl': 0.5.48 + '@yuku-parser/binding-win32-arm64': 0.5.48 + '@yuku-parser/binding-win32-x64': 0.5.48 + zod-to-json-schema@3.25.2(zod@4.4.3): dependencies: zod: 4.4.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 924b55f..a126ccf 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -1,2 +1,6 @@ packages: - packages/* + +overrides: + vite: npm:@voidzero-dev/vite-plus-core@latest + vitest: 4.1.10 diff --git a/src/ciphers/adfgvx.ts b/src/ciphers/adfgvx.ts index d892b1e..983e6fa 100644 --- a/src/ciphers/adfgvx.ts +++ b/src/ciphers/adfgvx.ts @@ -13,16 +13,25 @@ function buildAdfgvxGrid(key?: string): { grid: string[][]; pos: Map= 'A' && c <= 'Z') || (c >= '0' && c <= '9')) { - if (!seen.has(c)) { seen.add(c); chars.push(c) } + if (!seen.has(c)) { + seen.add(c) + chars.push(c) + } } } for (let i = 65; i <= 90; i++) { const c = String.fromCharCode(i) - if (!seen.has(c)) { seen.add(c); chars.push(c) } + if (!seen.has(c)) { + seen.add(c) + chars.push(c) + } } for (let i = 48; i <= 57; i++) { const c = String.fromCharCode(i) - if (!seen.has(c)) { seen.add(c); chars.push(c) } + if (!seen.has(c)) { + seen.add(c) + chars.push(c) + } } const grid: string[][] = [] const pos = new Map() @@ -44,7 +53,7 @@ function encodeAdfgvx(text: string, key?: string): string { for (const c of normalized) { const p = pos.get(c) if (p) { - result += (ADFGVX_LETTERS[p[0]] ?? "") + (ADFGVX_LETTERS[p[1]] ?? "") + result += (ADFGVX_LETTERS[p[0]] ?? '') + (ADFGVX_LETTERS[p[1]] ?? '') } } return result @@ -63,17 +72,26 @@ function decodeAdfgvx(text: string, key?: string): string { } class Adfgvx extends Cipher { - name(): string { return 'adfgvx' } + name(): string { + return 'adfgvx' + } info(): CipherInfo { return { name: 'adfgvx', label: 'ADFGVX', - description: 'WWI fractionation cipher — 6×6 grid (letters+digits) with ADFGVX coordinate encoding', + description: + 'WWI fractionation cipher — 6×6 grid (letters+digits) with ADFGVX coordinate encoding', family: 'fractionation', selfInverse: false, options: [ - { name: 'key', type: 'string', required: false, default: '', description: 'Optional keyword for the 6×6 grid' }, + { + name: 'key', + type: 'string', + required: false, + default: '', + description: 'Optional keyword for the 6×6 grid', + }, ], keyspace: '36! (full grid) or keyed subset', } @@ -82,15 +100,29 @@ class Adfgvx extends Cipher { encode(text: string, options?: CipherBaseOptions): CipherResult { try { const key = getOpt(options ?? {}, 'key', '') - return { text: encodeAdfgvx(text, key), cipher: 'adfgvx', operation: 'encode', options: { key } } - } catch (e) { throw normalizeError(e, 'adfgvx') } + return { + text: encodeAdfgvx(text, key), + cipher: 'adfgvx', + operation: 'encode', + options: { key }, + } + } catch (e) { + throw normalizeError(e, 'adfgvx') + } } decode(text: string, options?: CipherBaseOptions): CipherResult { try { const key = getOpt(options ?? {}, 'key', '') - return { text: decodeAdfgvx(text, key), cipher: 'adfgvx', operation: 'decode', options: { key } } - } catch (e) { throw normalizeError(e, 'adfgvx') } + return { + text: decodeAdfgvx(text, key), + cipher: 'adfgvx', + operation: 'decode', + options: { key }, + } + } catch (e) { + throw normalizeError(e, 'adfgvx') + } } } diff --git a/src/ciphers/affine.ts b/src/ciphers/affine.ts index b6f7679..fb9c16c 100644 --- a/src/ciphers/affine.ts +++ b/src/ciphers/affine.ts @@ -4,7 +4,9 @@ import { getOpt, processBaseOptions } from '../core/utils' import { InvalidOptionError, normalizeError } from '../core/errors' import { register } from '../core/registry' -function gcd(a: number, b: number): number { return b === 0 ? a : gcd(b, a % b) } +function gcd(a: number, b: number): number { + return b === 0 ? a : gcd(b, a % b) +} function modInverse(a: number, m: number): number { let [old_r, r] = [a % m, m] @@ -17,7 +19,14 @@ function modInverse(a: number, m: number): number { return ((old_s % m) + m) % m } -function affineProcess(text: string, a: number, b: number, decrypt: boolean, preserveCase: boolean, stripNonAlpha: boolean): string { +function affineProcess( + text: string, + a: number, + b: number, + decrypt: boolean, + preserveCase: boolean, + stripNonAlpha: boolean, +): string { let input = text if (stripNonAlpha) input = input.replace(/[^A-Za-z]/g, '') const aInv = decrypt ? modInverse(a, 26) : a @@ -27,26 +36,34 @@ function affineProcess(text: string, a: number, b: number, decrypt: boolean, pre if (!isUpper && !isLower) return c const upper = isUpper ? c : c.toUpperCase() const x = upper.charCodeAt(0) - 65 - const result = decrypt - ? (aInv * ((x - b + 26) % 26)) % 26 - : (a * x + b) % 26 - const code = String.fromCharCode(((result % 26) + 26) % 26 + 65) + const result = decrypt ? (aInv * ((x - b + 26) % 26)) % 26 : (a * x + b) % 26 + const code = String.fromCharCode((((result % 26) + 26) % 26) + 65) return preserveCase && isLower ? code.toLowerCase() : code }).join('') } -function validate(opts: CipherBaseOptions): { a: number; b: number; preserveCase: boolean; stripNonAlpha: boolean } { - const a = getOpt(opts, "a", 5) - const b = getOpt(opts, "b", 8) - if (!Number.isInteger(a) || a < 1 || a > 25) throw new InvalidOptionError('a', a, 'must be integer 1-25') - if (!Number.isInteger(b) || b < 0 || b > 25) throw new InvalidOptionError('b', b, 'must be integer 0-25') - if (gcd(a, 26) !== 1) throw new InvalidOptionError('a', a, 'must be coprime with 26 (gcd(a,26)=1)') +function validate(opts: CipherBaseOptions): { + a: number + b: number + preserveCase: boolean + stripNonAlpha: boolean +} { + const a = getOpt(opts, 'a', 5) + const b = getOpt(opts, 'b', 8) + if (!Number.isInteger(a) || a < 1 || a > 25) + throw new InvalidOptionError('a', a, 'must be integer 1-25') + if (!Number.isInteger(b) || b < 0 || b > 25) + throw new InvalidOptionError('b', b, 'must be integer 0-25') + if (gcd(a, 26) !== 1) + throw new InvalidOptionError('a', a, 'must be coprime with 26 (gcd(a,26)=1)') const base = processBaseOptions(opts) return { a, b, ...base } } class Affine extends Cipher { - name(): string { return 'affine' } + name(): string { + return 'affine' + } info(): CipherInfo { return { @@ -56,8 +73,20 @@ class Affine extends Cipher { family: 'substitution-multiplicative', selfInverse: false, options: [ - { name: 'a', type: 'number', required: false, default: 5, description: 'Multiplier (must be coprime with 26: 1,3,5,7,9,11,15,17,19,21,23,25)' }, - { name: 'b', type: 'number', required: false, default: 8, description: 'Additive shift (0-25)' }, + { + name: 'a', + type: 'number', + required: false, + default: 5, + description: 'Multiplier (must be coprime with 26: 1,3,5,7,9,11,15,17,19,21,23,25)', + }, + { + name: 'b', + type: 'number', + required: false, + default: 8, + description: 'Additive shift (0-25)', + }, ], keyspace: '12 × 26 = 312 (12 valid multipliers × 26 shifts)', } @@ -66,15 +95,29 @@ class Affine extends Cipher { encode(text: string, options?: CipherBaseOptions): CipherResult { try { const { a, b, preserveCase, stripNonAlpha } = validate(options ?? {}) - return { text: affineProcess(text, a, b, false, preserveCase, stripNonAlpha), cipher: 'affine', operation: 'encode', options: { a, b } } - } catch (e) { throw normalizeError(e, 'affine') } + return { + text: affineProcess(text, a, b, false, preserveCase, stripNonAlpha), + cipher: 'affine', + operation: 'encode', + options: { a, b }, + } + } catch (e) { + throw normalizeError(e, 'affine') + } } decode(text: string, options?: CipherBaseOptions): CipherResult { try { const { a, b, preserveCase, stripNonAlpha } = validate(options ?? {}) - return { text: affineProcess(text, a, b, true, preserveCase, stripNonAlpha), cipher: 'affine', operation: 'decode', options: { a, b } } - } catch (e) { throw normalizeError(e, 'affine') } + return { + text: affineProcess(text, a, b, true, preserveCase, stripNonAlpha), + cipher: 'affine', + operation: 'decode', + options: { a, b }, + } + } catch (e) { + throw normalizeError(e, 'affine') + } } } diff --git a/src/ciphers/alberti.ts b/src/ciphers/alberti.ts index 303d75d..4de9bfa 100644 --- a/src/ciphers/alberti.ts +++ b/src/ciphers/alberti.ts @@ -6,7 +6,12 @@ import { getOpt, processBaseOptions } from '../core/utils' const ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' -function parseOptions(options: CipherBaseOptions): { key: string; period: number; preserveCase: boolean; stripNonAlpha: boolean } { +function parseOptions(options: CipherBaseOptions): { + key: string + period: number + preserveCase: boolean + stripNonAlpha: boolean +} { const rawKey = getOpt(options, 'key', undefined) if (rawKey === undefined || rawKey === '') throw new MissingOptionError('key') if (typeof rawKey !== 'string' || !/^[A-Za-z]+$/.test(rawKey)) { @@ -23,8 +28,16 @@ function parseOptions(options: CipherBaseOptions): { key: string; period: number return { key, period, ...processBaseOptions(options) } } -function transform(text: string, key: string, period: number, decrypt: boolean, preserveCase: boolean, stripNonAlpha: boolean): string { - const innerDisk = key + Array.from(ALPHABET, letter => key.includes(letter) ? '' : letter).join('') +function transform( + text: string, + key: string, + period: number, + decrypt: boolean, + preserveCase: boolean, + stripNonAlpha: boolean, +): string { + const innerDisk = + key + Array.from(ALPHABET, (letter) => (key.includes(letter) ? '' : letter)).join('') const input = stripNonAlpha ? text.replace(/[^A-Za-z]/g, '') : text let position = 0 @@ -45,18 +58,31 @@ function transform(text: string, key: string, period: number, decrypt: boolean, } class Alberti extends Cipher { - name(): string { return 'alberti' } + name(): string { + return 'alberti' + } info(): CipherInfo { return { name: 'alberti', label: 'Alberti', - description: 'Simplified Alberti disk cipher with a keyed inner alphabet rotated at a fixed period', + description: + 'Simplified Alberti disk cipher with a keyed inner alphabet rotated at a fixed period', family: 'polyalphabetic', selfInverse: false, options: [ - { name: 'key', type: 'string', required: true, description: 'Keyword used to construct the movable inner disk' }, - { name: 'period', type: 'number', required: true, description: 'Letters processed before rotating the inner disk' }, + { + name: 'key', + type: 'string', + required: true, + description: 'Keyword used to construct the movable inner disk', + }, + { + name: 'period', + type: 'number', + required: true, + description: 'Letters processed before rotating the inner disk', + }, ], } } @@ -70,7 +96,9 @@ class Alberti extends Cipher { operation: 'encode', options: { key, period, preserveCase, stripNonAlpha }, } - } catch (error) { throw normalizeError(error, 'alberti') } + } catch (error) { + throw normalizeError(error, 'alberti') + } } decode(text: string, options?: CipherBaseOptions): CipherResult { @@ -82,7 +110,9 @@ class Alberti extends Cipher { operation: 'decode', options: { key, period, preserveCase, stripNonAlpha }, } - } catch (error) { throw normalizeError(error, 'alberti') } + } catch (error) { + throw normalizeError(error, 'alberti') + } } } diff --git a/src/ciphers/atbash.ts b/src/ciphers/atbash.ts index 0c7a113..0845125 100644 --- a/src/ciphers/atbash.ts +++ b/src/ciphers/atbash.ts @@ -13,7 +13,9 @@ function atbash(text: string, stripNonAlpha: boolean): string { } class Atbash extends Cipher { - name(): string { return 'atbash' } + name(): string { + return 'atbash' + } info(): CipherInfo { return { @@ -28,11 +30,21 @@ class Atbash extends Cipher { } encode(text: string, options?: CipherBaseOptions): CipherResult { - return { text: atbash(text, options?.stripNonAlpha ?? false), cipher: 'atbash', operation: 'encode', options: {} } + return { + text: atbash(text, options?.stripNonAlpha ?? false), + cipher: 'atbash', + operation: 'encode', + options: {}, + } } decode(text: string, options?: CipherBaseOptions): CipherResult { - return { text: atbash(text, options?.stripNonAlpha ?? false), cipher: 'atbash', operation: 'decode', options: {} } + return { + text: atbash(text, options?.stripNonAlpha ?? false), + cipher: 'atbash', + operation: 'decode', + options: {}, + } } } diff --git a/src/ciphers/bacon.ts b/src/ciphers/bacon.ts index e0b1be4..bfb3fd3 100644 --- a/src/ciphers/bacon.ts +++ b/src/ciphers/bacon.ts @@ -40,7 +40,9 @@ function decodeBacon(text: string): string { } class Bacon extends Cipher { - name(): string { return 'bacon' } + name(): string { + return 'bacon' + } info(): CipherInfo { return { @@ -56,14 +58,23 @@ class Bacon extends Cipher { encode(text: string, options?: CipherBaseOptions): CipherResult { try { - return { text: encodeBacon(text, options?.stripNonAlpha ?? false), cipher: 'bacon', operation: 'encode', options: {} } - } catch (e) { throw normalizeError(e, 'bacon') } + return { + text: encodeBacon(text, options?.stripNonAlpha ?? false), + cipher: 'bacon', + operation: 'encode', + options: {}, + } + } catch (e) { + throw normalizeError(e, 'bacon') + } } decode(text: string, _options?: CipherBaseOptions): CipherResult { try { return { text: decodeBacon(text), cipher: 'bacon', operation: 'decode', options: {} } - } catch (e) { throw normalizeError(e, 'bacon') } + } catch (e) { + throw normalizeError(e, 'bacon') + } } } diff --git a/src/ciphers/bifid.ts b/src/ciphers/bifid.ts index 7cf53d0..36ba62d 100644 --- a/src/ciphers/bifid.ts +++ b/src/ciphers/bifid.ts @@ -10,7 +10,10 @@ import { buildPolybiusSquare, getOpt } from '../core/utils' function encodeBifid(text: string, key: string, period: number): string { const { pos, square } = buildPolybiusSquare(key) - const clean = text.toUpperCase().replace(/[^A-Z]/g, '').replace(/J/g, 'I') + const clean = text + .toUpperCase() + .replace(/[^A-Z]/g, '') + .replace(/J/g, 'I') let result = '' for (let i = 0; i < clean.length; i += period) { const chunk = clean.slice(i, i + period) @@ -18,7 +21,10 @@ function encodeBifid(text: string, key: string, period: number): string { const cols: number[] = [] for (const c of chunk) { const p = pos.get(c) - if (p) { rows.push(p[0]); cols.push(p[1]) } + if (p) { + rows.push(p[0]) + cols.push(p[1]) + } } const combined = [...rows, ...cols] for (let j = 0; j + 1 < combined.length; j += 2) { @@ -32,14 +38,19 @@ function encodeBifid(text: string, key: string, period: number): string { function decodeBifid(text: string, key: string, period: number): string { const { pos, square } = buildPolybiusSquare(key) - const clean = text.toUpperCase().replace(/[^A-Z]/g, '').replace(/J/g, 'I') + const clean = text + .toUpperCase() + .replace(/[^A-Z]/g, '') + .replace(/J/g, 'I') let result = '' for (let i = 0; i < clean.length; i += period) { const chunk = clean.slice(i, i + period) const coords: number[] = [] for (const c of chunk) { const p = pos.get(c) - if (p) { coords.push(p[0], p[1]) } + if (p) { + coords.push(p[0], p[1]) + } } const half = coords.length / 2 for (let j = 0; j < half; j++) { @@ -61,7 +72,9 @@ function validate(opts: CipherBaseOptions): { key: string; period: number } { } class Bifid extends Cipher { - name(): string { return 'bifid' } + name(): string { + return 'bifid' + } info(): CipherInfo { return { @@ -71,8 +84,20 @@ class Bifid extends Cipher { family: 'fractionation', selfInverse: false, options: [ - { name: 'key', type: 'string', required: false, default: '', description: 'Optional keyword for Polybius square' }, - { name: 'period', type: 'number', required: false, default: 5, description: 'Transposition period length' }, + { + name: 'key', + type: 'string', + required: false, + default: '', + description: 'Optional keyword for Polybius square', + }, + { + name: 'period', + type: 'number', + required: false, + default: 5, + description: 'Transposition period length', + }, ], keyspace: '25! × period variants', } @@ -81,15 +106,29 @@ class Bifid extends Cipher { encode(text: string, options?: CipherBaseOptions): CipherResult { try { const { key, period } = validate(options ?? {}) - return { text: encodeBifid(text, key, period), cipher: 'bifid', operation: 'encode', options: { key, period } } - } catch (e) { throw normalizeError(e, 'bifid') } + return { + text: encodeBifid(text, key, period), + cipher: 'bifid', + operation: 'encode', + options: { key, period }, + } + } catch (e) { + throw normalizeError(e, 'bifid') + } } decode(text: string, options?: CipherBaseOptions): CipherResult { try { const { key, period } = validate(options ?? {}) - return { text: decodeBifid(text, key, period), cipher: 'bifid', operation: 'decode', options: { key, period } } - } catch (e) { throw normalizeError(e, 'bifid') } + return { + text: decodeBifid(text, key, period), + cipher: 'bifid', + operation: 'decode', + options: { key, period }, + } + } catch (e) { + throw normalizeError(e, 'bifid') + } } } diff --git a/src/ciphers/caesar.ts b/src/ciphers/caesar.ts index 8a6f663..e6219a2 100644 --- a/src/ciphers/caesar.ts +++ b/src/ciphers/caesar.ts @@ -4,7 +4,12 @@ import { InvalidOptionError, normalizeError } from '../core/errors' import { getOpt, processBaseOptions } from '../core/utils' import { register } from '../core/registry' -function caesarProcess(text: string, shift: number, preserveCase: boolean, stripNonAlpha: boolean): string { +function caesarProcess( + text: string, + shift: number, + preserveCase: boolean, + stripNonAlpha: boolean, +): string { let input = text if (stripNonAlpha) input = input.replace(/[^A-Za-z]/g, '') return Array.from(input, (c) => { @@ -13,13 +18,17 @@ function caesarProcess(text: string, shift: number, preserveCase: boolean, strip if (!isUpper && !isLower) return c const upper = isUpper ? c : c.toUpperCase() const x = upper.charCodeAt(0) - 65 - const code = ((x + shift) % 26 + 26) % 26 + const code = (((x + shift) % 26) + 26) % 26 const result = String.fromCharCode(code + 65) return preserveCase && isLower ? result.toLowerCase() : result }).join('') } -function validate(opts: CipherBaseOptions): { shift: number; preserveCase: boolean; stripNonAlpha: boolean } { +function validate(opts: CipherBaseOptions): { + shift: number + preserveCase: boolean + stripNonAlpha: boolean +} { const shift = getOpt(opts, 'shift', 3) if (!Number.isInteger(shift) || shift < 1 || shift > 25) { throw new InvalidOptionError('shift', shift, 'must be integer 1-25') @@ -29,7 +38,9 @@ function validate(opts: CipherBaseOptions): { shift: number; preserveCase: boole } class Caesar extends Cipher { - name(): string { return 'caesar' } + name(): string { + return 'caesar' + } info(): CipherInfo { return { @@ -39,7 +50,13 @@ class Caesar extends Cipher { family: 'substitution-shift', selfInverse: false, options: [ - { name: 'shift', type: 'number', required: false, default: 3, description: 'Number of positions to shift (1-25)' }, + { + name: 'shift', + type: 'number', + required: false, + default: 3, + description: 'Number of positions to shift (1-25)', + }, ], keyspace: '25 (shift 0 is identity)', } @@ -48,15 +65,29 @@ class Caesar extends Cipher { encode(text: string, options?: CipherBaseOptions): CipherResult { try { const { shift, preserveCase, stripNonAlpha } = validate(options ?? {}) - return { text: caesarProcess(text, shift, preserveCase, stripNonAlpha), cipher: 'caesar', operation: 'encode', options: { shift, preserveCase, stripNonAlpha } } - } catch (e) { throw normalizeError(e, 'caesar') } + return { + text: caesarProcess(text, shift, preserveCase, stripNonAlpha), + cipher: 'caesar', + operation: 'encode', + options: { shift, preserveCase, stripNonAlpha }, + } + } catch (e) { + throw normalizeError(e, 'caesar') + } } decode(text: string, options?: CipherBaseOptions): CipherResult { try { const { shift, preserveCase, stripNonAlpha } = validate(options ?? {}) - return { text: caesarProcess(text, 26 - shift, preserveCase, stripNonAlpha), cipher: 'caesar', operation: 'decode', options: { shift, preserveCase, stripNonAlpha } } - } catch (e) { throw normalizeError(e, 'caesar') } + return { + text: caesarProcess(text, 26 - shift, preserveCase, stripNonAlpha), + cipher: 'caesar', + operation: 'decode', + options: { shift, preserveCase, stripNonAlpha }, + } + } catch (e) { + throw normalizeError(e, 'caesar') + } } } diff --git a/src/ciphers/columnar.ts b/src/ciphers/columnar.ts index 3f3866a..92d9ce9 100644 --- a/src/ciphers/columnar.ts +++ b/src/ciphers/columnar.ts @@ -10,7 +10,7 @@ function buildColumnOrder(key: string): number[] { const upper = key.toUpperCase() const indexed = Array.from(upper, (c, i) => ({ char: c, idx: i })) const sorted = [...indexed].sort((a, b) => a.char.localeCompare(b.char) || a.idx - b.idx) - const order = new Array(sorted.length) + const order = Array.from({ length: sorted.length }, () => 0) for (let i = 0; i < sorted.length; i++) order[sorted[i]!.idx] = i return order } @@ -43,7 +43,7 @@ function decodeColumnar(text: string, key: string): string { const fullLen = rows * cols const padCount = fullLen - chars.length const colOrder = order.map((_, i) => order.indexOf(i)) - const colLens = new Array(cols).fill(rows) + const colLens = Array.from({ length: cols }, () => rows) for (let i = cols - padCount; i < cols; i++) { colLens[colOrder[i]!]!-- } @@ -65,7 +65,7 @@ function decodeColumnar(text: string, key: string): string { } function validate(opts: CipherBaseOptions): { key: string } { - const key = getOpt(opts, "key", undefined) + const key = getOpt(opts, 'key', undefined) if (!key) throw new MissingOptionError('key') return { key } } @@ -85,7 +85,9 @@ class Columnar extends Cipher { this.limiter = new RateLimiter(DEFAULT_RATE_LIMIT) } - name(): string { return 'columnar' } + name(): string { + return 'columnar' + } info(): CipherInfo { return { @@ -95,7 +97,12 @@ class Columnar extends Cipher { family: 'transposition', selfInverse: false, options: [ - { name: 'key', type: 'string', required: true, description: 'Keyword determining column order' }, + { + name: 'key', + type: 'string', + required: true, + description: 'Keyword determining column order', + }, ], keyspace: 'n! (column permutations)', } @@ -113,7 +120,9 @@ class Columnar extends Cipher { const result = encodeColumnar(text, key) this.cache.set(ck, result) return { text: result, cipher: 'columnar', operation: 'encode', options: { key } } - } catch (e) { throw normalizeError(e, 'columnar') } + } catch (e) { + throw normalizeError(e, 'columnar') + } } decode(text: string, options?: CipherBaseOptions): CipherResult { @@ -128,7 +137,9 @@ class Columnar extends Cipher { const result = decodeColumnar(text, key) this.cache.set(ck, result) return { text: result, cipher: 'columnar', operation: 'decode', options: { key } } - } catch (e) { throw normalizeError(e, 'columnar') } + } catch (e) { + throw normalizeError(e, 'columnar') + } } /** Reset cache and rate limiter. Useful for testing. */ diff --git a/src/ciphers/enigma.ts b/src/ciphers/enigma.ts index 6a3b543..b60c637 100644 --- a/src/ciphers/enigma.ts +++ b/src/ciphers/enigma.ts @@ -19,7 +19,8 @@ function stringOption(options: CipherBaseOptions, name: string, fallback: string } function parseLetters(value: string, name: string): { normalized: string; values: number[] } { - if (!/^[A-Za-z]{3}$/.test(value)) throw new InvalidOptionError(name, value, 'must be exactly three letters A-Z') + if (!/^[A-Za-z]{3}$/.test(value)) + throw new InvalidOptionError(name, value, 'must be exactly three letters A-Z') const normalized = value.toUpperCase() return { normalized, values: Array.from(normalized, (letter) => letter.charCodeAt(0) - 65) } } @@ -31,7 +32,11 @@ function parsePlugboard(value: string): { normalized: string; wiring: number[] } const used = new Set() for (const rawPair of value.split(/\s+/)) { if (!/^[A-Za-z]{2}$/.test(rawPair) || rawPair[0]!.toUpperCase() === rawPair[1]!.toUpperCase()) { - throw new InvalidOptionError('plugboard', value, 'must contain distinct letter pairs such as "AB CD"') + throw new InvalidOptionError( + 'plugboard', + value, + 'must contain distinct letter pairs such as "AB CD"', + ) } const pair = rawPair.toUpperCase() const [a, b] = pair @@ -67,7 +72,11 @@ function parseConfig(options: CipherBaseOptions): EnigmaConfig { plugboard: plugboard.wiring, preserveCase: options.preserveCase ?? true, stripNonAlpha: options.stripNonAlpha ?? false, - resultOptions: { positions: positions.normalized, rings: rings.normalized, plugboard: plugboard.normalized }, + resultOptions: { + positions: positions.normalized, + rings: rings.normalized, + plugboard: plugboard.normalized, + }, } } @@ -75,7 +84,13 @@ function mod26(value: number): number { return ((value % 26) + 26) % 26 } -function throughRotor(value: number, rotorIndex: number, position: number, ring: number, reverse: boolean): number { +function throughRotor( + value: number, + rotorIndex: number, + position: number, + ring: number, + reverse: boolean, +): number { const wiring = ROTORS[rotorIndex]!.wiring const shifted = mod26(value + position - ring) const wired = reverse ? wiring.indexOf(ALPHABET[shifted]!) : wiring.charCodeAt(shifted) - 65 @@ -103,9 +118,11 @@ function transform(text: string, config: EnigmaConfig): string { positions[2] = mod26(positions[2]! + 1) let value = plugboard[input]! - for (let rotor = 2; rotor >= 0; rotor--) value = throughRotor(value, rotor, positions[rotor]!, rings[rotor]!, false) + for (let rotor = 2; rotor >= 0; rotor--) + value = throughRotor(value, rotor, positions[rotor]!, rings[rotor]!, false) value = REFLECTOR_B.charCodeAt(value) - 65 - for (let rotor = 0; rotor < 3; rotor++) value = throughRotor(value, rotor, positions[rotor]!, rings[rotor]!, true) + for (let rotor = 0; rotor < 3; rotor++) + value = throughRotor(value, rotor, positions[rotor]!, rings[rotor]!, true) value = plugboard[value]! const encoded = ALPHABET[value]! @@ -116,7 +133,9 @@ function transform(text: string, config: EnigmaConfig): string { } class Enigma extends Cipher { - name(): string { return 'enigma' } + name(): string { + return 'enigma' + } info(): CipherInfo { return { @@ -126,9 +145,27 @@ class Enigma extends Cipher { family: 'rotor', selfInverse: true, options: [ - { name: 'positions', type: 'string', required: false, default: 'AAA', description: 'Initial rotor positions, left to right' }, - { name: 'rings', type: 'string', required: false, default: 'AAA', description: 'Ring settings, left to right' }, - { name: 'plugboard', type: 'string', required: false, default: '', description: 'Space-separated letter pairs, for example "AV BS CG"' }, + { + name: 'positions', + type: 'string', + required: false, + default: 'AAA', + description: 'Initial rotor positions, left to right', + }, + { + name: 'rings', + type: 'string', + required: false, + default: 'AAA', + description: 'Ring settings, left to right', + }, + { + name: 'plugboard', + type: 'string', + required: false, + default: '', + description: 'Space-separated letter pairs, for example "AV BS CG"', + }, ], keyspace: '26^6 × plugboard configurations', } @@ -137,15 +174,29 @@ class Enigma extends Cipher { encode(text: string, options?: CipherBaseOptions): CipherResult { try { const config = parseConfig(options ?? {}) - return { text: transform(text, config), cipher: 'enigma', operation: 'encode', options: config.resultOptions } - } catch (error) { throw normalizeError(error, 'enigma') } + return { + text: transform(text, config), + cipher: 'enigma', + operation: 'encode', + options: config.resultOptions, + } + } catch (error) { + throw normalizeError(error, 'enigma') + } } decode(text: string, options?: CipherBaseOptions): CipherResult { try { const config = parseConfig(options ?? {}) - return { text: transform(text, config), cipher: 'enigma', operation: 'decode', options: config.resultOptions } - } catch (error) { throw normalizeError(error, 'enigma') } + return { + text: transform(text, config), + cipher: 'enigma', + operation: 'decode', + options: config.resultOptions, + } + } catch (error) { + throw normalizeError(error, 'enigma') + } } } diff --git a/src/ciphers/morse.ts b/src/ciphers/morse.ts index a8e0b46..09694bd 100644 --- a/src/ciphers/morse.ts +++ b/src/ciphers/morse.ts @@ -4,15 +4,60 @@ import { normalizeError } from '../core/errors' import { register } from '../core/registry' const CHAR_TO_MORSE: Record = { - A: '.-', B: '-...', C: '-.-.', D: '-..', E: '.', F: '..-.', G: '--.', H: '....', - I: '..', J: '.---', K: '-.-', L: '.-..', M: '--', N: '-.', O: '---', P: '.--.', - Q: '--.-', R: '.-.', S: '...', T: '-', U: '..-', V: '...-', W: '.--', X: '-..-', - Y: '-.--', Z: '--..', '0': '-----', '1': '.----', '2': '..---', '3': '...--', - '4': '....-', '5': '.....', '6': '-....', '7': '--...', '8': '---..', '9': '----.', - '.': '.-.-.-', ',': '--..--', '?': '..--..', '!': '-.-.--', '/': '-..-.', - '(': '-.--.', ')': '-.--.-', '&': '.-...', ':': '---...', ';': '-.-.-.', - '=': '-...-', '+': '.-.-.', '-': '-....-', '_': '..--.-', '"': '.-..-.', - '$': '...-..-', '@': '.--.-.', "'": '.----.', + A: '.-', + B: '-...', + C: '-.-.', + D: '-..', + E: '.', + F: '..-.', + G: '--.', + H: '....', + I: '..', + J: '.---', + K: '-.-', + L: '.-..', + M: '--', + N: '-.', + O: '---', + P: '.--.', + Q: '--.-', + R: '.-.', + S: '...', + T: '-', + U: '..-', + V: '...-', + W: '.--', + X: '-..-', + Y: '-.--', + Z: '--..', + '0': '-----', + '1': '.----', + '2': '..---', + '3': '...--', + '4': '....-', + '5': '.....', + '6': '-....', + '7': '--...', + '8': '---..', + '9': '----.', + '.': '.-.-.-', + ',': '--..--', + '?': '..--..', + '!': '-.-.--', + '/': '-..-.', + '(': '-.--.', + ')': '-.--.-', + '&': '.-...', + ':': '---...', + ';': '-.-.-.', + '=': '-...-', + '+': '.-.-.', + '-': '-....-', + _: '..--.-', + '"': '.-..-.', + $: '...-..-', + '@': '.--.-.', + "'": '.----.', } const MORSE_TO_CHAR: Record = Object.fromEntries( @@ -28,13 +73,20 @@ function encodeMorse(text: string): string { function decodeMorse(text: string): string { const words = text.trim().split(/\s*\/\s*/) - return words.map((word) => - word.split(/\s+/).map((code) => MORSE_TO_CHAR[code] ?? code).join('') - ).join(' ') + return words + .map((word) => + word + .split(/\s+/) + .map((code) => MORSE_TO_CHAR[code] ?? code) + .join(''), + ) + .join(' ') } class Morse extends Cipher { - name(): string { return 'morse' } + name(): string { + return 'morse' + } info(): CipherInfo { return { @@ -51,13 +103,17 @@ class Morse extends Cipher { encode(text: string, _options?: CipherBaseOptions): CipherResult { try { return { text: encodeMorse(text), cipher: 'morse', operation: 'encode', options: {} } - } catch (e) { throw normalizeError(e, 'morse') } + } catch (e) { + throw normalizeError(e, 'morse') + } } decode(text: string, _options?: CipherBaseOptions): CipherResult { try { return { text: decodeMorse(text), cipher: 'morse', operation: 'decode', options: {} } - } catch (e) { throw normalizeError(e, 'morse') } + } catch (e) { + throw normalizeError(e, 'morse') + } } } diff --git a/src/ciphers/playfair.ts b/src/ciphers/playfair.ts index 23531e9..23289d2 100644 --- a/src/ciphers/playfair.ts +++ b/src/ciphers/playfair.ts @@ -7,7 +7,10 @@ import { register } from '../core/registry' // Uses shared buildPolybiusSquare (1-indexed positions) function prepareText(text: string): string[] { - const clean = text.toUpperCase().replace(/[^A-Z]/g, '').replace(/J/g, 'I') + const clean = text + .toUpperCase() + .replace(/[^A-Z]/g, '') + .replace(/J/g, 'I') const bigrams: string[] = [] let i = 0 while (i < clean.length) { @@ -28,7 +31,10 @@ function prepareText(text: string): string[] { } function pairCiphertext(text: string): string[] { - const clean = text.toUpperCase().replace(/[^A-Z]/g, '').replace(/J/g, 'I') + const clean = text + .toUpperCase() + .replace(/[^A-Z]/g, '') + .replace(/J/g, 'I') const bigrams: string[] = [] for (let i = 0; i < clean.length; i += 2) { bigrams.push(clean[i]! + (clean[i + 1] ?? 'X')) @@ -61,23 +67,31 @@ function processPlayfair(text: string, key: string, decrypt: boolean): string { } function validate(opts: CipherBaseOptions): { key: string } { - const key = getOpt(opts, "key", undefined) + const key = getOpt(opts, 'key', undefined) if (!key) throw new MissingOptionError('key') return { key } } class Playfair extends Cipher { - name(): string { return 'playfair' } + name(): string { + return 'playfair' + } info(): CipherInfo { return { name: 'playfair', label: 'Playfair', - description: 'Digraph substitution — encrypts letter pairs using a 5×5 key table (I/J share cell)', + description: + 'Digraph substitution — encrypts letter pairs using a 5×5 key table (I/J share cell)', family: 'digraph', selfInverse: false, options: [ - { name: 'key', type: 'string', required: true, description: 'Keyword for the 5×5 table (letters only)' }, + { + name: 'key', + type: 'string', + required: true, + description: 'Keyword for the 5×5 table (letters only)', + }, ], keyspace: '25! ≈ 1.5×10²⁵', } @@ -86,15 +100,29 @@ class Playfair extends Cipher { encode(text: string, options?: CipherBaseOptions): CipherResult { try { const { key } = validate(options ?? {}) - return { text: processPlayfair(text, key, false), cipher: 'playfair', operation: 'encode', options: { key } } - } catch (e) { throw normalizeError(e, 'playfair') } + return { + text: processPlayfair(text, key, false), + cipher: 'playfair', + operation: 'encode', + options: { key }, + } + } catch (e) { + throw normalizeError(e, 'playfair') + } } decode(text: string, options?: CipherBaseOptions): CipherResult { try { const { key } = validate(options ?? {}) - return { text: processPlayfair(text, key, true), cipher: 'playfair', operation: 'decode', options: { key } } - } catch (e) { throw normalizeError(e, 'playfair') } + return { + text: processPlayfair(text, key, true), + cipher: 'playfair', + operation: 'decode', + options: { key }, + } + } catch (e) { + throw normalizeError(e, 'playfair') + } } } diff --git a/src/ciphers/polybius.ts b/src/ciphers/polybius.ts index c06bded..349520d 100644 --- a/src/ciphers/polybius.ts +++ b/src/ciphers/polybius.ts @@ -5,7 +5,9 @@ import { register } from '../core/registry' import { buildPolybiusSquare, getOpt } from '../core/utils' class Polybius extends Cipher { - name(): string { return 'polybius' } + name(): string { + return 'polybius' + } info(): CipherInfo { return { @@ -15,7 +17,13 @@ class Polybius extends Cipher { family: 'fractionation', selfInverse: false, options: [ - { name: 'key', type: 'string', required: false, default: '', description: 'Optional keyword for the 5×5 table' }, + { + name: 'key', + type: 'string', + required: false, + default: '', + description: 'Optional keyword for the 5×5 table', + }, ], keyspace: '1 (standard) or 25! (keyed)', } @@ -34,7 +42,9 @@ class Polybius extends Cipher { } } return { text: encoded.join(' '), cipher: 'polybius', operation: 'encode', options: { key } } - } catch (e) { throw normalizeError(e, 'polybius') } + } catch (e) { + throw normalizeError(e, 'polybius') + } } decode(text: string, options?: CipherBaseOptions): CipherResult { @@ -57,7 +67,9 @@ class Polybius extends Cipher { } } return { text: result, cipher: 'polybius', operation: 'decode', options: { key } } - } catch (e) { throw normalizeError(e, 'polybius') } + } catch (e) { + throw normalizeError(e, 'polybius') + } } } diff --git a/src/ciphers/rail-fence.ts b/src/ciphers/rail-fence.ts index 1d9f060..d4481b9 100644 --- a/src/ciphers/rail-fence.ts +++ b/src/ciphers/rail-fence.ts @@ -34,7 +34,7 @@ function decodeRailFence(cipher: string, rails: number): string { } const railPositions: number[][] = Array.from({ length: rails }, () => []) for (let i = 0; i < n; i++) railPositions[pattern[i]!]!.push(i) - const result: string[] = new Array(n) + const result = Array.from({ length: n }, () => '') let idx = 0 for (let r = 0; r < rails; r++) { for (const pos of railPositions[r]!) { @@ -45,13 +45,16 @@ function decodeRailFence(cipher: string, rails: number): string { } function validate(opts: CipherBaseOptions): { rails: number } { - const rails = getOpt(opts, "rails", 3) - if (!Number.isInteger(rails) || rails < 2) throw new InvalidOptionError('rails', rails, 'must be integer >= 2') + const rails = getOpt(opts, 'rails', 3) + if (!Number.isInteger(rails) || rails < 2) + throw new InvalidOptionError('rails', rails, 'must be integer >= 2') return { rails } } class RailFence extends Cipher { - name(): string { return 'rail-fence' } + name(): string { + return 'rail-fence' + } info(): CipherInfo { return { @@ -61,7 +64,13 @@ class RailFence extends Cipher { family: 'transposition', selfInverse: false, options: [ - { name: 'rails', type: 'number', required: false, default: 3, description: 'Number of rails (2 or more)' }, + { + name: 'rails', + type: 'number', + required: false, + default: 3, + description: 'Number of rails (2 or more)', + }, ], keyspace: '~n (number of rails)', } @@ -70,15 +79,29 @@ class RailFence extends Cipher { encode(text: string, options?: CipherBaseOptions): CipherResult { try { const { rails } = validate(options ?? {}) - return { text: encodeRailFence(text, rails), cipher: 'rail-fence', operation: 'encode', options: { rails } } - } catch (e) { throw normalizeError(e, 'rail-fence') } + return { + text: encodeRailFence(text, rails), + cipher: 'rail-fence', + operation: 'encode', + options: { rails }, + } + } catch (e) { + throw normalizeError(e, 'rail-fence') + } } decode(text: string, options?: CipherBaseOptions): CipherResult { try { const { rails } = validate(options ?? {}) - return { text: decodeRailFence(text, rails), cipher: 'rail-fence', operation: 'decode', options: { rails } } - } catch (e) { throw normalizeError(e, 'rail-fence') } + return { + text: decodeRailFence(text, rails), + cipher: 'rail-fence', + operation: 'decode', + options: { rails }, + } + } catch (e) { + throw normalizeError(e, 'rail-fence') + } } } diff --git a/src/ciphers/rot13.ts b/src/ciphers/rot13.ts index fdf5aaf..d186cc1 100644 --- a/src/ciphers/rot13.ts +++ b/src/ciphers/rot13.ts @@ -13,7 +13,9 @@ function rot13(text: string, stripNonAlpha: boolean): string { } class Rot13 extends Cipher { - name(): string { return 'rot13' } + name(): string { + return 'rot13' + } info(): CipherInfo { return { @@ -28,11 +30,21 @@ class Rot13 extends Cipher { } encode(text: string, options?: CipherBaseOptions): CipherResult { - return { text: rot13(text, options?.stripNonAlpha ?? false), cipher: 'rot13', operation: 'encode', options: {} } + return { + text: rot13(text, options?.stripNonAlpha ?? false), + cipher: 'rot13', + operation: 'encode', + options: {}, + } } decode(text: string, options?: CipherBaseOptions): CipherResult { - return { text: rot13(text, options?.stripNonAlpha ?? false), cipher: 'rot13', operation: 'decode', options: {} } + return { + text: rot13(text, options?.stripNonAlpha ?? false), + cipher: 'rot13', + operation: 'decode', + options: {}, + } } } diff --git a/src/ciphers/rot47.ts b/src/ciphers/rot47.ts index 1d7acb3..b25eeb8 100644 --- a/src/ciphers/rot47.ts +++ b/src/ciphers/rot47.ts @@ -11,7 +11,9 @@ function rot47(text: string): string { } class Rot47 extends Cipher { - name(): string { return 'rot47' } + name(): string { + return 'rot47' + } info(): CipherInfo { return { diff --git a/src/ciphers/tap-code.ts b/src/ciphers/tap-code.ts index 406545a..924d141 100644 --- a/src/ciphers/tap-code.ts +++ b/src/ciphers/tap-code.ts @@ -50,7 +50,11 @@ function encodeTapCode(text: string): string { function decodeTapCode(text: string): string { // Input: space-separated digit pairs like "2 3 1 5 3 1 3 1 3 4" - const nums = text.trim().split(/\s+/).map(Number).filter((n) => n >= 1 && n <= 5) + const nums = text + .trim() + .split(/\s+/) + .map(Number) + .filter((n) => n >= 1 && n <= 5) let result = '' for (let i = 0; i + 1 < nums.length; i += 2) { result += tapReverse(nums[i]!, nums[i + 1]!) @@ -59,7 +63,9 @@ function decodeTapCode(text: string): string { } class TapCode extends Cipher { - name(): string { return 'tap-code' } + name(): string { + return 'tap-code' + } info(): CipherInfo { return { @@ -76,13 +82,17 @@ class TapCode extends Cipher { encode(text: string, _options?: CipherBaseOptions): CipherResult { try { return { text: encodeTapCode(text), cipher: 'tap-code', operation: 'encode', options: {} } - } catch (e) { throw normalizeError(e, 'tap-code') } + } catch (e) { + throw normalizeError(e, 'tap-code') + } } decode(text: string, _options?: CipherBaseOptions): CipherResult { try { return { text: decodeTapCode(text), cipher: 'tap-code', operation: 'decode', options: {} } - } catch (e) { throw normalizeError(e, 'tap-code') } + } catch (e) { + throw normalizeError(e, 'tap-code') + } } } diff --git a/src/ciphers/trithemius.ts b/src/ciphers/trithemius.ts index ad8a709..d80dbac 100644 --- a/src/ciphers/trithemius.ts +++ b/src/ciphers/trithemius.ts @@ -4,7 +4,12 @@ import { normalizeError } from '../core/errors' import { register } from '../core/registry' import { processBaseOptions } from '../core/utils' -function transform(text: string, decrypt: boolean, preserveCase: boolean, stripNonAlpha: boolean): string { +function transform( + text: string, + decrypt: boolean, + preserveCase: boolean, + stripNonAlpha: boolean, +): string { const input = stripNonAlpha ? text.replace(/[^A-Za-z]/g, '') : text let position = 0 @@ -15,14 +20,16 @@ function transform(text: string, decrypt: boolean, preserveCase: boolean, stripN const code = character.toUpperCase().charCodeAt(0) - 65 const shift = decrypt ? -position : position - const transformed = String.fromCharCode(((code + shift) % 26 + 26) % 26 + 65) + const transformed = String.fromCharCode(((((code + shift) % 26) + 26) % 26) + 65) position++ return preserveCase && isLower ? transformed.toLowerCase() : transformed }).join('') } class Trithemius extends Cipher { - name(): string { return 'trithemius' } + name(): string { + return 'trithemius' + } info(): CipherInfo { return { @@ -39,15 +46,29 @@ class Trithemius extends Cipher { encode(text: string, options?: CipherBaseOptions): CipherResult { try { const { preserveCase, stripNonAlpha } = processBaseOptions(options ?? {}) - return { text: transform(text, false, preserveCase, stripNonAlpha), cipher: 'trithemius', operation: 'encode', options: { preserveCase, stripNonAlpha } } - } catch (error) { throw normalizeError(error, 'trithemius') } + return { + text: transform(text, false, preserveCase, stripNonAlpha), + cipher: 'trithemius', + operation: 'encode', + options: { preserveCase, stripNonAlpha }, + } + } catch (error) { + throw normalizeError(error, 'trithemius') + } } decode(text: string, options?: CipherBaseOptions): CipherResult { try { const { preserveCase, stripNonAlpha } = processBaseOptions(options ?? {}) - return { text: transform(text, true, preserveCase, stripNonAlpha), cipher: 'trithemius', operation: 'decode', options: { preserveCase, stripNonAlpha } } - } catch (error) { throw normalizeError(error, 'trithemius') } + return { + text: transform(text, true, preserveCase, stripNonAlpha), + cipher: 'trithemius', + operation: 'decode', + options: { preserveCase, stripNonAlpha }, + } + } catch (error) { + throw normalizeError(error, 'trithemius') + } } } diff --git a/src/ciphers/vigenere.ts b/src/ciphers/vigenere.ts index dc24062..a2ded5f 100644 --- a/src/ciphers/vigenere.ts +++ b/src/ciphers/vigenere.ts @@ -4,11 +4,18 @@ import { getOpt, processBaseOptions } from '../core/utils' import { MissingOptionError, InvalidOptionError, normalizeError } from '../core/errors' import { register } from '../core/registry' -function vigenereProcess(text: string, key: string, decrypt: boolean, preserveCase: boolean, stripNonAlpha: boolean): string { +function vigenereProcess( + text: string, + key: string, + decrypt: boolean, + preserveCase: boolean, + stripNonAlpha: boolean, +): string { let input = text if (stripNonAlpha) input = input.replace(/[^A-Za-z]/g, '') const keyUpper = key.toUpperCase().replace(/[^A-Z]/g, '') - if (keyUpper.length === 0) throw new InvalidOptionError('key', key, 'must contain at least one letter') + if (keyUpper.length === 0) + throw new InvalidOptionError('key', key, 'must contain at least one letter') let ki = 0 return Array.from(input, (c) => { const isUpper = c >= 'A' && c <= 'Z' @@ -18,22 +25,28 @@ function vigenereProcess(text: string, key: string, decrypt: boolean, preserveCa const x = upper.charCodeAt(0) - 65 const shift = keyUpper.charCodeAt(ki % keyUpper.length) - 65 const effective = decrypt ? (26 - shift) % 26 : shift - const code = ((x + effective) % 26 + 26) % 26 + const code = (((x + effective) % 26) + 26) % 26 ki++ const result = String.fromCharCode(code + 65) return preserveCase && isLower ? result.toLowerCase() : result }).join('') } -function validate(opts: CipherBaseOptions): { key: string; preserveCase: boolean; stripNonAlpha: boolean } { - const key = getOpt(opts, "key", undefined) +function validate(opts: CipherBaseOptions): { + key: string + preserveCase: boolean + stripNonAlpha: boolean +} { + const key = getOpt(opts, 'key', undefined) if (!key) throw new MissingOptionError('key') const base = processBaseOptions(opts) - return { key, ...base } + return { key, ...base } } class Vigenere extends Cipher { - name(): string { return 'vigenere' } + name(): string { + return 'vigenere' + } info(): CipherInfo { return { @@ -43,7 +56,12 @@ class Vigenere extends Cipher { family: 'polyalphabetic', selfInverse: false, options: [ - { name: 'key', type: 'string', required: true, description: 'Keyword (letters only, case-insensitive)' }, + { + name: 'key', + type: 'string', + required: true, + description: 'Keyword (letters only, case-insensitive)', + }, ], keyspace: '26^keyLength', } @@ -52,15 +70,29 @@ class Vigenere extends Cipher { encode(text: string, options?: CipherBaseOptions): CipherResult { try { const { key, preserveCase, stripNonAlpha } = validate(options ?? {}) - return { text: vigenereProcess(text, key, false, preserveCase, stripNonAlpha), cipher: 'vigenere', operation: 'encode', options: { key, preserveCase, stripNonAlpha } } - } catch (e) { throw normalizeError(e, 'vigenere') } + return { + text: vigenereProcess(text, key, false, preserveCase, stripNonAlpha), + cipher: 'vigenere', + operation: 'encode', + options: { key, preserveCase, stripNonAlpha }, + } + } catch (e) { + throw normalizeError(e, 'vigenere') + } } decode(text: string, options?: CipherBaseOptions): CipherResult { try { const { key, preserveCase, stripNonAlpha } = validate(options ?? {}) - return { text: vigenereProcess(text, key, true, preserveCase, stripNonAlpha), cipher: 'vigenere', operation: 'decode', options: { key, preserveCase, stripNonAlpha } } - } catch (e) { throw normalizeError(e, 'vigenere') } + return { + text: vigenereProcess(text, key, true, preserveCase, stripNonAlpha), + cipher: 'vigenere', + operation: 'decode', + options: { key, preserveCase, stripNonAlpha }, + } + } catch (e) { + throw normalizeError(e, 'vigenere') + } } } diff --git a/src/commands/ciphers.ts b/src/commands/ciphers.ts index 4ab3e26..7668226 100644 --- a/src/commands/ciphers.ts +++ b/src/commands/ciphers.ts @@ -5,7 +5,12 @@ import { ciphers as listCiphers, create } from '../core/registry' export default defineCommand({ meta: { name: 'ciphers', description: 'List all available ciphers' }, args: { - verbose: { type: 'boolean', description: 'Show detailed info for each cipher', alias: 'v', default: false }, + verbose: { + type: 'boolean', + description: 'Show detailed info for each cipher', + alias: 'v', + default: false, + }, }, async run({ args }) { const names = listCiphers() diff --git a/src/commands/decode.ts b/src/commands/decode.ts index 8a69d51..ad19309 100644 --- a/src/commands/decode.ts +++ b/src/commands/decode.ts @@ -16,7 +16,11 @@ function parseIntOrFail(value: string | undefined, name: string): number | undef export default defineCommand({ meta: { name: 'decode', description: 'Decode ciphertext with a cipher' }, args: { - cipher: { type: 'positional', description: 'Cipher name (caesar, rot13, vigenere, ...)', required: true }, + cipher: { + type: 'positional', + description: 'Cipher name (caesar, rot13, vigenere, ...)', + required: true, + }, text: { type: 'positional', description: 'Text to decode', required: true }, shift: { type: 'string', description: 'Shift value (Caesar)', alias: 's' }, key: { type: 'string', description: 'Keyword for keyed ciphers', alias: 'k' }, diff --git a/src/commands/encode.ts b/src/commands/encode.ts index 9064750..046de62 100644 --- a/src/commands/encode.ts +++ b/src/commands/encode.ts @@ -16,7 +16,11 @@ function parseIntOrFail(value: string | undefined, name: string): number | undef export default defineCommand({ meta: { name: 'encode', description: 'Encode plaintext with a cipher' }, args: { - cipher: { type: 'positional', description: 'Cipher name (caesar, rot13, vigenere, ...)', required: true }, + cipher: { + type: 'positional', + description: 'Cipher name (caesar, rot13, vigenere, ...)', + required: true, + }, text: { type: 'positional', description: 'Text to encode', required: true }, shift: { type: 'string', description: 'Shift value (Caesar)', alias: 's' }, key: { type: 'string', description: 'Keyword for keyed ciphers', alias: 'k' }, diff --git a/src/commands/frequency.ts b/src/commands/frequency.ts index 96d911b..18e4dc2 100644 --- a/src/commands/frequency.ts +++ b/src/commands/frequency.ts @@ -2,7 +2,6 @@ import { defineCommand } from 'citty' import consola from 'consola' import { analyzeFrequency } from '../core/frequency' - export default defineCommand({ meta: { name: 'frequency', description: 'Frequency analysis of text' }, args: { @@ -12,11 +11,16 @@ export default defineCommand({ async run({ args }) { const language = args.lang === 'pl' ? 'pl' : 'en' const analysis = analyzeFrequency(args.text, language) - if (analysis === undefined) { consola.warn('No letters found in input'); return } + if (analysis === undefined) { + consola.warn('No letters found in input') + return + } const maxCount = analysis.counts[0]![1] - consola.info(`\x1b[1mFrequency Analysis\x1b[0m (${analysis.total} letters, lang=${analysis.language}):\n`) + consola.info( + `\x1b[1mFrequency Analysis\x1b[0m (${analysis.total} letters, lang=${analysis.language}):\n`, + ) consola.info(' Letter | Count | Freq | Bar') consola.info(' -------+-------+--------+' + '-'.repeat(30)) for (const [character, count] of analysis.counts) { @@ -24,7 +28,9 @@ export default defineCommand({ const bar = '\u2588'.repeat(Math.ceil((count / maxCount) * 20)) consola.log(` ${character} | ${String(count).padStart(5)} | ${percentage}% | ${bar}`) } - consola.info(`\n Expected order (${analysis.language}): ${analysis.reference.split('').join(' ')}`) + consola.info( + `\n Expected order (${analysis.language}): ${analysis.reference.split('').join(' ')}`, + ) consola.info(` Actual order: ${analysis.counts.map(([character]) => character).join(' ')}`) }, }) diff --git a/src/core/errors.ts b/src/core/errors.ts index 987416f..d76e46c 100644 --- a/src/core/errors.ts +++ b/src/core/errors.ts @@ -21,7 +21,7 @@ export class InvalidOptionError extends CipherError { public readonly value: unknown, public readonly reason: string, ) { - super(`Invalid option ${option}=${value}: ${reason}`) + super(`Invalid option ${option}=${String(value)}: ${reason}`) this.name = 'InvalidOptionError' } } diff --git a/src/core/resolve.ts b/src/core/resolve.ts index ff9096b..d663e00 100644 --- a/src/core/resolve.ts +++ b/src/core/resolve.ts @@ -8,6 +8,6 @@ export function resolveCipher(preferred?: string): Cipher { const normalized = preferred.toLowerCase().replace(/\s+/g, '-') if (has(normalized)) return create(normalized) } - + throw new UnknownCipherError(preferred ?? '(none)') } diff --git a/src/core/types.ts b/src/core/types.ts index eb0f103..df5c186 100644 --- a/src/core/types.ts +++ b/src/core/types.ts @@ -52,7 +52,6 @@ export interface EnigmaOptions extends CipherBaseOptions { plugboard?: string } - /** Rail Fence cipher options. */ export interface RailFenceOptions extends CipherBaseOptions { /** Number of rails (2 or more). Default: 3. */ @@ -103,7 +102,16 @@ export interface CipherInfo { /** One-line description. */ description: string /** Cipher family. */ - family: 'substitution-shift' | 'substitution-keyed' | 'substitution-multiplicative' | 'substitution-reflection' | 'digraph' | 'fractionation' | 'transposition' | 'polyalphabetic' | 'rotor' + family: + | 'substitution-shift' + | 'substitution-keyed' + | 'substitution-multiplicative' + | 'substitution-reflection' + | 'digraph' + | 'fractionation' + | 'transposition' + | 'polyalphabetic' + | 'rotor' /** Self-inverse: encode(encode(x)) == x. */ selfInverse: boolean /** Required/optional options. */ @@ -111,4 +119,3 @@ export interface CipherInfo { /** Keyspace size description. */ keyspace?: string } - diff --git a/src/core/utils.ts b/src/core/utils.ts index 2112735..8794281 100644 --- a/src/core/utils.ts +++ b/src/core/utils.ts @@ -18,11 +18,17 @@ export function buildPolybiusSquare(key?: string): { for (const c of src) { if (c < 'A' || c > 'Z') continue const ch = c === 'J' ? 'I' : c - if (!seen.has(ch)) { seen.add(ch); letters.push(ch) } + if (!seen.has(ch)) { + seen.add(ch) + letters.push(ch) + } } for (let i = 65; i <= 90; i++) { const ch = String.fromCharCode(i) === 'J' ? 'I' : String.fromCharCode(i) - if (!seen.has(ch)) { seen.add(ch); letters.push(ch) } + if (!seen.has(ch)) { + seen.add(ch) + letters.push(ch) + } } const square: string[][] = [] const pos = new Map() @@ -73,13 +79,23 @@ export class LruCache { } this.map.set(key, { value, ts: performance.now() }) } - has(key: K): boolean { return this.map.has(key) } - clear(): void { this.map.clear() } - get size(): number { return this.map.size } + has(key: K): boolean { + return this.map.has(key) + } + clear(): void { + this.map.clear() + } + get size(): number { + return this.map.size + } } /** Build a stable cache key from encode/decode arguments. */ -export function cipherCacheKey(operation: string, text: string, options?: Record): string { +export function cipherCacheKey( + operation: string, + text: string, + options?: Record, +): string { return `${operation}|${text}|${JSON.stringify(options ?? {})}` } @@ -89,16 +105,17 @@ export function cipherCacheKey(operation: string, text: string, options?: Record export class RateLimiter { private tokens: number private lastRefill: number - constructor( - private readonly maxPerSec: number, - ) { + constructor(private readonly maxPerSec: number) { this.tokens = maxPerSec this.lastRefill = Date.now() } /** Returns true if call is allowed; false if throttled. */ allow(): boolean { this.refill() - if (this.tokens >= 1) { this.tokens--; return true } + if (this.tokens >= 1) { + this.tokens-- + return true + } return false } private refill(): void { @@ -133,7 +150,9 @@ export function withCipherError(name: string, fn: () => T): T { const msg = e instanceof Error ? e.message : String(e) throw new (class extends Error { name = 'CipherError' - constructor() { super(`[${name}] ${msg}`) } + constructor() { + super(`[${name}] ${msg}`) + } })() } } diff --git a/test/unit/ciphers.test.ts b/test/unit/ciphers.test.ts index 156785c..b7718e8 100644 --- a/test/unit/ciphers.test.ts +++ b/test/unit/ciphers.test.ts @@ -1,4 +1,4 @@ -import { describe, it, expect } from 'vitest' +import { describe, it, expect } from 'vite-plus/test' import '../../src/index' import { create, ciphers, has } from '../../src/core/registry' import { resolveCipher } from '../../src/core/resolve' @@ -7,7 +7,20 @@ import { Cipher } from '../../src/core/cipher' describe('registry', () => { it('registers all 18 ciphers', () => { expect(ciphers()).toHaveLength(18) - for (const name of ['caesar', 'rot13', 'rot47', 'atbash', 'vigenere', 'trithemius', 'alberti', 'rail-fence', 'affine', 'playfair', 'polybius', 'enigma']) { + for (const name of [ + 'caesar', + 'rot13', + 'rot47', + 'atbash', + 'vigenere', + 'trithemius', + 'alberti', + 'rail-fence', + 'affine', + 'playfair', + 'polybius', + 'enigma', + ]) { expect(has(name)).toBe(true) } }) @@ -138,10 +151,18 @@ describe('vigenere', () => { }) it('reports resolved base options', () => { - const encoded = vigenere.encode('Attack at dawn!', { key: 'LEMON', preserveCase: false, stripNonAlpha: true }) + const encoded = vigenere.encode('Attack at dawn!', { + key: 'LEMON', + preserveCase: false, + stripNonAlpha: true, + }) expect(encoded.text).toBe('LXFOPVEFRNHR') expect(encoded.options).toEqual({ key: 'LEMON', preserveCase: false, stripNonAlpha: true }) - expect(vigenere.decode(encoded.text, { key: 'LEMON' }).options).toEqual({ key: 'LEMON', preserveCase: true, stripNonAlpha: false }) + expect(vigenere.decode(encoded.text, { key: 'LEMON' }).options).toEqual({ + key: 'LEMON', + preserveCase: true, + stripNonAlpha: false, + }) }) it('requires key', () => { @@ -166,11 +187,17 @@ describe('trithemius', () => { }) it('honors base options and roundtrips', () => { - const encoded = trithemius.encode('Attack at dawn!', { preserveCase: false, stripNonAlpha: true }) + const encoded = trithemius.encode('Attack at dawn!', { + preserveCase: false, + stripNonAlpha: true, + }) expect(encoded.text).toBe('AUVDGPGALJGY') expect(encoded.options).toEqual({ preserveCase: false, stripNonAlpha: true }) expect(trithemius.decode(encoded.text, { preserveCase: false }).text).toBe('ATTACKATDAWN') - expect(trithemius.decode(encoded.text).options).toEqual({ preserveCase: true, stripNonAlpha: false }) + expect(trithemius.decode(encoded.text).options).toEqual({ + preserveCase: true, + stripNonAlpha: false, + }) }) }) @@ -178,8 +205,12 @@ describe('alberti', () => { const alberti = create('alberti') it('matches a keyed fixed-period disk vector', () => { - expect(alberti.encode('ATTACK AT DAWN', { key: 'ALBERTI', period: 4 }).text).toBe('ASSAEH LU TBYN') - expect(alberti.decode('ASSAEH LU TBYN', { key: 'ALBERTI', period: 4 }).text).toBe('ATTACK AT DAWN') + expect(alberti.encode('ATTACK AT DAWN', { key: 'ALBERTI', period: 4 }).text).toBe( + 'ASSAEH LU TBYN', + ) + expect(alberti.decode('ASSAEH LU TBYN', { key: 'ALBERTI', period: 4 }).text).toBe( + 'ATTACK AT DAWN', + ) }) it('advances the disk only after the configured number of Latin letters', () => { @@ -187,9 +218,21 @@ describe('alberti', () => { }) it('normalizes duplicate key letters and honors base options', () => { - const encoded = alberti.encode('Attack at dawn!', { key: 'Letter', period: 3, preserveCase: false, stripNonAlpha: true }) - expect(encoded.options).toEqual({ key: 'LETR', period: 3, preserveCase: false, stripNonAlpha: true }) - expect(alberti.decode(encoded.text, { key: 'Letter', period: 3, preserveCase: false }).text).toBe('ATTACKATDAWN') + const encoded = alberti.encode('Attack at dawn!', { + key: 'Letter', + period: 3, + preserveCase: false, + stripNonAlpha: true, + }) + expect(encoded.options).toEqual({ + key: 'LETR', + period: 3, + preserveCase: false, + stripNonAlpha: true, + }) + expect( + alberti.decode(encoded.text, { key: 'Letter', period: 3, preserveCase: false }).text, + ).toBe('ATTACKATDAWN') }) it('requires an ASCII-letter key and positive integer period', () => { @@ -205,7 +248,9 @@ describe('rail-fence', () => { const railFence = create('rail-fence') it('encodes with 3 rails', () => { - expect(railFence.encode('WEAREDISCOVEREDRUNATONCE', { rails: 3 }).text).toBe('WECRUOERDSOEERNTNEAIVDAC') + expect(railFence.encode('WEAREDISCOVEREDRUNATONCE', { rails: 3 }).text).toBe( + 'WECRUOERDSOEERNTNEAIVDAC', + ) }) it('decodes with 3 rails', () => { @@ -406,7 +451,9 @@ describe('columnar', () => { const col = create('columnar') it('encodes with key', () => { - expect(col.encode('DEFEND THE EAST WALL', { key: 'GERMAN' }).text).toBe('N W ETSLD ALEE DEA FHT') + expect(col.encode('DEFEND THE EAST WALL', { key: 'GERMAN' }).text).toBe( + 'N W ETSLD ALEE DEA FHT', + ) }) it('roundtrips', () => { @@ -449,7 +496,9 @@ describe('bifid', () => { const bifid = create('bifid') it('encodes FLEE AT ONCE', () => { - expect(bifid.encode('FLEE AT ONCE', { key: 'BICONDITIONAL', period: 5 }).text).toBe('GTDUXDBTUM') + expect(bifid.encode('FLEE AT ONCE', { key: 'BICONDITIONAL', period: 5 }).text).toBe( + 'GTDUXDBTUM', + ) }) it('roundtrips', () => { @@ -464,13 +513,15 @@ describe('bifid', () => { expect(decoded.text).toBe('CRYPTOGRAPHY') }) - it.each([0, -1, 1.5, Number.NaN, Number.POSITIVE_INFINITY])('rejects invalid period %s', (period) => { - expect(() => bifid.encode('HELLO', { period })).toThrow(/must be a positive integer/) - expect(() => bifid.decode('HELLO', { period })).toThrow(/must be a positive integer/) - }) + it.each([0, -1, 1.5, Number.NaN, Number.POSITIVE_INFINITY])( + 'rejects invalid period %s', + (period) => { + expect(() => bifid.encode('HELLO', { period })).toThrow(/must be a positive integer/) + expect(() => bifid.decode('HELLO', { period })).toThrow(/must be a positive integer/) + }, + ) }) - describe('enigma', () => { const enigma = create('enigma') diff --git a/test/unit/cli-args.test.ts b/test/unit/cli-args.test.ts index e687eb7..e1cb3e8 100644 --- a/test/unit/cli-args.test.ts +++ b/test/unit/cli-args.test.ts @@ -1,21 +1,21 @@ -import { describe, it, expect } from "vitest" -import { normalizeMainArgs } from "../../src/cli-args" +import { describe, it, expect } from 'vite-plus/test' +import { normalizeMainArgs } from '../../src/cli-args' -describe("normalizeMainArgs", () => { - it("returns ciphers for empty argv", () => { - expect(normalizeMainArgs([])).toEqual(["ciphers"]) +describe('normalizeMainArgs', () => { + it('returns ciphers for empty argv', () => { + expect(normalizeMainArgs([])).toEqual(['ciphers']) }) - it("preserves known subcommands", () => { - expect(normalizeMainArgs(["encode", "caesar", "3"])).toEqual(["encode", "caesar", "3"]) - expect(normalizeMainArgs(["decode", "caesar", "3"])).toEqual(["decode", "caesar", "3"]) + it('preserves known subcommands', () => { + expect(normalizeMainArgs(['encode', 'caesar', '3'])).toEqual(['encode', 'caesar', '3']) + expect(normalizeMainArgs(['decode', 'caesar', '3'])).toEqual(['decode', 'caesar', '3']) }) - it("lowercases subcommands (case-insensitive check)", () => { - expect(normalizeMainArgs(["Encode", "caesar", "3"])).toEqual(["Encode", "caesar", "3"]) + it('lowercases subcommands (case-insensitive check)', () => { + expect(normalizeMainArgs(['Encode', 'caesar', '3'])).toEqual(['Encode', 'caesar', '3']) }) - it("prepends encode for unknown first arg (treated as cipher name)", () => { - expect(normalizeMainArgs(["caesar", "3", "hello"])).toEqual(["encode", "caesar", "3", "hello"]) + it('prepends encode for unknown first arg (treated as cipher name)', () => { + expect(normalizeMainArgs(['caesar', '3', 'hello'])).toEqual(['encode', 'caesar', '3', 'hello']) }) - it("prepends encode for numbers", () => { - expect(normalizeMainArgs(["3", "hello"])).toEqual(["encode", "3", "hello"]) + it('prepends encode for numbers', () => { + expect(normalizeMainArgs(['3', 'hello'])).toEqual(['encode', '3', 'hello']) }) }) diff --git a/test/unit/columnar-cache.test.ts b/test/unit/columnar-cache.test.ts index 27bb1bf..d45d598 100644 --- a/test/unit/columnar-cache.test.ts +++ b/test/unit/columnar-cache.test.ts @@ -1,7 +1,7 @@ -import { describe, it, expect, vi } from 'vitest' +import { describe, it, expect } from 'vite-plus/test' import '../../src/index' import { create } from '../../src/core/registry' -import { LruCache, RateLimiter, RateLimitError, cipherCacheKey } from '../../src/core/utils' +import { LruCache, RateLimiter, cipherCacheKey } from '../../src/core/utils' // ── LruCache unit tests ───────────────────────────────────────────────── @@ -86,7 +86,7 @@ describe('RateLimiter', () => { for (let i = 0; i < 10; i++) limiter.allow() expect(limiter.allow()).toBe(false) // Wait 200ms → ~2 tokens refilled (10/sec) - await new Promise(r => setTimeout(r, 250)) + await new Promise((r) => setTimeout(r, 250)) expect(limiter.allow()).toBe(true) }) }) @@ -205,7 +205,9 @@ describe('columnar — rate limit', () => { col.reset() // Force rate limit for (let i = 0; i < 200; i++) { - try { col.encode(`X-${i}`, { key: 'K' }) } catch {} + try { + col.encode(`X-${i}`, { key: 'K' }) + } catch {} } let gotCipherError = false try { @@ -224,7 +226,9 @@ describe('columnar — backwards compatibility', () => { it('encodes with key', () => { const col = create('columnar') col.reset() - expect(col.encode('DEFEND THE EAST WALL', { key: 'GERMAN' }).text).toBe('N W ETSLD ALEE DEA FHT') + expect(col.encode('DEFEND THE EAST WALL', { key: 'GERMAN' }).text).toBe( + 'N W ETSLD ALEE DEA FHT', + ) }) it('roundtrips', () => { diff --git a/test/unit/frequency.test.ts b/test/unit/frequency.test.ts index 1b89696..7b04e25 100644 --- a/test/unit/frequency.test.ts +++ b/test/unit/frequency.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it } from 'vitest' +import { describe, expect, it } from 'vite-plus/test' import { analyzeFrequency } from '../../src/core/frequency' describe('analyzeFrequency', () => { @@ -6,7 +6,11 @@ describe('analyzeFrequency', () => { expect(analyzeFrequency('AaA, bb! c')).toEqual({ total: 6, language: 'en', - counts: [['A', 3], ['B', 2], ['C', 1]], + counts: [ + ['A', 3], + ['B', 2], + ['C', 1], + ], reference: 'ETAOINSHRDLCUMWFGYPBVKJXQZ', }) }) diff --git a/test/unit/omp-extension.test.ts b/test/unit/omp-extension.test.ts index 8b5a771..83f5c23 100644 --- a/test/unit/omp-extension.test.ts +++ b/test/unit/omp-extension.test.ts @@ -1,4 +1,4 @@ -import { beforeAll, describe, expect, it } from 'vitest' +import { beforeAll, describe, expect, it } from 'vite-plus/test' import { Type } from '@oh-my-pi/omptype/typebox' import ciphersExtension from '../../packages/omp/extensions/ciphers' @@ -21,12 +21,14 @@ type RegisteredTool = { const tools = new Map() function isRegisteredTool(value: unknown): value is RegisteredTool { - return typeof value === 'object' - && value !== null - && typeof Reflect.get(value, 'name') === 'string' - && typeof Reflect.get(value, 'label') === 'string' - && typeof Reflect.get(value, 'parameters') === 'function' - && typeof Reflect.get(value, 'execute') === 'function' + return ( + typeof value === 'object' && + value !== null && + typeof Reflect.get(value, 'name') === 'string' && + typeof Reflect.get(value, 'label') === 'string' && + typeof Reflect.get(value, 'parameters') === 'function' && + typeof Reflect.get(value, 'execute') === 'function' + ) } function getTool(name: string): RegisteredTool { @@ -50,16 +52,28 @@ beforeAll(() => { describe('OMP extension', () => { it('registers four essential read-only tools', () => { - expect([...tools.values()].map(({ name, label, approval, loadMode }) => ({ - name, - label, - approval, - loadMode, - }))).toEqual([ + expect( + [...tools.values()].map(({ name, label, approval, loadMode }) => ({ + name, + label, + approval, + loadMode, + })), + ).toEqual([ { name: 'cipher_encode', label: 'Cipher Encode', approval: 'read', loadMode: 'essential' }, { name: 'cipher_decode', label: 'Cipher Decode', approval: 'read', loadMode: 'essential' }, - { name: 'cipher_brute_caesar', label: 'Brute Force Caesar', approval: 'read', loadMode: 'essential' }, - { name: 'cipher_frequency', label: 'Frequency Analysis', approval: 'read', loadMode: 'essential' }, + { + name: 'cipher_brute_caesar', + label: 'Brute Force Caesar', + approval: 'read', + loadMode: 'essential', + }, + { + name: 'cipher_frequency', + label: 'Frequency Analysis', + approval: 'read', + loadMode: 'essential', + }, ]) }) @@ -78,10 +92,12 @@ describe('OMP extension', () => { }) expect(decoded.content[0]?.text).toBe('ATTACK AT DAWN') - await expect(getTool('cipher_encode').execute('failure', { - cipher: 'cae', - text: 'TEST', - })).rejects.toThrow('Unknown cipher') + await expect( + getTool('cipher_encode').execute('failure', { + cipher: 'cae', + text: 'TEST', + }), + ).rejects.toThrow('Unknown cipher') }) it('enforces language, option, and resource boundaries in OMP schemas', () => { @@ -91,17 +107,43 @@ describe('OMP extension', () => { expect(frequencySchema.safeParse({ text: 'X'.repeat(100_001) }).success).toBe(false) const transformSchema = getTool('cipher_encode').parameters - expect(transformSchema.safeParse({ cipher: 'caesar', text: 'X'.repeat(10_000) }).success).toBe(true) - expect(transformSchema.safeParse({ cipher: 'caesar', text: 'X'.repeat(10_001) }).success).toBe(false) + expect(transformSchema.safeParse({ cipher: 'caesar', text: 'X'.repeat(10_000) }).success).toBe( + true, + ) + expect(transformSchema.safeParse({ cipher: 'caesar', text: 'X'.repeat(10_001) }).success).toBe( + false, + ) expect(transformSchema.safeParse({ cipher: 'bifid', text: 'X', period: 0 }).success).toBe(false) - expect(transformSchema.safeParse({ cipher: 'rail-fence', text: 'X', rails: 1 }).success).toBe(false) - expect(transformSchema.safeParse({ cipher: 'vigenere', text: 'X', key: 'K'.repeat(1_001) }).success).toBe(false) - expect(transformSchema.safeParse({ cipher: 'enigma', text: 'A', positions: 'AAA', rings: 'AAA', plugboard: 'AV BS' }).success).toBe(true) - expect(transformSchema.safeParse({ cipher: 'enigma', text: 'A', positions: 'AA' }).success).toBe(false) - expect(transformSchema.safeParse({ cipher: 'enigma', text: 'A', rings: 'AAAA' }).success).toBe(false) - expect(transformSchema.safeParse({ cipher: 'enigma', text: 'A', positions: '123' }).success).toBe(false) - expect(transformSchema.safeParse({ cipher: 'enigma', text: 'A', rings: 'A1A' }).success).toBe(false) - expect(transformSchema.safeParse({ cipher: 'enigma', text: 'A', plugboard: 'A'.repeat(78) }).success).toBe(false) + expect(transformSchema.safeParse({ cipher: 'rail-fence', text: 'X', rails: 1 }).success).toBe( + false, + ) + expect( + transformSchema.safeParse({ cipher: 'vigenere', text: 'X', key: 'K'.repeat(1_001) }).success, + ).toBe(false) + expect( + transformSchema.safeParse({ + cipher: 'enigma', + text: 'A', + positions: 'AAA', + rings: 'AAA', + plugboard: 'AV BS', + }).success, + ).toBe(true) + expect( + transformSchema.safeParse({ cipher: 'enigma', text: 'A', positions: 'AA' }).success, + ).toBe(false) + expect(transformSchema.safeParse({ cipher: 'enigma', text: 'A', rings: 'AAAA' }).success).toBe( + false, + ) + expect( + transformSchema.safeParse({ cipher: 'enigma', text: 'A', positions: '123' }).success, + ).toBe(false) + expect(transformSchema.safeParse({ cipher: 'enigma', text: 'A', rings: 'A1A' }).success).toBe( + false, + ) + expect( + transformSchema.safeParse({ cipher: 'enigma', text: 'A', plugboard: 'A'.repeat(78) }).success, + ).toBe(false) const bruteSchema = getTool('cipher_brute_caesar').parameters expect(bruteSchema.safeParse({ text: 'X'.repeat(2_001) }).success).toBe(false) diff --git a/test/unit/utils.test.ts b/test/unit/utils.test.ts index 2b278e1..aaa2d4f 100644 --- a/test/unit/utils.test.ts +++ b/test/unit/utils.test.ts @@ -1,11 +1,10 @@ /** * ciphers pure utility tests. */ -import { describe, it, expect } from 'vitest' +import { describe, it, expect } from 'vite-plus/test' import { LruCache, RateLimiter, - RateLimitError, cipherCacheKey, buildPolybiusSquare, processBaseOptions, @@ -31,7 +30,7 @@ describe('LruCache', () => { const c = new LruCache(2) c.set('a', 1) c.set('b', 2) - c.set('c', 3) // evicts 'a' + c.set('c', 3) // evicts 'a' expect(c.get('a')).toBeUndefined() expect(c.get('b')).toBe(2) expect(c.get('c')).toBe(3) @@ -41,8 +40,8 @@ describe('LruCache', () => { const c = new LruCache(2) c.set('a', 1) c.set('b', 2) - c.get('a') // 'a' is now most recent - c.set('c', 3) // evicts 'b' + c.get('a') // 'a' is now most recent + c.set('c', 3) // evicts 'b' expect(c.get('a')).toBe(1) expect(c.get('b')).toBeUndefined() expect(c.get('c')).toBe(3) @@ -142,7 +141,7 @@ describe('RateLimiter', () => { // Drain for (let i = 0; i < 10; i++) rl.allow() // Wait briefly for refill - await new Promise(r => setTimeout(r, 150)) + await new Promise((r) => setTimeout(r, 150)) // Should have some tokens back expect(rl.allow()).toBe(true) }) @@ -155,13 +154,23 @@ describe('withCipherError', () => { }) it('normalizes errors thrown by the operation', () => { - expect(() => withCipherError('test', () => { throw new Error('boom') })).toThrow() + expect(() => + withCipherError('test', () => { + throw new Error('boom') + }), + ).toThrow() }) it('passes through UnknownCipherError subclass unchanged (preserves .cipher)', () => { const original = new UnknownCipherError('foo') let caught: unknown - try { withCipherError('test', () => { throw original }) } catch (e) { caught = e } + try { + withCipherError('test', () => { + throw original + }) + } catch (e) { + caught = e + } expect(caught).toBe(original) expect(caught).toBeInstanceOf(UnknownCipherError) expect((caught as UnknownCipherError).cipher).toBe('foo') @@ -170,7 +179,13 @@ describe('withCipherError', () => { it('passes through InvalidOptionError subclass unchanged (preserves .option/.value/.reason)', () => { const original = new InvalidOptionError('shift', 99, 'out of range') let caught: unknown - try { withCipherError('test', () => { throw original }) } catch (e) { caught = e } + try { + withCipherError('test', () => { + throw original + }) + } catch (e) { + caught = e + } expect(caught).toBe(original) expect(caught).toBeInstanceOf(InvalidOptionError) expect((caught as InvalidOptionError).option).toBe('shift') @@ -179,7 +194,13 @@ describe('withCipherError', () => { it('passes through MissingOptionError subclass unchanged', () => { const original = new MissingOptionError('key') let caught: unknown - try { withCipherError('test', () => { throw original }) } catch (e) { caught = e } + try { + withCipherError('test', () => { + throw original + }) + } catch (e) { + caught = e + } expect(caught).toBe(original) expect(caught).toBeInstanceOf(MissingOptionError) }) diff --git a/vite.config.ts b/vite.config.ts new file mode 100644 index 0000000..a7f74fd --- /dev/null +++ b/vite.config.ts @@ -0,0 +1,14 @@ +import { defineConfig } from 'vite-plus' + +export default defineConfig({ + lint: { + options: { + typeAware: true, + typeCheck: false, + }, + }, + fmt: { + semi: false, + singleQuote: true, + }, +}) From b38edd86d972ba05743bfbece608a4a4a042de83 Mon Sep 17 00:00:00 2001 From: oritwoen <18102267+oritwoen@users.noreply.github.com> Date: Fri, 14 Aug 2026 12:44:47 +0200 Subject: [PATCH 2/2] fix: pin Vite+ toolchain versions --- package.json | 2 +- pnpm-lock.yaml | 4 ++-- pnpm-workspace.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 0c0e6d3..ebf8040 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "tsx": "^4.22.4", "typebox": "^1.1.39", "typescript": "^6.0.3", - "vite-plus": "^0.2.9" + "vite-plus": "0.2.9" }, "engines": { "node": ">=25" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 1b9d310..10487db 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -5,7 +5,7 @@ settings: excludeLinksFromLockfile: false overrides: - vite: npm:@voidzero-dev/vite-plus-core@latest + vite: npm:@voidzero-dev/vite-plus-core@0.2.9 vitest: 4.1.10 importers: @@ -47,7 +47,7 @@ importers: specifier: ^6.0.3 version: 6.0.3 vite-plus: - specifier: ^0.2.9 + specifier: 0.2.9 version: 0.2.9(@opentelemetry/api@1.9.1)(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(typescript@6.0.3)(vite@8.0.14(@types/node@25.9.1)(esbuild@0.28.0)(jiti@2.7.0)(tsx@4.22.4)(yaml@2.9.0))(yaml@2.9.0) packages: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index a126ccf..6ccf10d 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,5 +2,5 @@ packages: - packages/* overrides: - vite: npm:@voidzero-dev/vite-plus-core@latest + vite: npm:@voidzero-dev/vite-plus-core@0.2.9 vitest: 4.1.10