diff --git a/.changeset/cold-horses-crash.md b/.changeset/cold-horses-crash.md new file mode 100644 index 0000000..03d4ce1 --- /dev/null +++ b/.changeset/cold-horses-crash.md @@ -0,0 +1,5 @@ +--- +'webauthx': patch +--- + +Added AAGUID lookup and resolve to get webauthn provider info diff --git a/.github/workflows/verify.yml b/.github/workflows/verify.yml index a57056b..3684c6c 100644 --- a/.github/workflows/verify.yml +++ b/.github/workflows/verify.yml @@ -18,6 +18,9 @@ jobs: - name: Check code run: pnpm check + - name: Build + run: pnpm build + - name: Check types run: pnpm check:types @@ -32,7 +35,7 @@ jobs: uses: ./.github/actions/install-dependencies - name: Install Playwright browsers - run: pnpx playwright@1.58.2 install --with-deps chromium + run: pnpm exec playwright install --with-deps chromium - name: Run tests run: pnpm run test --bail=1 diff --git a/.gitignore b/.gitignore index 8e57cf8..a6f3fc9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,9 @@ dist node_modules plans -*.tsbuildinfo \ No newline at end of file +*.tsbuildinfo +_ +.env +.env.* +!.env.example +.DS_Store diff --git a/.vscode/settings.json b/.vscode/settings.json index d0bc5d5..e335e7c 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,5 +6,8 @@ }, "[jsonc]": { "editor.defaultFormatter": "nicolo-ribaudo.oxfmt-vscode" + }, + "[typescript]": { + "editor.defaultFormatter": "oxc.oxc-vscode" } } diff --git a/README.md b/README.md index 4146a78..1efabf7 100644 --- a/README.md +++ b/README.md @@ -104,10 +104,23 @@ app.post('/register/verify', async (request) => { }) // Persist the credential for future authentication. - await store.storeCredential(result.credential) + await store.storeCredential({ + ...result.credential, + aaguid: result.aaguid, + }) }) ``` +#### 4. Resolve Authenticator Name (Optional) + +```ts +import { Aaguid } from 'webauthx/server' + +const info = await Aaguid.lookup({ id: storedCredential.aaguid }) +// => { name: '1Password', iconLight?: 'data:image/...', iconDark?: 'data:image/...' } +// => null +``` + ### Authentication (Log in) Authenticate a returning user with their existing passkey. The server generates a challenge, the client signs it, and the server verifies the signature. @@ -199,14 +212,14 @@ const response = await Authentication.sign({ options }) ##### Parameters -| Parameter | Type | Description | -| --- | --- | --- | -| `getFn` | `function` | Custom credential request function (for testing). | +| Parameter | Type | Description | +| --------- | -------------------------- | -------------------------------------------------- | +| `getFn` | `function` | Custom credential request function (for testing). | | `options` | `CredentialRequestOptions` | WebAuthn options from `Authentication.getOptions`. | ##### Return Value -`Promise` +`Promise` Response to send to the server. @@ -224,14 +237,14 @@ const credential = await Registration.create({ options }) ##### Parameters -| Parameter | Type | Description | -| --- | --- | --- | -| `createFn` | `function` | Custom credential creation function (for testing). | -| `options` | `CredentialCreationOptions` | WebAuthn options from `Registration.getOptions`. | +| Parameter | Type | Description | +| ---------- | --------------------------- | -------------------------------------------------- | +| `createFn` | `function` | Custom credential creation function (for testing). | +| `options` | `CredentialCreationOptions` | WebAuthn options from `Registration.getOptions`. | ##### Return Value -`Promise` +`Promise` Credential to send to the server. @@ -256,13 +269,13 @@ const { challenge, options } = Authentication.getOptions({ ##### Parameters -| Parameter | Type | Description | -| --- | --- | --- | -| `challenge` | `Hex` | Optional challenge. A random 32-byte hex value is generated if omitted. | -| `credentialId` | `string \| string[]` | Credential ID(s) to allow. | -| `rpId` | `string` | Relying party ID. | -| `timeout` | `number` | Timeout in milliseconds. | -| `userVerification` | `string` | User verification requirement. | +| Parameter | Type | Description | +| ------------------ | -------------------- | ----------------------------------------------------------------------- | +| `challenge` | `Hex` | Optional challenge. A random 32-byte hex value is generated if omitted. | +| `credentialId` | `string \| string[]` | Credential ID(s) to allow. | +| `rpId` | `string` | Relying party ID. | +| `timeout` | `number` | Timeout in milliseconds. | +| `userVerification` | `string` | User verification requirement. | ##### Return Value @@ -289,13 +302,13 @@ const valid = Authentication.verify(response, { ##### Parameters -| Parameter | Type | Description | -| --- | --- | --- | -| `options.challenge` | `Hex` | Expected challenge. | -| `options.origin` | `string \| string[]` | Expected origin(s). | -| `options.publicKey` | `Hex` | The stored P-256 public key (hex). | -| `options.rpId` | `string` | Expected relying party ID. | -| `response` | `Authentication.Response` | The authentication response from the client. | +| Parameter | Type | Description | +| ------------------- | ------------------------- | -------------------------------------------- | +| `options.challenge` | `Hex` | Expected challenge. | +| `options.origin` | `string \| string[]` | Expected origin(s). | +| `options.publicKey` | `Hex` | The stored P-256 public key (hex). | +| `options.rpId` | `string` | Expected relying party ID. | +| `response` | `Authentication.Response` | The authentication response from the client. | ##### Return Value @@ -320,16 +333,16 @@ const { challenge, options } = Registration.getOptions({ ##### Parameters -| Parameter | Type | Description | -| --- | --- | --- | -| `attestation` | `string` | Attestation conveyance preference. | -| `authenticatorSelection` | `object` | Authenticator selection criteria. | -| `challenge` | `Hex` | Optional challenge. A random 32-byte hex value is generated if omitted. | -| `excludeCredentialIds` | `string[]` | Credential IDs to exclude (prevent re-registration). | -| `name` | `string` | Display name for the credential (shorthand for `user.name`). | -| `rp` | `{ id: string; name: string }` | Relying party identifier and display name. | -| `timeout` | `number` | Timeout in milliseconds. | -| `user` | `{ name: string; displayName?: string; id?: BufferSource }` | User account descriptor. Alternative to `name`. | +| Parameter | Type | Description | +| ------------------------ | ----------------------------------------------------------- | ----------------------------------------------------------------------- | +| `attestation` | `string` | Attestation conveyance preference. | +| `authenticatorSelection` | `object` | Authenticator selection criteria. | +| `challenge` | `Hex` | Optional challenge. A random 32-byte hex value is generated if omitted. | +| `excludeCredentialIds` | `string[]` | Credential IDs to exclude (prevent re-registration). | +| `name` | `string` | Display name for the credential (shorthand for `user.name`). | +| `rp` | `{ id: string; name: string }` | Relying party identifier and display name. | +| `timeout` | `number` | Timeout in milliseconds. | +| `user` | `{ name: string; displayName?: string; id?: BufferSource }` | User account descriptor. Alternative to `name`. | ##### Return Value @@ -355,17 +368,75 @@ const result = Registration.verify(credential, { ##### Parameters -| Parameter | Type | Description | -| --- | --- | --- | -| `credential` | `Registration.Credential` | The credential from the client. | -| `options.challenge` | `Hex \| Uint8Array \| ((challenge: string) => boolean)` | Expected challenge value or async validator function. | -| `options.origin` | `string \| string[]` | Expected origin(s) (e.g. `"https://example.com"`). | -| `options.rpId` | `string` | Relying party ID (e.g. `"example.com"`). | -| `options.userVerification` | `string` | User verification requirement. Default: `'required'`. | +| Parameter | Type | Description | +| -------------------------- | ------------------------------------------------------- | ----------------------------------------------------- | +| `credential` | `Registration.Credential` | The credential from the client. | +| `options.challenge` | `Hex \| Uint8Array \| ((challenge: string) => boolean)` | Expected challenge value or async validator function. | +| `options.origin` | `string \| string[]` | Expected origin(s) (e.g. `"https://example.com"`). | +| `options.rpId` | `string` | Relying party ID (e.g. `"example.com"`). | +| `options.userVerification` | `string` | User verification requirement. Default: `'required'`. | ##### Return Value -`Registration.Response` +`Registration.Response & { aaguid: string }` + +Includes the verified credential, signature counter, and the credential's +authenticator AAGUID. + +### `webauthx/server` + +Looks up friendly authenticator metadata from the community-maintained AAGUID +registry. By default, the registry is fetched from the upstream combined JSON +file and cached in-memory for subsequent lookups. + +##### Usage + +```ts +import { Aaguid } from 'webauthx/server' + +const info = await Aaguid.lookup({ + id: '08987058-cadc-4b81-b6e1-30de50dcbe96', +}) +``` + +##### `Aaguid.extract` + +Extracts the AAGUID from a serialized registration credential. + +```ts +const aaguid = Aaguid.extract(credential) +``` + +Returns `string | undefined`. + +##### `Aaguid.lookup` + +Looks up authenticator metadata by AAGUID. + +```ts +const info = await Aaguid.lookup({ id: aaguid }) +``` + +Returns `Aaguid.Info | null`. + +Parameters: + +| Parameter | Type | Description | +| ------------ | -------------- | ------------------------------------------------------------------------ | +| `cache` | `boolean` | Reuse an in-memory cache for the selected `remoteList`. Default: `true`. | +| `fetchFn` | `typeof fetch` | Custom fetch implementation. | +| `id` | `string` | AAGUID to resolve. | +| `remoteList` | `string` | Override the remote registry URL. Defaults to `Aaguid.remoteList`. | + +##### `Aaguid.Info` + +```ts +type Info = { + name: string + iconLight?: string | undefined + iconDark?: string | undefined +} +``` ## License diff --git a/examples/hono/README.md b/examples/hono/README.md index 147e914..7fc6438 100644 --- a/examples/hono/README.md +++ b/examples/hono/README.md @@ -4,4 +4,4 @@ cp .env.example .env pnpm i pnpm dev -``` \ No newline at end of file +``` diff --git a/examples/hono/package.json b/examples/hono/package.json index 0dbc355..99412ad 100644 --- a/examples/hono/package.json +++ b/examples/hono/package.json @@ -5,7 +5,7 @@ "scripts": { "dev": "vite", "build": "vite build", - "check:types": "tsc -b", + "check:types": "pnpm gen:types && tsc -b", "gen:types": "wrangler types", "postinstall": "pnpm gen:types", "preview": "pnpm build && vite preview", diff --git a/examples/hono/src/App.tsx b/examples/hono/src/App.tsx index 653493c..08b76e5 100644 --- a/examples/hono/src/App.tsx +++ b/examples/hono/src/App.tsx @@ -1,75 +1,118 @@ -import { useState } from 'react' +import { hc } from 'hono/client' +import { startTransition, useEffect, useEffectEvent, useState } from 'react' import { Authentication, Registration } from 'webauthx/client' +import type { AppType } from './worker/index.ts' + +const client = hc(`${import.meta.env.BASE_URL}/`) + +type Authenticator = { + iconDark?: string + iconLight?: string + name: string +} + +type Session = { + aaguid: string | null + authenticator: Authenticator | null + credentialId: string + publicKey: `0x${string}` +} + export default function App() { - const [name, setName] = useState('') - const [loggedIn, setLoggedIn] = useState(false) - const [me, setMe] = useState(null) - const [meLoading, setMeLoading] = useState(false) - const [loading, setLoading] = useState(false) + const [displayName, setDisplayName] = useState('') + const [error, setError] = useState(null) + const [isPending, setIsPending] = useState(false) + const { isRefreshing, refresh, session, setSession } = useSession() - const fetchMe = async () => { - try { - setMeLoading(true) - const [res] = await Promise.all([fetch('/me'), new Promise((r) => setTimeout(r, 200))]) - setMe(await res.json()) - } finally { - setMeLoading(false) - } - } + const isBusy = isPending || isRefreshing + const canRegister = displayName.trim().length > 0 && !isBusy async function register() { + setError(null) + setIsPending(true) + try { - setLoading(true) - const { options } = await fetch('/register/options', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ name }), - }).then((r) => r.json()) - - const credential = await Registration.create({ options }) - - await fetch('/register', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ credential }), - }).then((r) => r.json()) - - setLoggedIn(true) + const registerOptionsResponse = await client.register.options.$post({ + json: { name: displayName.trim() }, + }) + if (!registerOptionsResponse.ok) { + const errorText = await registerOptionsResponse.text() + console.error('Registration error:', errorText) + throw new Error( + `Request failed with status ${registerOptionsResponse.status}: ${errorText}`, + ) + } + const { options } = await registerOptionsResponse.json() + + // TODO: Remove cast once `ox` excludes `signal` from serialized options. + const credential = await Registration.create({ options: options as any }) + + const RegistrationResponse = await client.register.$post({ + json: { credential }, + }) + if (!RegistrationResponse.ok) { + const errorText = await RegistrationResponse.text() + console.error('Registration error:', errorText) + throw new Error(`Request failed with status ${RegistrationResponse.status}: ${errorText}`) + } + + await refresh() + setDisplayName('') + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + setError(errorMessage) } finally { - setLoading(false) + setIsPending(false) } } async function login() { - try { - setLoading(true) - - const { options } = await fetch('/authenticate/options', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({}), - }).then((r) => r.json()) + setError(null) + setIsPending(true) - const response = await Authentication.sign({ options }) - - const res = await fetch('/authenticate', { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ response }), + try { + const authenticateOptionsResponse = await client.authenticate.options.$post({ + json: {}, }) - if (!res.ok) throw new Error((await res.json()).error) + if (!authenticateOptionsResponse.ok) { + const errorText = await authenticateOptionsResponse.text() + console.error('Authentication error:', errorText) + throw new Error( + `Request failed with status ${authenticateOptionsResponse.status}: ${errorText}`, + ) + } + const { options } = await authenticateOptionsResponse.json() + // TODO: Remove cast once `ox` excludes `signal` from serialized options. + const response = await Authentication.sign({ options: options as any }) - setLoggedIn(true) + await client.authenticate.$post({ + json: { response }, + }) + await refresh() + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + setError(errorMessage) } finally { - setLoading(false) + setIsPending(false) } } async function logout() { - await fetch('/logout', { method: 'POST' }) - setLoggedIn(false) - setMe(null) + setError(null) + setIsPending(true) + + try { + await client.logout.$post() + startTransition(() => { + setSession(null) + }) + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error) + setError(errorMessage) + } finally { + setIsPending(false) + } } return ( @@ -77,34 +120,121 @@ export default function App() {

webauthx + Hono

- {loggedIn ? ( - + {session ? ( + ) : ( <> setName(e.target.value)} - placeholder="username" - value={name} + disabled={isBusy} + onChange={(event) => setDisplayName(event.target.value)} + placeholder="display name" + value={displayName} /> - -
- +
+ + +
)}
+ {error ? ( +
+
Error
+
{error}
+
+ ) : null} + + {session ? : null} +
-
Test authenticated route
- - {me ?
{JSON.stringify(me, null, 2)}
: null} + {session ? ( +
{JSON.stringify(session, null, 2)}
+ ) : ( +
{'null'}
+ )} +
+ + ) +} + +function PasskeyCard({ session }: { session: Session }) { + const icon = session.authenticator?.iconLight ?? session.authenticator?.iconDark ?? null + const name = session.authenticator?.name ?? 'Unknown authenticator' + + return ( +
+
Current passkey
+
+ {icon ? ( + {`${name} + ) : ( +
PK
+ )} +
+
{name}
+
+ {session.aaguid ? `AAGUID ${session.aaguid}` : 'Authenticator unavailable'} +
+
+ + +
+ ) +} + +function Detail({ label, value }: { label: string; value: string }) { + return ( +
+ {label} + {value}
) } + +function useSession() { + const [isRefreshing, setIsRefreshing] = useState(false) + const [session, setSession] = useState(null) + + const refresh = useEffectEvent(async () => { + setIsRefreshing(true) + + try { + const sessionResponse = await client.me.$get() + if (!sessionResponse.ok) { + const errorText = await sessionResponse.text() + console.error('Session refresh error:', errorText) + throw new Error(`Request failed with status ${sessionResponse.status}: ${errorText}`) + } + const session = await sessionResponse.json() + + startTransition(() => { + setSession(session) + }) + } finally { + setIsRefreshing(false) + } + }) + + useEffect(() => { + void refresh() + }, []) + + return { + isRefreshing, + refresh, + session, + setSession, + } +} diff --git a/examples/hono/src/index.css b/examples/hono/src/index.css index bde0538..1adce5a 100644 --- a/examples/hono/src/index.css +++ b/examples/hono/src/index.css @@ -117,6 +117,72 @@ button.logout:hover { margin: 4px 0; } +.passkey { + align-items: center; + display: flex; + gap: 12px; +} + +.passkey-copy { + display: flex; + flex: 1; + flex-direction: column; + gap: 2px; + min-width: 0; +} + +.passkey-title { + color: #f2f2f2; + font-size: 13px; +} + +.passkey-meta { + color: #666; + font-size: 11px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.passkey-icon { + background: #fff; + border: 1px solid #2a2a2a; + border-radius: 999px; + display: grid; + flex: none; + height: 36px; + object-fit: cover; + place-items: center; + width: 36px; +} + +.passkey-icon-fallback { + background: #171717; + color: #888; + font-size: 10px; + letter-spacing: 0.08em; +} + +.details { + display: flex; + flex-direction: column; + gap: 4px; +} + +.details span { + color: #555; + font-size: 10px; + letter-spacing: 0.05em; + text-transform: uppercase; +} + +.details code { + color: #9a9a9a; + font-family: inherit; + font-size: 12px; + overflow-wrap: anywhere; +} + pre { background: #0a0a0a; border-radius: 4px; diff --git a/examples/hono/src/worker/index.ts b/examples/hono/src/worker/index.ts index b5c006c..9c3e8f7 100644 --- a/examples/hono/src/worker/index.ts +++ b/examples/hono/src/worker/index.ts @@ -3,10 +3,13 @@ import { Hono } from 'hono' import { deleteCookie, getSignedCookie, setSignedCookie } from 'hono/cookie' import { createMiddleware } from 'hono/factory' import type { CookieOptions } from 'hono/utils/cookie' -import { Authentication, Registration } from 'webauthx/server' +import { Authentication, Registration, Aaguid } from 'webauthx/server' import { z } from 'zod' -const app = new Hono<{ Bindings: Env }>() +type CredentialRecord = { + aaguid?: string | undefined + publicKey: `0x${string}` +} const cookie = { challenge: { @@ -27,118 +30,132 @@ const cookie = { const auth = createMiddleware<{ Bindings: Env - Variables: { credentialId: string; publicKey: string } + Variables: { credential: CredentialRecord; credentialId: string } }>(async (c, next) => { const credentialId = await getSignedCookie(c, c.env.SECRET_KEY, 'session') if (!credentialId) return c.json({ error: 'Not authenticated' }, 401) + const publicKey = await c.env.AUTH_KV.get(`credential:${credentialId}`) if (!publicKey) return c.json({ error: 'Unknown credential' }, 401) + c.set('credential', JSON.parse(publicKey)) c.set('credentialId', credentialId) - c.set('publicKey', publicKey) await next() }) -app.post( - '/register/options', - zValidator('json', z.object({ name: z.string().min(1).max(64) })), - async (c) => { - const { name } = c.req.valid('json') - - const { challenge, options } = Registration.getOptions({ - name, - rp: { id: c.env.RP_ID, name: 'webauthx Demo' }, - }) - - await setSignedCookie(c, 'challenge', challenge, c.env.SECRET_KEY, cookie.challenge) - - return c.json({ options }) - }, -) - -app.post( - '/register', - zValidator('json', z.object({ credential: z.custom() })), - async (c) => { - const { credential } = c.req.valid('json') - - const challenge = (await getSignedCookie(c, c.env.SECRET_KEY, 'challenge')) as - | `0x${string}` - | false - deleteCookie(c, 'challenge', { path: '/' }) - if (!challenge) return c.json({ error: 'Invalid or expired challenge' }, 400) - - const result = Registration.verify(credential, { - challenge, - origin: c.env.ORIGIN, - rpId: c.env.RP_ID, - }) - - await c.env.AUTH_KV.put(`credential:${result.credential.id}`, result.credential.publicKey) - - await setSignedCookie(c, 'session', result.credential.id, c.env.SECRET_KEY, cookie.session) - - return c.json({ - id: result.credential.id, - publicKey: result.credential.publicKey, - }) - }, -) - -app.post( - '/authenticate/options', - zValidator('json', z.object({ credentialId: z.string().max(1024).optional() })), - async (c) => { - const { credentialId } = c.req.valid('json') - - const { challenge, options } = Authentication.getOptions({ - ...(credentialId ? { credentialId } : {}), - rpId: c.env.RP_ID, - }) - - await setSignedCookie(c, 'challenge', challenge, c.env.SECRET_KEY, cookie.challenge) - - return c.json({ options }) - }, -) +const app = new Hono<{ Bindings: Env }>() + .post( + '/register/options', + zValidator('json', z.object({ name: z.string().min(1).max(64) })), + async (c) => { + const { name } = c.req.valid('json') + + const { challenge, options } = Registration.getOptions({ + name, + rp: { id: c.env.RP_ID, name: 'webauthx Demo' }, + }) -app.post( - '/authenticate', - zValidator('json', z.object({ response: z.custom() })), - async (c) => { - const { response } = c.req.valid('json') + await setSignedCookie(c, 'challenge', challenge, c.env.SECRET_KEY, cookie.challenge) - const challenge = (await getSignedCookie(c, c.env.SECRET_KEY, 'challenge')) as - | `0x${string}` - | false - deleteCookie(c, 'challenge', { path: '/' }) - if (!challenge) return c.json({ error: 'Invalid or expired challenge' }, 400) + return c.json({ options }) + }, + ) + .post( + '/register', + zValidator('json', z.object({ credential: z.custom() })), + async (c) => { + const { credential } = c.req.valid('json') - const publicKey = await c.env.AUTH_KV.get(`credential:${response.id}`) - if (!publicKey) return c.json({ error: 'Unknown credential' }, 400) + const challenge = (await getSignedCookie(c, c.env.SECRET_KEY, 'challenge')) as + | `0x${string}` + | false + deleteCookie(c, 'challenge', { path: '/' }) + if (!challenge) return c.json({ error: 'Invalid or expired challenge' }, 400) - if ( - !Authentication.verify(response, { + const result = Registration.verify(credential, { challenge, origin: c.env.ORIGIN, - publicKey, rpId: c.env.RP_ID, }) - ) - return c.json({ error: 'Verification failed' }, 401) - await setSignedCookie(c, 'session', response.id, c.env.SECRET_KEY, cookie.session) + const record = { + aaguid: result.aaguid, + publicKey: result.credential.publicKey, + } satisfies CredentialRecord + await c.env.AUTH_KV.put(`credential:${result.credential.id}`, JSON.stringify(record)) - return c.json({ credentialId: response.id }) - }, -) + await setSignedCookie(c, 'session', result.credential.id, c.env.SECRET_KEY, cookie.session) -app.get('/me', auth, async (c) => { - return c.json({ credentialId: c.var.credentialId, publicKey: c.var.publicKey }) -}) + return c.json({ + authenticator: result.aaguid ? await Aaguid.lookup({ id: result.aaguid }) : null, + aaguid: result.aaguid ?? null, + id: result.credential.id, + publicKey: result.credential.publicKey, + }) + }, + ) + .post( + '/authenticate/options', + zValidator('json', z.object({ credentialId: z.string().max(1024).optional() })), + async (c) => { + const { credentialId } = c.req.valid('json') + + const { challenge, options } = Authentication.getOptions({ + credentialId, + rpId: c.env.RP_ID, + }) -app.post('/logout', auth, async (c) => { - deleteCookie(c, 'session', { path: '/' }) - return c.json({ ok: true }) -}) + await setSignedCookie(c, 'challenge', challenge, c.env.SECRET_KEY, cookie.challenge) + + return c.json({ options }) + }, + ) + .post( + '/authenticate', + zValidator('json', z.object({ response: z.custom() })), + async (c) => { + const { response } = c.req.valid('json') + + const challenge = (await getSignedCookie(c, c.env.SECRET_KEY, 'challenge')) as + | `0x${string}` + | false + deleteCookie(c, 'challenge', { path: '/' }) + if (!challenge) return c.json({ error: 'Invalid or expired challenge' }, 400) + + const value = await c.env.AUTH_KV.get(`credential:${response.id}`) + if (!value) return c.json({ error: 'Unknown credential' }, 400) + + const credential = JSON.parse(value) as CredentialRecord + if (!credential) return c.json({ error: 'Unknown credential' }, 400) + + if ( + !Authentication.verify(response, { + challenge, + origin: c.env.ORIGIN, + publicKey: credential.publicKey, + rpId: c.env.RP_ID, + }) + ) + return c.json({ error: 'Verification failed' }, 401) + + await setSignedCookie(c, 'session', response.id, c.env.SECRET_KEY, cookie.session) + + return c.json({ credentialId: response.id }) + }, + ) + .get('/me', auth, async (c) => { + const aaguid = c.var.credential.aaguid ?? null + return c.json({ + aaguid, + authenticator: aaguid ? await Aaguid.lookup({ id: aaguid }) : null, + credentialId: c.var.credentialId, + publicKey: c.var.credential.publicKey, + }) + }) + .post('/logout', auth, async (c) => { + deleteCookie(c, 'session', { path: '/' }) + return c.json({ ok: true }) + }) + +export type AppType = typeof app -export default app +export default app satisfies ExportedHandler diff --git a/examples/hono/tsconfig.app.json b/examples/hono/tsconfig.app.json index f6f9879..89692ee 100644 --- a/examples/hono/tsconfig.app.json +++ b/examples/hono/tsconfig.app.json @@ -1,8 +1,8 @@ { "compilerOptions": { "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo", - "target": "ES2020", - "lib": ["ES2020", "DOM", "DOM.Iterable"], + "target": "esnext", + "lib": ["esnext", "DOM", "DOM.Iterable"], "module": "ESNext", "moduleResolution": "bundler", "jsx": "react-jsx", @@ -10,8 +10,9 @@ "skipLibCheck": true, "isolatedModules": true, "moduleDetection": "force", - "noEmit": true + "noEmit": true, + "allowImportingTsExtensions": true }, - "include": ["src"], + "include": ["src", "./worker-configuration.d.ts"], "exclude": ["src/worker"] } diff --git a/examples/hono/tsconfig.node.json b/examples/hono/tsconfig.node.json index 8239cd1..63d6432 100644 --- a/examples/hono/tsconfig.node.json +++ b/examples/hono/tsconfig.node.json @@ -1,8 +1,8 @@ { "compilerOptions": { "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo", - "target": "ES2022", - "lib": ["ES2023"], + "target": "ES2024", + "lib": ["ES2024"], "module": "ESNext", "moduleResolution": "bundler", "strict": true, diff --git a/examples/hono/wrangler.jsonc b/examples/hono/wrangler.jsonc index a9b9a0d..6b46476 100644 --- a/examples/hono/wrangler.jsonc +++ b/examples/hono/wrangler.jsonc @@ -2,7 +2,7 @@ "$schema": "node_modules/wrangler/config-schema.json", "name": "webauthx-hono-example", "main": "./src/worker/index.ts", - "compatibility_date": "2025-01-01", + "compatibility_date": "2026-04-01", "assets": { "directory": "./dist/client", "not_found_handling": "single-page-application", @@ -10,6 +10,7 @@ "vars": { "ORIGIN": "http://localhost:5173", "RP_ID": "localhost", + "SECRET_KEY": "", }, "kv_namespaces": [ { diff --git a/package.json b/package.json index 19b4148..6c9fa57 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "changeset:publish": "zile publish:prepare && changeset publish && zile publish:post", "changeset:version": "changeset version", "check": "oxlint --fix --ignore-pattern package.json && oxfmt", - "check:types": "tsc -b", + "check:types": "tsc -b && pnpm --filter='*' --parallel --stream check:types", "dev": "zile dev", "test": "vitest" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a3eeba4..866c4fb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,28 +8,28 @@ catalogs: default: '@changesets/cli': specifier: latest - version: 2.29.8 + version: 2.30.0 '@types/node': specifier: latest - version: 25.3.0 + version: 25.5.2 '@vitest/browser-playwright': specifier: ^4.0.18 - version: 4.0.18 + version: 4.1.2 oxfmt: specifier: latest - version: 0.34.0 + version: 0.44.0 oxlint: specifier: latest - version: 1.49.0 + version: 1.59.0 typescript: - specifier: latest + specifier: ^5 version: 5.9.3 vitest: specifier: latest - version: 4.0.18 + version: 4.1.2 zile: specifier: latest - version: 0.0.21 + version: 0.0.24 overrides: '@vitest/browser>vitest': latest @@ -42,41 +42,41 @@ importers: dependencies: ox: specifier: ~0.14.11 - version: 0.14.11(typescript@5.9.3)(zod@4.3.6) + version: 0.14.15(typescript@5.9.3)(zod@4.3.6) devDependencies: '@changesets/cli': specifier: 'catalog:' - version: 2.29.8(@types/node@25.3.0) + version: 2.30.0(@types/node@25.5.2) '@types/node': specifier: 'catalog:' - version: 25.3.0 + version: 25.5.2 '@vitest/browser-playwright': specifier: 'catalog:' - version: 4.0.18(playwright@1.58.2)(vite@7.3.1(@types/node@25.3.0))(vitest@4.0.18) + version: 4.1.2(playwright@1.59.1)(vite@6.4.2(@types/node@25.5.2))(vitest@4.1.2) oxfmt: specifier: 'catalog:' - version: 0.34.0 + version: 0.44.0 oxlint: specifier: 'catalog:' - version: 1.49.0 + version: 1.59.0 typescript: specifier: 'catalog:' version: 5.9.3 vitest: specifier: 'catalog:' - version: 4.0.18(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18) + version: 4.1.2(@types/node@25.5.2)(@vitest/browser-playwright@4.1.2)(vite@6.4.2(@types/node@25.5.2)) zile: specifier: 'catalog:' - version: 0.0.21(typescript@5.9.3) + version: 0.0.24(typescript@5.9.3) examples/hono: dependencies: '@hono/zod-validator': specifier: ^0.7.6 - version: 0.7.6(hono@4.12.0)(zod@4.3.6) + version: 0.7.6(hono@4.12.12)(zod@4.3.6) hono: specifier: ^4 - version: 4.12.0 + version: 4.12.12 react: specifier: ^19 version: 19.2.4 @@ -92,7 +92,7 @@ importers: devDependencies: '@cloudflare/vite-plugin': specifier: ^1 - version: 1.25.2(vite@6.4.1(@types/node@25.3.0))(workerd@1.20260219.0)(wrangler@4.67.0) + version: 1.31.0(vite@6.4.2(@types/node@25.5.2))(workerd@1.20260401.1)(wrangler@4.80.0) '@types/react': specifier: ^19 version: 19.2.14 @@ -101,16 +101,16 @@ importers: version: 19.2.3(@types/react@19.2.14) '@vitejs/plugin-react': specifier: ^5 - version: 5.1.4(vite@6.4.1(@types/node@25.3.0)) + version: 5.2.0(vite@6.4.2(@types/node@25.5.2)) typescript: specifier: ^5 version: 5.9.3 vite: specifier: ^6 - version: 6.4.1(@types/node@25.3.0) + version: 6.4.2(@types/node@25.5.2) wrangler: specifier: ^4 - version: 4.67.0 + version: 4.80.0 packages: @@ -167,12 +167,12 @@ packages: resolution: {integrity: sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.28.6': - resolution: {integrity: sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==} + '@babel/helpers@7.29.2': + resolution: {integrity: sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==} engines: {node: '>=6.9.0'} - '@babel/parser@7.29.0': - resolution: {integrity: sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==} + '@babel/parser@7.29.2': + resolution: {integrity: sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==} engines: {node: '>=6.0.0'} hasBin: true @@ -188,8 +188,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/runtime@7.28.6': - resolution: {integrity: sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==} + '@babel/runtime@7.29.2': + resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} engines: {node: '>=6.9.0'} '@babel/template@7.28.6': @@ -204,8 +204,11 @@ packages: resolution: {integrity: sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==} engines: {node: '>=6.9.0'} - '@changesets/apply-release-plan@7.0.14': - resolution: {integrity: sha512-ddBvf9PHdy2YY0OUiEl3TV78mH9sckndJR14QAt87KLEbIov81XO0q0QAmvooBxXlqRRP8I9B7XOzZwQG7JkWA==} + '@blazediff/core@1.9.1': + resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==} + + '@changesets/apply-release-plan@7.1.0': + resolution: {integrity: sha512-yq8ML3YS7koKQ/9bk1PqO0HMzApIFNwjlwCnwFEXMzNe8NpzeeYYKCmnhWJGkN8g7E51MnWaSbqRcTcdIxUgnQ==} '@changesets/assemble-release-plan@6.0.9': resolution: {integrity: sha512-tPgeeqCHIwNo8sypKlS3gOPmsS3wP0zHt67JDuL20P4QcXiw/O4Hl7oXiuLnP9yg+rXLQ2sScdV1Kkzde61iSQ==} @@ -213,12 +216,12 @@ packages: '@changesets/changelog-git@0.2.1': resolution: {integrity: sha512-x/xEleCFLH28c3bQeQIyeZf8lFXyDFVn1SgcBiR2Tw/r4IAWlk1fzxCEZ6NxQAjF2Nwtczoen3OA2qR+UawQ8Q==} - '@changesets/cli@2.29.8': - resolution: {integrity: sha512-1weuGZpP63YWUYjay/E84qqwcnt5yJMM0tep10Up7Q5cS/DGe2IZ0Uj3HNMxGhCINZuR7aO9WBMdKnPit5ZDPA==} + '@changesets/cli@2.30.0': + resolution: {integrity: sha512-5D3Nk2JPqMI1wK25pEymeWRSlSMdo5QOGlyfrKg0AOufrUcjEE3RQgaCpHoBiM31CSNrtSgdJ0U6zL1rLDDfBA==} hasBin: true - '@changesets/config@3.1.2': - resolution: {integrity: sha512-CYiRhA4bWKemdYi/uwImjPxqWNpqGPNbEBdX1BdONALFIDK7MCUj6FPkzD+z9gJcvDFUQJn9aDVf4UG7OT6Kog==} + '@changesets/config@3.1.3': + resolution: {integrity: sha512-vnXjcey8YgBn2L1OPWd3ORs0bGC4LoYcK/ubpgvzNVr53JXV5GiTVj7fWdMRsoKUH7hhhMAQnsJUqLr21EncNw==} '@changesets/errors@0.2.0': resolution: {integrity: sha512-6BLOQUscTpZeGljvyQXlWOItQyU71kCdGz7Pi8H8zdw6BI0g3m43iL4xKUVPWtG+qrrL9DTjpdn8eYuCQSRpow==} @@ -226,8 +229,8 @@ packages: '@changesets/get-dependents-graph@2.1.3': resolution: {integrity: sha512-gphr+v0mv2I3Oxt19VdWRRUxq3sseyUpX9DaHpTUmLj92Y10AGy+XOtV+kbM6L/fDcpx7/ISDFK6T8A/P3lOdQ==} - '@changesets/get-release-plan@4.0.14': - resolution: {integrity: sha512-yjZMHpUHgl4Xl5gRlolVuxDkm4HgSJqT93Ri1Uz8kGrQb+5iJ8dkXJ20M2j/Y4iV5QzS2c5SeTxVSKX+2eMI0g==} + '@changesets/get-release-plan@4.0.15': + resolution: {integrity: sha512-Q04ZaRPuEVZtA+auOYgFaVQQSA98dXiVe/yFaZfY7hoSmQICHGvP0TF4u3EDNHWmmCS4ekA/XSpKlSM2PyTS2g==} '@changesets/get-version-range-type@0.4.0': resolution: {integrity: sha512-hwawtob9DryoGTpixy1D3ZXbGgJu1Rhr+ySH2PvTLHvkZuQ7sRT4oQwMh0hbqZH1weAooedEjRsbrWcGLCeyVQ==} @@ -238,14 +241,14 @@ packages: '@changesets/logger@0.1.1': resolution: {integrity: sha512-OQtR36ZlnuTxKqoW4Sv6x5YIhOmClRd5pWsjZsddYxpWs517R0HkyiefQPIytCVh4ZcC5x9XaG8KTdd5iRQUfg==} - '@changesets/parse@0.4.2': - resolution: {integrity: sha512-Uo5MC5mfg4OM0jU3up66fmSn6/NE9INK+8/Vn/7sMVcdWg46zfbvvUSjD9EMonVqPi9fbrJH9SXHn48Tr1f2yA==} + '@changesets/parse@0.4.3': + resolution: {integrity: sha512-ZDmNc53+dXdWEv7fqIUSgRQOLYoUom5Z40gmLgmATmYR9NbL6FJJHwakcCpzaeCy+1D0m0n7mT4jj2B/MQPl7A==} '@changesets/pre@2.0.2': resolution: {integrity: sha512-HaL/gEyFVvkf9KFg6484wR9s0qjAXlZ8qWPDkTyKF6+zqjBe/I2mygg3MbpZ++hdi0ToqNUF8cjj7fBy0dg8Ug==} - '@changesets/read@0.6.6': - resolution: {integrity: sha512-P5QaN9hJSQQKJShzzpBT13FzOSPyHbqdoIBUd2DJdgvnECCyO6LmAOWSV+O8se2TaZJVwSXjL+v9yhb+a9JeJg==} + '@changesets/read@0.6.7': + resolution: {integrity: sha512-D1G4AUYGrBEk8vj8MGwf75k9GpN6XL3wg8i42P2jZZwFLXnlr2Pn7r9yuQNbaMCarP7ZQWNJbV6XLeysAIMhTA==} '@changesets/should-skip-package@0.1.2': resolution: {integrity: sha512-qAK/WrqWLNCP22UDdBTMPH5f41elVDlsNyat180A33dWxuUDyNpg6fPi/FyTZwRriVjg0L8gnjJn2F9XAoF0qw==} @@ -259,57 +262,57 @@ packages: '@changesets/write@0.4.0': resolution: {integrity: sha512-CdTLvIOPiCNuH71pyDu3rA+Q0n65cmAbXnwWH84rKGiFumFzkmHNT8KHTMEchcxN+Kl8I54xGUhJ7l3E7X396Q==} - '@clack/core@1.0.1': - resolution: {integrity: sha512-WKeyK3NOBwDOzagPR5H08rFk9D/WuN705yEbuZvKqlkmoLM2woKtXb10OO2k1NoSU4SFG947i2/SCYh+2u5e4g==} + '@clack/core@1.2.0': + resolution: {integrity: sha512-qfxof/3T3t9DPU/Rj3OmcFyZInceqj/NVtO9rwIuJqCUgh32gwPjpFQQp/ben07qKlhpwq7GzfWpST4qdJ5Drg==} - '@clack/prompts@1.0.1': - resolution: {integrity: sha512-/42G73JkuYdyWZ6m8d/CJtBrGl1Hegyc7Fy78m5Ob+jF85TOUmLR5XLce/U3LxYAw0kJ8CT5aI99RIvPHcGp/Q==} + '@clack/prompts@1.2.0': + resolution: {integrity: sha512-4jmztR9fMqPMjz6H/UZXj0zEmE43ha1euENwkckKKel4XpSfokExPo5AiVStdHSAlHekz4d0CA/r45Ok1E4D3w==} '@cloudflare/kv-asset-handler@0.4.2': resolution: {integrity: sha512-SIOD2DxrRRwQ+jgzlXCqoEFiKOFqaPjhnNTGKXSRLvp1HiOvapLaFG2kEr9dYQTYe8rKrd9uvDUzmAITeNyaHQ==} engines: {node: '>=18.0.0'} - '@cloudflare/unenv-preset@2.14.0': - resolution: {integrity: sha512-XKAkWhi1nBdNsSEoNG9nkcbyvfUrSjSf+VYVPfOto3gLTZVc3F4g6RASCMh6IixBKCG2yDgZKQIHGKtjcnLnKg==} + '@cloudflare/unenv-preset@2.16.0': + resolution: {integrity: sha512-8ovsRpwzPoEqPUzoErAYVv8l3FMZNeBVQfJTvtzP4AgLSRGZISRfuChFxHWUQd3n6cnrwkuTGxT+2cGo8EsyYg==} peerDependencies: unenv: 2.0.0-rc.24 - workerd: ^1.20260218.0 + workerd: 1.20260301.1 || ~1.20260302.1 || ~1.20260303.1 || ~1.20260304.1 || >1.20260305.0 <2.0.0-0 peerDependenciesMeta: workerd: optional: true - '@cloudflare/vite-plugin@1.25.2': - resolution: {integrity: sha512-CU4eRPZRumFkcsfinmr8txVCvGVwYEpR5/ATk2p3/004EdFKV4rZ0XzXhLlWVD1XppQ4DAc9FLjFJK4na7QF1A==} + '@cloudflare/vite-plugin@1.31.0': + resolution: {integrity: sha512-wkIoqOTVltHMsl8Zpt2bcndbdf+w7czICJ8SbxQq+VzvGprf8glJt5y7iyMCj9YeofkUdsR6AlyTZvZ8kpx0FQ==} peerDependencies: - vite: ^6.1.0 || ^7.0.0 - wrangler: ^4.67.0 + vite: ^6.1.0 || ^7.0.0 || ^8.0.0 + wrangler: ^4.80.0 - '@cloudflare/workerd-darwin-64@1.20260219.0': - resolution: {integrity: sha512-k+xM+swQBQnkrvwobjRPxyeYwjLSludJusR0PqeHe+h6X9QIRGgw3s1AO38lXQsqzMSgG5709oOXSF19NKVVaQ==} + '@cloudflare/workerd-darwin-64@1.20260401.1': + resolution: {integrity: sha512-ZSmceM70jH6k+/62VkEcmMNzrpr4kSctkX5Lsgqv38KktfhPY/hsh75y1lRoPWS3H3kgMa4p2pUSlidZR1u2hw==} engines: {node: '>=16'} cpu: [x64] os: [darwin] - '@cloudflare/workerd-darwin-arm64@1.20260219.0': - resolution: {integrity: sha512-EyfQdsG1KcIVAf4qndT00LZly7sLFm1VxMWHBvOFB/EVYF2sE5HZ0dPbe+yrax5p3eS0oLZthR8ynhz4UulMUQ==} + '@cloudflare/workerd-darwin-arm64@1.20260401.1': + resolution: {integrity: sha512-7UKWF+IUZ3NXMVPsDg8Cjg0r58b+uYlfvs5Yt8bvtU+geCtW4P2MxRHmRSEo8SryckXOJjb/b8tcncgCykFu8g==} engines: {node: '>=16'} cpu: [arm64] os: [darwin] - '@cloudflare/workerd-linux-64@1.20260219.0': - resolution: {integrity: sha512-N0UHXILYYa6htFO/uC92uAqusvynbSbOcHcrVXMKqP9Jy7eqXGMovyKIrNgzYnKIszNB+0lfUYdGI3Wci07LuA==} + '@cloudflare/workerd-linux-64@1.20260401.1': + resolution: {integrity: sha512-MDWUH/0bvL/l9aauN8zEddyYOXId1OueqrUCXXENNJ95R/lSmF6OgGVuXaYhoIhxQkNiEJ/0NOlnVYj9mJq4dw==} engines: {node: '>=16'} cpu: [x64] os: [linux] - '@cloudflare/workerd-linux-arm64@1.20260219.0': - resolution: {integrity: sha512-835pjQ9uuAtwPBOAkPf+oxH3mNE5mqWuE3H7hJsul7WZsRD2FDcariyoT2AW6xyOePILrn4uMnmG1KGc9m/8Pg==} + '@cloudflare/workerd-linux-arm64@1.20260401.1': + resolution: {integrity: sha512-UgkzpMzVWM/bwbo3vjCTg2aoKfGcUhiEoQoDdo6RGWvbHRJyLVZ4VQCG9ZcISiztkiS2ICCoYOtPy6M/lV6Gcw==} engines: {node: '>=16'} cpu: [arm64] os: [linux] - '@cloudflare/workerd-windows-64@1.20260219.0': - resolution: {integrity: sha512-i7qcuOsuAxqqn1n5Ar3Rh1dHUL9vNmpF9FcdMTT84jIrdm5UNrPZz5grJthPmpB9LTcreT9iiP6qKbzGjnCwPA==} + '@cloudflare/workerd-windows-64@1.20260401.1': + resolution: {integrity: sha512-HBLzcQF5iF4Qv20tQ++pG7xs3OsCnaIbc+GAi6fmhUKZhvmzvml/jwrQzLJ+MPm0cQo41K5OO/U3T4S8tvJetQ==} engines: {node: '>=16'} cpu: [x64] os: [win32] @@ -318,8 +321,8 @@ packages: resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@emnapi/runtime@1.8.1': - resolution: {integrity: sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==} + '@emnapi/runtime@1.9.2': + resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==} '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} @@ -639,8 +642,8 @@ packages: hono: '>=3.9.0' zod: ^3.25.0 || ^4.0.0 - '@img/colour@1.0.0': - resolution: {integrity: sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==} + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} engines: {node: '>=18'} '@img/sharp-darwin-arm64@0.34.5': @@ -850,246 +853,246 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@oxfmt/binding-android-arm-eabi@0.34.0': - resolution: {integrity: sha512-sqkqjh/Z38l+duOb1HtVqJTAj1grt2ttkobCopC/72+a4Xxz4xUgZPFyQ4HxrYMvyqO/YA0tvM1QbfOu70Gk1Q==} + '@oxfmt/binding-android-arm-eabi@0.44.0': + resolution: {integrity: sha512-5UvghMd9SA/yvKTWCAxMAPXS1d2i054UeOf4iFjZjfayTwCINcC3oaSXjtbZfCaEpxgJod7XiOjTtby5yEv/BQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxfmt/binding-android-arm64@0.34.0': - resolution: {integrity: sha512-1KRCtasHcVcGOMwfOP9d5Bus2NFsN8yAYM5cBwi8LBg5UtXC3C49WHKrlEa8iF1BjOS6CR2qIqiFbGoA0DJQNQ==} + '@oxfmt/binding-android-arm64@0.44.0': + resolution: {integrity: sha512-IVudM1BWfvrYO++Khtzr8q9n5Rxu7msUvoFMqzGJVdX7HfUXUDHwaH2zHZNB58svx2J56pmCUzophyaPFkcG/A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxfmt/binding-darwin-arm64@0.34.0': - resolution: {integrity: sha512-b+Rmw9Bva6e/7PBES2wLO8sEU7Mi0+/Kv+pXSe/Y8i4fWNftZZlGwp8P01eECaUqpXATfSgNxdEKy7+ssVNz7g==} + '@oxfmt/binding-darwin-arm64@0.44.0': + resolution: {integrity: sha512-eWCLAIKAHfx88EqEP1Ga2yz7qVcqDU5lemn4xck+07bH182hDdprOHjbogyk0In1Djys3T0/pO2JepFnRJ41Mg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxfmt/binding-darwin-x64@0.34.0': - resolution: {integrity: sha512-QGjpevWzf1T9COEokZEWt80kPOtthW1zhRbo7x4Qoz646eTTfi6XsHG2uHeDWJmTbgBoJZPMgj2TAEV/ppEZaA==} + '@oxfmt/binding-darwin-x64@0.44.0': + resolution: {integrity: sha512-eHTBznHLM49++dwz07MblQ2cOXyIgeedmE3Wgy4ptUESj38/qYZyRi1MPwC9olQJWssMeY6WI3UZ7YmU5ggvyQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxfmt/binding-freebsd-x64@0.34.0': - resolution: {integrity: sha512-VMSaC02cG75qL59M9M/szEaqq/RsLfgpzQ4nqUu8BUnX1zkiZIW2gTpUv3ZJ6qpWnHxIlAXiRZjQwmcwpvtbcg==} + '@oxfmt/binding-freebsd-x64@0.44.0': + resolution: {integrity: sha512-jLMmbj0u0Ft43QpkUVr/0v1ZfQCGWAvU+WznEHcN3wZC/q6ox7XeSJtk9P36CCpiDSUf3sGnzbIuG1KdEMEDJQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxfmt/binding-linux-arm-gnueabihf@0.34.0': - resolution: {integrity: sha512-Klm367PFJhH6vYK3vdIOxFepSJZHPaBfIuqwxdkOcfSQ4qqc/M8sgK0UTFnJWWTA/IkhMIh1kW6uEqiZ/xtQqg==} + '@oxfmt/binding-linux-arm-gnueabihf@0.44.0': + resolution: {integrity: sha512-n+A/u/ByK1qV8FVGOwyaSpw5NPNl0qlZfgTBqHeGIqr8Qzq1tyWZ4lAaxPoe5mZqE3w88vn3+jZtMxriHPE7tg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm-musleabihf@0.34.0': - resolution: {integrity: sha512-nqn0QueVXRfbN9m58/E9Zij0Ap8lzayx591eWBYn0sZrGzY1IRv9RYS7J/1YUXbb0Ugedo0a8qIWzUHU9bWQuA==} + '@oxfmt/binding-linux-arm-musleabihf@0.44.0': + resolution: {integrity: sha512-5eax+FkxyCqAi3Rw0mrZFr7+KTt/XweFsbALR+B5ljWBLBl8nHe4ADrUnb1gLEfQCJLl+Ca5FIVD4xEt95AwIw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxfmt/binding-linux-arm64-gnu@0.34.0': - resolution: {integrity: sha512-DDn+dcqW+sMTCEjvLoQvC/VWJjG7h8wcdN/J+g7ZTdf/3/Dx730pQElxPPGsCXPhprb11OsPyMp5FwXjMY3qvA==} + '@oxfmt/binding-linux-arm64-gnu@0.44.0': + resolution: {integrity: sha512-58l8JaHxSGOmOMOG2CIrNsnkRJAj0YcHQCmvNACniOa/vd1iRHhlPajczegzS5jwMENlqgreyiTR9iNlke8qCw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-arm64-musl@0.34.0': - resolution: {integrity: sha512-H+F8+71gHQoGTFPPJ6z4dD0Fzfzi0UP8Zx94h5kUmIFThLvMq5K1Y/bUUubiXwwHfwb5C3MPjUpYijiy0rj51Q==} + '@oxfmt/binding-linux-arm64-musl@0.44.0': + resolution: {integrity: sha512-AlObQIXyVRZ96LbtVljtFq0JqH5B92NU+BQeDFrXWBUWlCKAM0wF5GLfIhCLT5kQ3Sl+U0YjRJ7Alqj5hGQaCg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-ppc64-gnu@0.34.0': - resolution: {integrity: sha512-dIGnzTNhCXqQD5pzBwduLg8pClm+t8R53qaE9i5h8iua1iaFAJyLffh4847CNZSlASb7gn1Ofuv7KoG/EpoGZg==} + '@oxfmt/binding-linux-ppc64-gnu@0.44.0': + resolution: {integrity: sha512-YcFE8/q/BbrCiIiM5piwbkA6GwJc5QqhMQp2yDrqQ2fuVkZ7CInb1aIijZ/k8EXc72qXMSwKpVlBv1w/MsGO/A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-gnu@0.34.0': - resolution: {integrity: sha512-FGQ2GTTooilDte/ogwWwkHuuL3lGtcE3uKM2EcC7kOXNWdUfMY6Jx3JCodNVVbFoybv4A+HuCj8WJji2uu1Ceg==} + '@oxfmt/binding-linux-riscv64-gnu@0.44.0': + resolution: {integrity: sha512-eOdzs6RqkRzuqNHUX5C8ISN5xfGh4xDww8OEd9YAmc3OWN8oAe5bmlIqQ+rrHLpv58/0BuU48bxkhnIGjA/ATQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-riscv64-musl@0.34.0': - resolution: {integrity: sha512-2dGbGneJ7ptOIVKMwEIHdCkdZEomh74X3ggo4hCzEXL/rl9HwfsZDR15MkqfQqAs6nVXMvtGIOMxjDYa5lwKaA==} + '@oxfmt/binding-linux-riscv64-musl@0.44.0': + resolution: {integrity: sha512-YBgNTxntD/QvlFUfgvh8bEdwOhXiquX8gaofZJAwYa/Xp1S1DQrFVZEeck7GFktr24DztsSp8N8WtWCBwxs0Hw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxfmt/binding-linux-s390x-gnu@0.34.0': - resolution: {integrity: sha512-cCtGgmrTrxq3OeSG0UAO+w6yLZTMeOF4XM9SAkNrRUxYhRQELSDQ/iNPCLyHhYNi38uHJQbS5RQweLUDpI4ajA==} + '@oxfmt/binding-linux-s390x-gnu@0.44.0': + resolution: {integrity: sha512-GLIh1R6WHWshl/i4QQDNgj0WtT25aRO4HNUWEoitxiywyRdhTFmFEYT2rXlcl9U6/26vhmOqG5cRlMLG3ocaIA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-gnu@0.34.0': - resolution: {integrity: sha512-7AvMzmeX+k7GdgitXp99GQoIV/QZIpAS7rwxQvC/T541yWC45nwvk4mpnU8N+V6dE5SPEObnqfhCjO80s7qIsg==} + '@oxfmt/binding-linux-x64-gnu@0.44.0': + resolution: {integrity: sha512-gZOpgTlOsLcLfAF9qgpTr7FIIFSKnQN3hDf/0JvQ4CIwMY7h+eilNjxq/CorqvYcEOu+LRt1W4ZS7KccEHLOdA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxfmt/binding-linux-x64-musl@0.34.0': - resolution: {integrity: sha512-uNiglhcmivJo1oDMh3hoN/Z0WsbEXOpRXZdQ3W/IkOpyV8WF308jFjSC1ZxajdcNRXWej0zgge9QXba58Owt+g==} + '@oxfmt/binding-linux-x64-musl@0.44.0': + resolution: {integrity: sha512-1CyS9JTB+pCUFYFI6pkQGGZaT/AY5gnhHVrQQLhFba6idP9AzVYm1xbdWfywoldTYvjxQJV6x4SuduCIfP3W+A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxfmt/binding-openharmony-arm64@0.34.0': - resolution: {integrity: sha512-5eFsTjCyji25j6zznzlMc+wQAZJoL9oWy576xhqd2efv+N4g1swIzuSDcb1dz4gpcVC6veWe9pAwD7HnrGjLwg==} + '@oxfmt/binding-openharmony-arm64@0.44.0': + resolution: {integrity: sha512-bmEv70Ak6jLr1xotCbF5TxIKjsmQaiX+jFRtnGtfA03tJPf6VG3cKh96S21boAt3JZc+Vjx8PYcDuLj39vM2Pw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxfmt/binding-win32-arm64-msvc@0.34.0': - resolution: {integrity: sha512-6id8kK0t5hKfbV6LHDzRO21wRTA6ctTlKGTZIsG/mcoir0rssvaYsedUymF4HDj7tbCUlnxCX/qOajKlEuqbIw==} + '@oxfmt/binding-win32-arm64-msvc@0.44.0': + resolution: {integrity: sha512-yWzB+oCpSnP/dmw85eFLAT5o35Ve5pkGS2uF/UCISpIwDqf1xa7OpmtomiqY/Vzg8VyvMbuf6vroF2khF/+1Vg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxfmt/binding-win32-ia32-msvc@0.34.0': - resolution: {integrity: sha512-QHaz+w673mlYqn9v/+fuiKZpjkmagleXQ+NygShDv8tdHpRYX2oYhTJwwt9j1ZfVhRgza1EIUW3JmzCXmtPdhQ==} + '@oxfmt/binding-win32-ia32-msvc@0.44.0': + resolution: {integrity: sha512-TcWpo18xEIE3AmIG2kpr3kz5IEhQgnx0lazl2+8L+3eTopOAUevQcmlr4nhguImNWz0OMeOZrYZOhJNCf16nlQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxfmt/binding-win32-x64-msvc@0.34.0': - resolution: {integrity: sha512-CXKQM/VaF+yuvGru8ktleHLJoBdjBtTFmAsLGePiESiTN0NjCI/PiaiOCfHMJ1HdP1LykvARUwMvgaN3tDhcrg==} + '@oxfmt/binding-win32-x64-msvc@0.44.0': + resolution: {integrity: sha512-oj8aLkPJZppIM4CMQNsyir9ybM1Xw/CfGPTSsTnzpVGyljgfbdP0EVUlURiGM0BDrmw5psQ6ArmGCcUY/yABaQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] - '@oxlint/binding-android-arm-eabi@1.49.0': - resolution: {integrity: sha512-2WPoh/2oK9r/i2R4o4J18AOrm3HVlWiHZ8TnuCaS4dX8m5ZzRmHW0I3eLxEurQLHWVruhQN7fHgZnah+ag5iQg==} + '@oxlint/binding-android-arm-eabi@1.59.0': + resolution: {integrity: sha512-etYDw/UaEv936AQUd/CRMBVd+e+XuuU6wC+VzOv1STvsTyZenLChepLWqLtnyTTp4YMlM22ypzogDDwqYxv5cg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] - '@oxlint/binding-android-arm64@1.49.0': - resolution: {integrity: sha512-YqJAGvNB11EzoKm1euVhZntb79alhMvWW/j12bYqdvVxn6xzEQWrEDCJg9BPo3A3tBCSUBKH7bVkAiCBqK/L1w==} + '@oxlint/binding-android-arm64@1.59.0': + resolution: {integrity: sha512-TgLc7XVLKH2a4h8j3vn1MDjfK33i9MY60f/bKhRGWyVzbk5LCZ4X01VZG7iHrMmi5vYbAp8//Ponigx03CLsdw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@oxlint/binding-darwin-arm64@1.49.0': - resolution: {integrity: sha512-WFocCRlvVkMhChCJ2qpJfp1Gj/IjvyjuifH9Pex8m8yHonxxQa3d8DZYreuDQU3T4jvSY8rqhoRqnpc61Nlbxw==} + '@oxlint/binding-darwin-arm64@1.59.0': + resolution: {integrity: sha512-DXyFPf5ZKldMLloRHx/B9fsxsiTQomaw7cmEW3YIJko2HgCh+GUhp9gGYwHrqlLJPsEe3dYj9JebjX92D3j3AA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@oxlint/binding-darwin-x64@1.49.0': - resolution: {integrity: sha512-BN0KniwvehbUfYztOMwEDkYoojGm/narf5oJf+/ap+6PnzMeWLezMaVARNIS0j3OdMkjHTEP8s3+GdPJ7WDywQ==} + '@oxlint/binding-darwin-x64@1.59.0': + resolution: {integrity: sha512-LgvrsdgVLX1qWqIEmNsSmMXJhpAWdtUQ0M+oR0CySwi+9IHWyOGuIL8w8+u/kbZNMyZr4WUyYB5i0+D+AKgkLg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@oxlint/binding-freebsd-x64@1.49.0': - resolution: {integrity: sha512-SnkAc/DPIY6joMCiP/+53Q+N2UOGMU6ULvbztpmvPJNF/jYPGhNbKtN982uj2Gs6fpbxYkmyj08QnpkD4fbHJA==} + '@oxlint/binding-freebsd-x64@1.59.0': + resolution: {integrity: sha512-bOJhqX/ny4hrFuTPlyk8foSRx/vLRpxJh0jOOKN2NWW6FScXHPAA5rQbrwdQPcgGB5V8Ua51RS03fke8ssBcug==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@oxlint/binding-linux-arm-gnueabihf@1.49.0': - resolution: {integrity: sha512-6Z3EzRvpQVIpO7uFhdiGhdE8Mh3S2VWKLL9xuxVqD6fzPhyI3ugthpYXlCChXzO8FzcYIZ3t1+Kau+h2NY1hqA==} + '@oxlint/binding-linux-arm-gnueabihf@1.59.0': + resolution: {integrity: sha512-vVUXxYMF9trXCsz4m9H6U0IjehosVHxBzVgJUxly1uz4W1PdDyicaBnpC0KRXsHYretLVe+uS9pJy8iM57Kujw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm-musleabihf@1.49.0': - resolution: {integrity: sha512-wdjXaQYAL/L25732mLlngfst4Jdmi/HLPVHb3yfCoP5mE3lO/pFFrmOJpqWodgv29suWY74Ij+RmJ/YIG5VuzQ==} + '@oxlint/binding-linux-arm-musleabihf@1.59.0': + resolution: {integrity: sha512-TULQW8YBPGRWg5yZpFPL54HLOnJ3/HiX6VenDPi6YfxB/jlItwSMFh3/hCeSNbh+DAMaE1Py0j5MOaivHkI/9Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@oxlint/binding-linux-arm64-gnu@1.49.0': - resolution: {integrity: sha512-oSHpm8zmSvAG1BWUumbDRSg7moJbnwoEXKAkwDf/xTQJOzvbUknq95NVQdw/AduZr5dePftalB8rzJNGBogUMg==} + '@oxlint/binding-linux-arm64-gnu@1.59.0': + resolution: {integrity: sha512-Gt54Y4eqSgYJ90xipm24xeyaPV854706o/kiT8oZvUt3VDY7qqxdqyGqchMaujd87ib+/MXvnl9WkK8Cc1BExg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-arm64-musl@1.49.0': - resolution: {integrity: sha512-xeqkMOARgGBlEg9BQuPDf6ZW711X6BT5qjDyeM5XNowCJeTSdmMhpePJjTEiVbbr3t21sIlK8RE6X5bc04nWyQ==} + '@oxlint/binding-linux-arm64-musl@1.59.0': + resolution: {integrity: sha512-3CtsKp7NFB3OfqQzbuAecrY7GIZeiv7AD+xutU4tefVQzlfmTI7/ygWLrvkzsDEjTlMq41rYHxgsn6Yh8tybmA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] - '@oxlint/binding-linux-ppc64-gnu@1.49.0': - resolution: {integrity: sha512-uvcqRO6PnlJGbL7TeePhTK5+7/JXbxGbN+C6FVmfICDeeRomgQqrfVjf0lUrVpUU8ii8TSkIbNdft3M+oNlOsQ==} + '@oxlint/binding-linux-ppc64-gnu@1.59.0': + resolution: {integrity: sha512-K0diOpT3ncDmOfl9I1HuvpEsAuTxkts0VYwIv/w6Xiy9CdwyPBVX88Ga9l8VlGgMrwBMnSY4xIvVlVY/fkQk7Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-gnu@1.49.0': - resolution: {integrity: sha512-Dw1HkdXAwHNH+ZDserHP2RzXQmhHtpsYYI0hf8fuGAVCIVwvS6w1+InLxpPMY25P8ASRNiFN3hADtoh6lI+4lg==} + '@oxlint/binding-linux-riscv64-gnu@1.59.0': + resolution: {integrity: sha512-xAU7+QDU6kTJJ7mJLOGgo7oOjtAtkKyFZ0Yjdb5cEo3DiCCPFLvyr08rWiQh6evZ7RiUTf+o65NY/bqttzJiQQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-riscv64-musl@1.49.0': - resolution: {integrity: sha512-EPlMYaA05tJ9km/0dI9K57iuMq3Tw+nHst7TNIegAJZrBPtsOtYaMFZEaWj02HA8FI5QvSnRHMt+CI+RIhXJBQ==} + '@oxlint/binding-linux-riscv64-musl@1.59.0': + resolution: {integrity: sha512-KUmZmKlTTyauOnvUNVxK7G40sSSx0+w5l1UhaGsC6KPpOYHenx2oqJTnabmpLJicok7IC+3Y6fXAUOMyexaeJQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] - '@oxlint/binding-linux-s390x-gnu@1.49.0': - resolution: {integrity: sha512-yZiQL9qEwse34aMbnMb5VqiAWfDY+fLFuoJbHOuzB1OaJZbN1MRF9Nk+W89PIpGr5DNPDipwjZb8+Q7wOywoUQ==} + '@oxlint/binding-linux-s390x-gnu@1.59.0': + resolution: {integrity: sha512-4usRxC8gS0PGdkHnRmwJt/4zrQNZyk6vL0trCxwZSsAKM+OxhB8nKiR+mhjdBbl8lbMh2gc3bZpNN/ik8c4c2A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-gnu@1.49.0': - resolution: {integrity: sha512-CcCDwMMXSchNkhdgvhVn3DLZ4EnBXAD8o8+gRzahg+IdSt/72y19xBgShJgadIRF0TsRcV/MhDUMwL5N/W54aQ==} + '@oxlint/binding-linux-x64-gnu@1.59.0': + resolution: {integrity: sha512-s/rNE2gDmbwAOOP493xk2X7M8LZfI1LJFSSW1+yanz3vuQCFPiHkx4GY+O1HuLUDtkzGlhtMrIcxxzyYLv308w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] - '@oxlint/binding-linux-x64-musl@1.49.0': - resolution: {integrity: sha512-u3HfKV8BV6t6UCCbN0RRiyqcymhrnpunVmLFI8sEa5S/EBu+p/0bJ3D7LZ2KT6PsBbrB71SWq4DeFrskOVgIZg==} + '@oxlint/binding-linux-x64-musl@1.59.0': + resolution: {integrity: sha512-+yYj1udJa2UvvIUmEm0IcKgc0UlPMgz0nsSTvkPL2y6n0uU5LgIHSwVu4AHhrve6j9BpVSoRksnz8c9QcvITJA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] - '@oxlint/binding-openharmony-arm64@1.49.0': - resolution: {integrity: sha512-dRDpH9fw+oeUMpM4br0taYCFpW6jQtOuEIec89rOgDA1YhqwmeRcx0XYeCv7U48p57qJ1XZHeMGM9LdItIjfzA==} + '@oxlint/binding-openharmony-arm64@1.59.0': + resolution: {integrity: sha512-bUplUb48LYsB3hHlQXP2ZMOenpieWoOyppLAnnAhuPag3MGPnt+7caxE3w/Vl9wpQsTA3gzLntQi9rxWrs7Xqg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@oxlint/binding-win32-arm64-msvc@1.49.0': - resolution: {integrity: sha512-6rrKe/wL9tn0qnOy76i1/0f4Dc3dtQnibGlU4HqR/brVHlVjzLSoaH0gAFnLnznh9yQ6gcFTBFOPrcN/eKPDGA==} + '@oxlint/binding-win32-arm64-msvc@1.59.0': + resolution: {integrity: sha512-/HLsLuz42rWl7h7ePdmMTpHm2HIDmPtcEMYgm5BBEHiEiuNOrzMaUpd2z7UnNni5LGN9obJy2YoAYBLXQwazrA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@oxlint/binding-win32-ia32-msvc@1.49.0': - resolution: {integrity: sha512-CXHLWAtLs2xG/aVy1OZiYJzrULlq0QkYpI6cd7VKMrab+qur4fXVE/B1Bp1m0h1qKTj5/FTGg6oU4qaXMjS/ug==} + '@oxlint/binding-win32-ia32-msvc@1.59.0': + resolution: {integrity: sha512-rUPy+JnanpPwV/aJCPnxAD1fW50+XPI0VkWr7f0vEbqcdsS8NpB24Rw6RsS7SdpFv8Dw+8ugCwao5nCFbqOUSg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] - '@oxlint/binding-win32-x64-msvc@1.49.0': - resolution: {integrity: sha512-VteIelt78kwzSglOozaQcs6BCS4Lk0j+QA+hGV0W8UeyaqQ3XpbZRhDU55NW1PPvCy1tg4VXsTlEaPovqto7nQ==} + '@oxlint/binding-win32-x64-msvc@1.59.0': + resolution: {integrity: sha512-xkE7puteDS/vUyRngLXW0t8WgdWoS/tfxXjhP/P7SMqPDx+hs44SpssO3h3qmTqECYEuXBUPzcAw5257Ka+ofA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -1109,141 +1112,141 @@ packages: '@rolldown/pluginutils@1.0.0-rc.3': resolution: {integrity: sha512-eybk3TjzzzV97Dlj5c+XrBFW57eTNhzod66y9HrBlzJ6NsCrWCp/2kaPS3K9wJmurBC0Tdw4yPjXKZqlznim3Q==} - '@rollup/rollup-android-arm-eabi@4.57.1': - resolution: {integrity: sha512-A6ehUVSiSaaliTxai040ZpZ2zTevHYbvu/lDoeAteHI8QnaosIzm4qwtezfRg1jOYaUmnzLX1AOD6Z+UJjtifg==} + '@rollup/rollup-android-arm-eabi@4.60.1': + resolution: {integrity: sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.57.1': - resolution: {integrity: sha512-dQaAddCY9YgkFHZcFNS/606Exo8vcLHwArFZ7vxXq4rigo2bb494/xKMMwRRQW6ug7Js6yXmBZhSBRuBvCCQ3w==} + '@rollup/rollup-android-arm64@4.60.1': + resolution: {integrity: sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.57.1': - resolution: {integrity: sha512-crNPrwJOrRxagUYeMn/DZwqN88SDmwaJ8Cvi/TN1HnWBU7GwknckyosC2gd0IqYRsHDEnXf328o9/HC6OkPgOg==} + '@rollup/rollup-darwin-arm64@4.60.1': + resolution: {integrity: sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.57.1': - resolution: {integrity: sha512-Ji8g8ChVbKrhFtig5QBV7iMaJrGtpHelkB3lsaKzadFBe58gmjfGXAOfI5FV0lYMH8wiqsxKQ1C9B0YTRXVy4w==} + '@rollup/rollup-darwin-x64@4.60.1': + resolution: {integrity: sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.57.1': - resolution: {integrity: sha512-R+/WwhsjmwodAcz65guCGFRkMb4gKWTcIeLy60JJQbXrJ97BOXHxnkPFrP+YwFlaS0m+uWJTstrUA9o+UchFug==} + '@rollup/rollup-freebsd-arm64@4.60.1': + resolution: {integrity: sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.57.1': - resolution: {integrity: sha512-IEQTCHeiTOnAUC3IDQdzRAGj3jOAYNr9kBguI7MQAAZK3caezRrg0GxAb6Hchg4lxdZEI5Oq3iov/w/hnFWY9Q==} + '@rollup/rollup-freebsd-x64@4.60.1': + resolution: {integrity: sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.57.1': - resolution: {integrity: sha512-F8sWbhZ7tyuEfsmOxwc2giKDQzN3+kuBLPwwZGyVkLlKGdV1nvnNwYD0fKQ8+XS6hp9nY7B+ZeK01EBUE7aHaw==} + '@rollup/rollup-linux-arm-gnueabihf@4.60.1': + resolution: {integrity: sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==} cpu: [arm] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.57.1': - resolution: {integrity: sha512-rGfNUfn0GIeXtBP1wL5MnzSj98+PZe/AXaGBCRmT0ts80lU5CATYGxXukeTX39XBKsxzFpEeK+Mrp9faXOlmrw==} + '@rollup/rollup-linux-arm-musleabihf@4.60.1': + resolution: {integrity: sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==} cpu: [arm] os: [linux] libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.57.1': - resolution: {integrity: sha512-MMtej3YHWeg/0klK2Qodf3yrNzz6CGjo2UntLvk2RSPlhzgLvYEB3frRvbEF2wRKh1Z2fDIg9KRPe1fawv7C+g==} + '@rollup/rollup-linux-arm64-gnu@4.60.1': + resolution: {integrity: sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==} cpu: [arm64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.57.1': - resolution: {integrity: sha512-1a/qhaaOXhqXGpMFMET9VqwZakkljWHLmZOX48R0I/YLbhdxr1m4gtG1Hq7++VhVUmf+L3sTAf9op4JlhQ5u1Q==} + '@rollup/rollup-linux-arm64-musl@4.60.1': + resolution: {integrity: sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==} cpu: [arm64] os: [linux] libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.57.1': - resolution: {integrity: sha512-QWO6RQTZ/cqYtJMtxhkRkidoNGXc7ERPbZN7dVW5SdURuLeVU7lwKMpo18XdcmpWYd0qsP1bwKPf7DNSUinhvA==} + '@rollup/rollup-linux-loong64-gnu@4.60.1': + resolution: {integrity: sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==} cpu: [loong64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.57.1': - resolution: {integrity: sha512-xpObYIf+8gprgWaPP32xiN5RVTi/s5FCR+XMXSKmhfoJjrpRAjCuuqQXyxUa/eJTdAE6eJ+KDKaoEqjZQxh3Gw==} + '@rollup/rollup-linux-loong64-musl@4.60.1': + resolution: {integrity: sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==} cpu: [loong64] os: [linux] libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.57.1': - resolution: {integrity: sha512-4BrCgrpZo4hvzMDKRqEaW1zeecScDCR+2nZ86ATLhAoJ5FQ+lbHVD3ttKe74/c7tNT9c6F2viwB3ufwp01Oh2w==} + '@rollup/rollup-linux-ppc64-gnu@4.60.1': + resolution: {integrity: sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==} cpu: [ppc64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.57.1': - resolution: {integrity: sha512-NOlUuzesGauESAyEYFSe3QTUguL+lvrN1HtwEEsU2rOwdUDeTMJdO5dUYl/2hKf9jWydJrO9OL/XSSf65R5+Xw==} + '@rollup/rollup-linux-ppc64-musl@4.60.1': + resolution: {integrity: sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==} cpu: [ppc64] os: [linux] libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.57.1': - resolution: {integrity: sha512-ptA88htVp0AwUUqhVghwDIKlvJMD/fmL/wrQj99PRHFRAG6Z5nbWoWG4o81Nt9FT+IuqUQi+L31ZKAFeJ5Is+A==} + '@rollup/rollup-linux-riscv64-gnu@4.60.1': + resolution: {integrity: sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==} cpu: [riscv64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.57.1': - resolution: {integrity: sha512-S51t7aMMTNdmAMPpBg7OOsTdn4tySRQvklmL3RpDRyknk87+Sp3xaumlatU+ppQ+5raY7sSTcC2beGgvhENfuw==} + '@rollup/rollup-linux-riscv64-musl@4.60.1': + resolution: {integrity: sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==} cpu: [riscv64] os: [linux] libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.57.1': - resolution: {integrity: sha512-Bl00OFnVFkL82FHbEqy3k5CUCKH6OEJL54KCyx2oqsmZnFTR8IoNqBF+mjQVcRCT5sB6yOvK8A37LNm/kPJiZg==} + '@rollup/rollup-linux-s390x-gnu@4.60.1': + resolution: {integrity: sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==} cpu: [s390x] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.57.1': - resolution: {integrity: sha512-ABca4ceT4N+Tv/GtotnWAeXZUZuM/9AQyCyKYyKnpk4yoA7QIAuBt6Hkgpw8kActYlew2mvckXkvx0FfoInnLg==} + '@rollup/rollup-linux-x64-gnu@4.60.1': + resolution: {integrity: sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==} cpu: [x64] os: [linux] libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.57.1': - resolution: {integrity: sha512-HFps0JeGtuOR2convgRRkHCekD7j+gdAuXM+/i6kGzQtFhlCtQkpwtNzkNj6QhCDp7DRJ7+qC/1Vg2jt5iSOFw==} + '@rollup/rollup-linux-x64-musl@4.60.1': + resolution: {integrity: sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==} cpu: [x64] os: [linux] libc: [musl] - '@rollup/rollup-openbsd-x64@4.57.1': - resolution: {integrity: sha512-H+hXEv9gdVQuDTgnqD+SQffoWoc0Of59AStSzTEj/feWTBAnSfSD3+Dql1ZruJQxmykT/JVY0dE8Ka7z0DH1hw==} + '@rollup/rollup-openbsd-x64@4.60.1': + resolution: {integrity: sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.57.1': - resolution: {integrity: sha512-4wYoDpNg6o/oPximyc/NG+mYUejZrCU2q+2w6YZqrAs2UcNUChIZXjtafAiiZSUc7On8v5NyNj34Kzj/Ltk6dQ==} + '@rollup/rollup-openharmony-arm64@4.60.1': + resolution: {integrity: sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.57.1': - resolution: {integrity: sha512-O54mtsV/6LW3P8qdTcamQmuC990HDfR71lo44oZMZlXU4tzLrbvTii87Ni9opq60ds0YzuAlEr/GNwuNluZyMQ==} + '@rollup/rollup-win32-arm64-msvc@4.60.1': + resolution: {integrity: sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.57.1': - resolution: {integrity: sha512-P3dLS+IerxCT/7D2q2FYcRdWRl22dNbrbBEtxdWhXrfIMPP9lQhb5h4Du04mdl5Woq05jVCDPCMF7Ub0NAjIew==} + '@rollup/rollup-win32-ia32-msvc@4.60.1': + resolution: {integrity: sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.57.1': - resolution: {integrity: sha512-VMBH2eOOaKGtIJYleXsi2B8CPVADrh+TyNxJ4mWPnKfLB/DBUmzW+5m1xUrcwWoMfSLagIRpjUFeW5CO5hyciQ==} + '@rollup/rollup-win32-x64-gnu@4.60.1': + resolution: {integrity: sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.57.1': - resolution: {integrity: sha512-mxRFDdHIWRxg3UfIIAwCm6NzvxG0jDX/wBN6KsQFTvKFqqg9vTrWUE68qEjHt19A5wwx5X5aUi2zuZT7YR0jrA==} + '@rollup/rollup-win32-x64-msvc@4.60.1': + resolution: {integrity: sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==} cpu: [x64] os: [win32] @@ -1260,8 +1263,8 @@ packages: resolution: {integrity: sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==} engines: {node: '>=18'} - '@speed-highlight/core@1.2.14': - resolution: {integrity: sha512-G4ewlBNhUtlLvrJTb88d2mdy2KRijzs4UhnlrOSRT4bmjh/IqNElZa3zkrZ+TC47TwtlDWzVLFADljF1Ijp5hA==} + '@speed-highlight/core@1.2.15': + resolution: {integrity: sha512-BMq1K3DsElxDWawkX6eLg9+CKJrTVGCBAWVuHXVUV2u0s2711qiChLSId6ikYPfxhdYocLNt3wWwSvDiTvFabw==} '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -1290,8 +1293,8 @@ packages: '@types/node@12.20.55': resolution: {integrity: sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==} - '@types/node@25.3.0': - resolution: {integrity: sha512-4K3bqJpXpqfg2XKGK9bpDTc6xO/xoUP/RBWS7AtRMug6zZFaRekiLzjVtAoZMquxoAbzBvy5nxQ7veS5eYzf8A==} + '@types/node@25.5.2': + resolution: {integrity: sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==} '@types/react-dom@19.2.3': resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} @@ -1301,51 +1304,51 @@ packages: '@types/react@19.2.14': resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} - '@vitejs/plugin-react@5.1.4': - resolution: {integrity: sha512-VIcFLdRi/VYRU8OL/puL7QXMYafHmqOnwTZY50U1JPlCNj30PxCMx65c494b1K9be9hX83KVt0+gTEwTWLqToA==} + '@vitejs/plugin-react@5.2.0': + resolution: {integrity: sha512-YmKkfhOAi3wsB1PhJq5Scj3GXMn3WvtQ/JC0xoopuHoXSdmtdStOpFrYaT1kie2YgFBcIe64ROzMYRjCrYOdYw==} engines: {node: ^20.19.0 || >=22.12.0} peerDependencies: - vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 + vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 - '@vitest/browser-playwright@4.0.18': - resolution: {integrity: sha512-gfajTHVCiwpxRj1qh0Sh/5bbGLG4F/ZH/V9xvFVoFddpITfMta9YGow0W6ZpTTORv2vdJuz9TnrNSmjKvpOf4g==} + '@vitest/browser-playwright@4.1.2': + resolution: {integrity: sha512-N0Z2HzMLvMR6k/tWPTS6Q/DaRscrkax/f2f9DIbNQr+Cd1l4W4wTf/I6S983PAMr0tNqqoTL+xNkLh9M5vbkLg==} peerDependencies: playwright: '*' - vitest: 4.0.18 + vitest: 4.1.2 - '@vitest/browser@4.0.18': - resolution: {integrity: sha512-gVQqh7paBz3gC+ZdcCmNSWJMk70IUjDeVqi+5m5vYpEHsIwRgw3Y545jljtajhkekIpIp5Gg8oK7bctgY0E2Ng==} + '@vitest/browser@4.1.2': + resolution: {integrity: sha512-CwdIf90LNf1Zitgqy63ciMAzmyb4oIGs8WZ40VGYrWkssQKeEKr32EzO8MKUrDPPcPVHFI9oQ5ni2Hp24NaNRQ==} peerDependencies: - vitest: 4.0.18 + vitest: 4.1.2 - '@vitest/expect@4.0.18': - resolution: {integrity: sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==} + '@vitest/expect@4.1.2': + resolution: {integrity: sha512-gbu+7B0YgUJ2nkdsRJrFFW6X7NTP44WlhiclHniUhxADQJH5Szt9mZ9hWnJPJ8YwOK5zUOSSlSvyzRf0u1DSBQ==} - '@vitest/mocker@4.0.18': - resolution: {integrity: sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==} + '@vitest/mocker@4.1.2': + resolution: {integrity: sha512-Ize4iQtEALHDttPRCmN+FKqOl2vxTiNUhzobQFFt/BM1lRUTG7zRCLOykG/6Vo4E4hnUdfVLo5/eqKPukcWW7Q==} peerDependencies: msw: ^2.4.9 - vite: ^6.0.0 || ^7.0.0-0 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true - '@vitest/pretty-format@4.0.18': - resolution: {integrity: sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==} + '@vitest/pretty-format@4.1.2': + resolution: {integrity: sha512-dwQga8aejqeuB+TvXCMzSQemvV9hNEtDDpgUKDzOmNQayl2OG241PSWeJwKRH3CiC+sESrmoFd49rfnq7T4RnA==} - '@vitest/runner@4.0.18': - resolution: {integrity: sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==} + '@vitest/runner@4.1.2': + resolution: {integrity: sha512-Gr+FQan34CdiYAwpGJmQG8PgkyFVmARK8/xSijia3eTFgVfpcpztWLuP6FttGNfPLJhaZVP/euvujeNYar36OQ==} - '@vitest/snapshot@4.0.18': - resolution: {integrity: sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==} + '@vitest/snapshot@4.1.2': + resolution: {integrity: sha512-g7yfUmxYS4mNxk31qbOYsSt2F4m1E02LFqO53Xpzg3zKMhLAPZAjjfyl9e6z7HrW6LvUdTwAQR3HHfLjpko16A==} - '@vitest/spy@4.0.18': - resolution: {integrity: sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==} + '@vitest/spy@4.1.2': + resolution: {integrity: sha512-DU4fBnbVCJGNBwVA6xSToNXrkZNSiw59H8tcuUspVMsBDBST4nfvsPsEHDHGtWRRnqBERBQu7TrTKskmjqTXKA==} - '@vitest/utils@4.0.18': - resolution: {integrity: sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==} + '@vitest/utils@4.1.2': + resolution: {integrity: sha512-xw2/TiX82lQHA06cgbqRKFb5lCAy3axQ4H4SoUFhUsg+wztiet+co86IAMDtF6Vm1hc7J6j09oh/rgDn+JdKIQ==} abitype@1.2.3: resolution: {integrity: sha512-Ofer5QUnuUdTFsBRwARMoWKOH1ND5ehwYhJ3OJ/BQO+StkwQjHw0XyVh4vDttzHB7QOFhPHa/o413PJ82gU/Tg==} @@ -1380,8 +1383,8 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - baseline-browser-mapping@2.10.0: - resolution: {integrity: sha512-lIyg0szRfYbiy67j9KN8IyeD7q7hcmqnJ1ddWmNt19ItGpNN64mnllmxUNFIOdOm6by97jlL6wfpTTJrmnjWAA==} + baseline-browser-mapping@2.10.16: + resolution: {integrity: sha512-Lyf3aK28zpsD1yQMiiHD4RvVb6UdMoo8xzG2XzFIfR9luPzOpcBlAsT/qfB1XWS1bxWT+UtE4WmQgsp297FYOA==} engines: {node: '>=6.0.0'} hasBin: true @@ -1396,8 +1399,8 @@ packages: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.28.1: - resolution: {integrity: sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==} + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1405,8 +1408,8 @@ packages: resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} engines: {node: '>=8'} - caniuse-lite@1.0.30001770: - resolution: {integrity: sha512-x/2CLQ1jHENRbHg5PSId2sXq1CIO1CISvwWAj027ltMVG2UNgW+w9oH2+HzgEIRFembL8bUlXtfbBHR1fCg2xw==} + caniuse-lite@1.0.30001786: + resolution: {integrity: sha512-4oxTZEvqmLLrERwxO76yfKM7acZo310U+v4kqexI2TL1DkkUEMT8UijrxxcnVdxR3qkVf5awGRX+4Z6aPHVKrA==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} @@ -1415,10 +1418,6 @@ packages: chardet@2.1.1: resolution: {integrity: sha512-PsezH1rqdV9VvyNhxxOW32/d75r01NY7TQCmOqomRo15ZSOKbpTFVsfjghxo6JloQUCGnH4k1LGu0R4yCLlWQQ==} - ci-info@3.9.0: - resolution: {integrity: sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==} - engines: {node: '>=8'} - convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} @@ -1454,8 +1453,8 @@ packages: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} - electron-to-chromium@1.5.286: - resolution: {integrity: sha512-9tfDXhJ4RKFNerfjdCcZfufu49vg620741MNs26a9+bhLThdB+plgMeou98CAaHu/WATj2iHOOHTp1hWtABj2A==} + electron-to-chromium@1.5.332: + resolution: {integrity: sha512-7OOtytmh/rINMLwaFTbcMVvYXO3AUm029X0LcyfYk0B557RlPkdpTpnH9+htMlfu5dKwOmT0+Zs2Aw+lnn6TeQ==} enquirer@2.4.1: resolution: {integrity: sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==} @@ -1464,8 +1463,8 @@ packages: error-stack-parser-es@1.0.5: resolution: {integrity: sha512-5qucVt2XcuGMcEGgWI7i+yZpmpByQ8J1lHhcL7PwqCwu9FPP3VUXzT4ltHe5i2z9dePwEHcDVOAfSnHsOlCXRA==} - es-module-lexer@1.7.0: - resolution: {integrity: sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==} + es-module-lexer@2.0.0: + resolution: {integrity: sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==} esbuild@0.25.12: resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==} @@ -1503,6 +1502,15 @@ packages: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} + fast-string-truncated-width@1.2.1: + resolution: {integrity: sha512-Q9acT/+Uu3GwGj+5w/zsGuQjh9O1TyywhIwAxHudtWrgF09nHOPrvTLhQevPbttcxjr/SNN7mJmfOw/B1bXgow==} + + fast-string-width@1.1.0: + resolution: {integrity: sha512-O3fwIVIH5gKB38QNbdg+3760ZmGz0SZMgvwJbA1b2TGXceKE6A2cOlfogh1iw8lr049zPyd7YADHy+B7U4W9bQ==} + + fast-wrap-ansi@0.1.6: + resolution: {integrity: sha512-HlUwET7a5gqjURj70D5jl7aC3Zmy4weA1SHUfM0JFI0Ptq987NH2TwbBFLoERhfwk+E+eaq4EK3jXoT+R3yp3w==} + fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} @@ -1556,8 +1564,8 @@ packages: graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} - hono@4.12.0: - resolution: {integrity: sha512-NekXntS5M94pUfiVZ8oXXK/kkri+5WpX2/Ik+LVsl+uvw+soj4roXIsPqO+XsWrAw20mOzaXOZf3Q7PfB9A/IA==} + hono@4.12.12: + resolution: {integrity: sha512-p1JfQMKaceuCbpJKAPKVqyqviZdS0eUxH9v82oWo1kb9xjQ5wA6iP3FNVAPDFlz5/p7d45lO+BpSk1tuSZMF4Q==} engines: {node: '>=16.9.0'} human-id@4.1.3: @@ -1644,8 +1652,8 @@ packages: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} - miniflare@4.20260219.0: - resolution: {integrity: sha512-EIb5wXbWUnnC60XU2aiFOPNd4fgTXzECkwRSOXZ1vdcY9WZaEE9rVf+h+Apw+WkOHRkp3Dr9/ZhQ5y1R+9iZ4Q==} + miniflare@4.20260401.0: + resolution: {integrity: sha512-lngHPzZFN9sxYG/mhzvnWiBMNVAN5MsO/7g32ttJ07rymtiK/ZBalODTKb8Od+BQdlU5DOR4CjVt9NydjnUyYg==} engines: {node: '>=18.0.0'} hasBin: true @@ -1665,8 +1673,8 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - node-releases@2.0.27: - resolution: {integrity: sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==} + node-releases@2.0.37: + resolution: {integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==} obug@2.1.1: resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} @@ -1674,25 +1682,25 @@ packages: outdent@0.5.0: resolution: {integrity: sha512-/jHxFIzoMXdqPzTaCpFzAAWhpkSjZPF4Vsn6jAfNpmbH/ymsmd7Qc6VE9BGn0L6YMj6uwpQLxCECpus4ukKS9Q==} - ox@0.14.11: - resolution: {integrity: sha512-sPMs9FapVBDrsutvvlsWI1Yzx2qIZWa1JonZTVpAl72qMig5YruzhY6IUyBy1xs0juk/x8l/0uVqKCC/qW7Jrw==} + ox@0.14.15: + resolution: {integrity: sha512-3TubCmbKen/cuZQzX0qDbOS5lojjdSZ90lqKxWIDWd5siuJ0IJBaTXMYs8eMPLcraqnOwGZazz3apHPGiRCkGQ==} peerDependencies: typescript: '>=5.4.0' peerDependenciesMeta: typescript: optional: true - oxfmt@0.34.0: - resolution: {integrity: sha512-t+zTE4XGpzPTK+Zk9gSwcJcFi4pqjl6PwO/ZxPBJiJQ2XCKMucwjPlHxvPHyVKJtkMSyrDGfQ7Ntg/hUr4OgHQ==} + oxfmt@0.44.0: + resolution: {integrity: sha512-lnncqvHewyRvaqdrnntVIrZV2tEddz8lbvPsQzG/zlkfvgZkwy0HP1p/2u1aCDToeg1jb9zBpbJdfkV73Itw+w==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - oxlint@1.49.0: - resolution: {integrity: sha512-YZffp0gM+63CJoRhHjtjRnwKtAgUnXM6j63YQ++aigji2NVvLGsUlrXo9gJUXZOdcbfShLYtA6RuTu8GZ4lzOQ==} + oxlint@1.59.0: + resolution: {integrity: sha512-0xBLeGGjP4vD9pygRo8iuOkOzEU1MqOnfiOl7KYezL/QvWL8NUg6n03zXc7ZVqltiOpUxBk2zgHI3PnRIEdAvw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: - oxlint-tsgolint: '>=0.14.1' + oxlint-tsgolint: '>=0.18.0' peerDependenciesMeta: oxlint-tsgolint: optional: true @@ -1741,29 +1749,25 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} - picomatch@2.3.1: - resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==} + picomatch@2.3.2: + resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.3: - resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} pify@4.0.1: resolution: {integrity: sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==} engines: {node: '>=6'} - pixelmatch@7.1.0: - resolution: {integrity: sha512-1wrVzJ2STrpmONHKBy228LM1b84msXDUoAzVEl0R8Mz4Ce6EPr+IVtxm8+yvrqLYMHswREkjYFaMxnyGnaY3Ng==} - hasBin: true - - playwright-core@1.58.2: - resolution: {integrity: sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==} + playwright-core@1.59.1: + resolution: {integrity: sha512-HBV/RJg81z5BiiZ9yPzIiClYV/QMsDCKUyogwH9p3MCP6IYjUFu/MActgYAvK0oWyV9NlwM3GLBjADyWgydVyg==} engines: {node: '>=18'} hasBin: true - playwright@1.58.2: - resolution: {integrity: sha512-vA30H8Nvkq/cPBnNw4Q8TWz1EJyqgpuinBcHET0YVJVFldr8JDNiU9LaWAE1KqSkRYazuaBhTpB5ZzShOezQ6A==} + playwright@1.59.1: + resolution: {integrity: sha512-C8oWjPR3F81yljW9o5OxcWzfh6avkVwDD2VYdwIGqTkl+OGFISgypqzfu7dOe4QNLL2aqcWBmI3PMtLIK233lw==} engines: {node: '>=18'} hasBin: true @@ -1771,8 +1775,8 @@ packages: resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} engines: {node: '>=14.19.0'} - postcss@8.5.6: - resolution: {integrity: sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==} + postcss@8.5.8: + resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} engines: {node: ^10 || ^12 || >=14} prettier@2.8.8: @@ -1811,8 +1815,8 @@ packages: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} - rollup@4.57.1: - resolution: {integrity: sha512-oQL6lgK3e2QZeQ7gcgIkS2YZPg5slw37hYufJ3edKlfQSGGm8ICoxswK15ntSzF/a8+h7ekRy7k7oWc3BQ7y8A==} + rollup@4.60.1: + resolution: {integrity: sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1877,8 +1881,8 @@ packages: stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} - std-env@3.10.0: - resolution: {integrity: sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==} + std-env@4.0.0: + resolution: {integrity: sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==} strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} @@ -1899,8 +1903,8 @@ packages: tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} - tinyexec@1.0.2: - resolution: {integrity: sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==} + tinyexec@1.0.4: + resolution: {integrity: sha512-u9r3uZC0bdpGOXtlxUIdwf9pkmvhqJdrVCH9fapQtgy/OeTTMZ1nqH7agtvEfmGui6e1XxjcdrlxvxJvc3sMqw==} engines: {node: '>=18'} tinyglobby@0.2.15: @@ -1911,8 +1915,8 @@ packages: resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} engines: {node: ^20.0.0 || >=22.0.0} - tinyrainbow@3.0.3: - resolution: {integrity: sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==} + tinyrainbow@3.1.0: + resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} to-regex-range@5.0.1: @@ -1944,8 +1948,8 @@ packages: undici-types@7.18.2: resolution: {integrity: sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==} - undici@7.18.2: - resolution: {integrity: sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==} + undici@7.24.4: + resolution: {integrity: sha512-BM/JzwwaRXxrLdElV2Uo6cTLEjhSb3WXboncJamZ15NgUURmvlXvxa6xkwIOILIjPNo9i8ku136ZvWV0Uly8+w==} engines: {node: '>=20.18.1'} unenv@2.0.0-rc.24: @@ -1961,8 +1965,8 @@ packages: peerDependencies: browserslist: '>= 4.21.0' - vite@6.4.1: - resolution: {integrity: sha512-+Oxm7q9hDoLMyJOYfUYBuHQo+dkAloi33apOPP56pzj+vsdJDzr+j1NISE5pyaAuKL4A3UD34qd0lx5+kfKp2g==} + vite@6.4.2: + resolution: {integrity: sha512-2N/55r4JDJ4gdrCvGgINMy+HH3iRpNIz8K6SFwVsA+JbQScLiC+clmAxBgwiSPgcG9U15QmvqCGWzMbqda5zGQ==} engines: {node: ^18.0.0 || ^20.0.0 || >=22.0.0} hasBin: true peerDependencies: @@ -2001,60 +2005,21 @@ packages: yaml: optional: true - vite@7.3.1: - resolution: {integrity: sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==} - engines: {node: ^20.19.0 || >=22.12.0} - hasBin: true - peerDependencies: - '@types/node': ^20.19.0 || >=22.12.0 - jiti: '>=1.21.0' - less: ^4.0.0 - lightningcss: ^1.21.0 - sass: ^1.70.0 - sass-embedded: ^1.70.0 - stylus: '>=0.54.8' - sugarss: ^5.0.0 - terser: ^5.16.0 - tsx: ^4.8.1 - yaml: ^2.4.2 - peerDependenciesMeta: - '@types/node': - optional: true - jiti: - optional: true - less: - optional: true - lightningcss: - optional: true - sass: - optional: true - sass-embedded: - optional: true - stylus: - optional: true - sugarss: - optional: true - terser: - optional: true - tsx: - optional: true - yaml: - optional: true - - vitest@4.0.18: - resolution: {integrity: sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==} + vitest@4.1.2: + resolution: {integrity: sha512-xjR1dMTVHlFLh98JE3i/f/WePqJsah4A0FK9cc8Ehp9Udk0AZk6ccpIZhh1qJ/yxVWRZ+Q54ocnD8TXmkhspGg==} 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.0.18 - '@vitest/browser-preview': 4.0.18 - '@vitest/browser-webdriverio': 4.0.18 - '@vitest/ui': 4.0.18 + '@vitest/browser-playwright': 4.1.2 + '@vitest/browser-preview': 4.1.2 + '@vitest/browser-webdriverio': 4.1.2 + '@vitest/ui': 4.1.2 happy-dom: '*' jsdom: '*' + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true @@ -2085,17 +2050,17 @@ packages: engines: {node: '>=8'} hasBin: true - workerd@1.20260219.0: - resolution: {integrity: sha512-l4U4iT5H8jNV6+EK23ExnUV2z6JvqQtQPrT8XCm4G8RpwC9EPpYTOO9s/ImMPJKe1WSbQUQoJ4k8Nd83fz8skQ==} + workerd@1.20260401.1: + resolution: {integrity: sha512-mUYCd+ohaWJWF5nhDzxugWaAD/DM8Dw0ze3B7bu8JaA7S70+XQJXcvcvwE8C4qGcxSdCyqjsrFzqxKubECDwzg==} engines: {node: '>=16'} hasBin: true - wrangler@4.67.0: - resolution: {integrity: sha512-58OoVth7bqm0nqsRgcI67gHbpp0IfR1JIBqDY0XR1FzRu9Qkjn6v2iJAdFf82QcVBFhaMBYQi88WqYGswq5wlQ==} - engines: {node: '>=20.0.0'} + wrangler@4.80.0: + resolution: {integrity: sha512-2ZKF7uPeOZy65BGk3YfvqBCPo/xH1MrAlMmH9mVP+tCNBrTUMnwOHSj1HrZHgR8LttkAqhko0fGz+I4ax1rzyQ==} + engines: {node: '>=20.3.0'} hasBin: true peerDependencies: - '@cloudflare/workers-types': ^4.20260219.0 + '@cloudflare/workers-types': ^4.20260401.1 peerDependenciesMeta: '@cloudflare/workers-types': optional: true @@ -2112,8 +2077,8 @@ packages: utf-8-validate: optional: true - ws@8.19.0: - resolution: {integrity: sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==} + ws@8.20.0: + resolution: {integrity: sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -2133,8 +2098,8 @@ packages: youch@4.1.0-beta.10: resolution: {integrity: sha512-rLfVLB4FgQneDr0dv1oddCVZmKjcJ6yX6mS4pU82Mq/Dt9a3cLZQ62pDBL4AUO+uVrCvtWz3ZFUL2HFAFJ/BXQ==} - zile@0.0.21: - resolution: {integrity: sha512-Q5jCsjc/wQI6+bX33qHcrDNnTw+mEAqA0aEFgTh0r8iL6+dGvHOjx681A7wouu8tqJCj9jnxLEHB8FsgB/2cJw==} + zile@0.0.24: + resolution: {integrity: sha512-fUabaQBvEhE10CDADPUt271ghj7Vh6KOO+CU04YLEbhKX+mMiH3K/9TF6f7wBLCyhC0Ee6TSz66vI+kCjBycyw==} hasBin: true peerDependencies: '@typescript/native-preview': '>=7.0.0' @@ -2166,8 +2131,8 @@ snapshots: '@babel/generator': 7.29.1 '@babel/helper-compilation-targets': 7.28.6 '@babel/helper-module-transforms': 7.28.6(@babel/core@7.29.0) - '@babel/helpers': 7.28.6 - '@babel/parser': 7.29.0 + '@babel/helpers': 7.29.2 + '@babel/parser': 7.29.2 '@babel/template': 7.28.6 '@babel/traverse': 7.29.0 '@babel/types': 7.29.0 @@ -2182,7 +2147,7 @@ snapshots: '@babel/generator@7.29.1': dependencies: - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 @@ -2192,7 +2157,7 @@ snapshots: dependencies: '@babel/compat-data': 7.29.0 '@babel/helper-validator-option': 7.27.1 - browserslist: 4.28.1 + browserslist: 4.28.2 lru-cache: 5.1.1 semver: 6.3.1 @@ -2222,12 +2187,12 @@ snapshots: '@babel/helper-validator-option@7.27.1': {} - '@babel/helpers@7.28.6': + '@babel/helpers@7.29.2': dependencies: '@babel/template': 7.28.6 '@babel/types': 7.29.0 - '@babel/parser@7.29.0': + '@babel/parser@7.29.2': dependencies: '@babel/types': 7.29.0 @@ -2241,12 +2206,12 @@ snapshots: '@babel/core': 7.29.0 '@babel/helper-plugin-utils': 7.28.6 - '@babel/runtime@7.28.6': {} + '@babel/runtime@7.29.2': {} '@babel/template@7.28.6': dependencies: '@babel/code-frame': 7.29.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 '@babel/traverse@7.29.0': @@ -2254,7 +2219,7 @@ snapshots: '@babel/code-frame': 7.29.0 '@babel/generator': 7.29.1 '@babel/helper-globals': 7.28.0 - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/template': 7.28.6 '@babel/types': 7.29.0 debug: 4.4.3 @@ -2266,9 +2231,11 @@ snapshots: '@babel/helper-string-parser': 7.27.1 '@babel/helper-validator-identifier': 7.28.5 - '@changesets/apply-release-plan@7.0.14': + '@blazediff/core@1.9.1': {} + + '@changesets/apply-release-plan@7.1.0': dependencies: - '@changesets/config': 3.1.2 + '@changesets/config': 3.1.3 '@changesets/get-version-range-type': 0.4.0 '@changesets/git': 3.0.4 '@changesets/should-skip-package': 0.1.2 @@ -2295,30 +2262,28 @@ snapshots: dependencies: '@changesets/types': 6.1.0 - '@changesets/cli@2.29.8(@types/node@25.3.0)': + '@changesets/cli@2.30.0(@types/node@25.5.2)': dependencies: - '@changesets/apply-release-plan': 7.0.14 + '@changesets/apply-release-plan': 7.1.0 '@changesets/assemble-release-plan': 6.0.9 '@changesets/changelog-git': 0.2.1 - '@changesets/config': 3.1.2 + '@changesets/config': 3.1.3 '@changesets/errors': 0.2.0 '@changesets/get-dependents-graph': 2.1.3 - '@changesets/get-release-plan': 4.0.14 + '@changesets/get-release-plan': 4.0.15 '@changesets/git': 3.0.4 '@changesets/logger': 0.1.1 '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.6 + '@changesets/read': 0.6.7 '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@changesets/write': 0.4.0 - '@inquirer/external-editor': 1.0.3(@types/node@25.3.0) + '@inquirer/external-editor': 1.0.3(@types/node@25.5.2) '@manypkg/get-packages': 1.1.3 ansi-colors: 4.1.3 - ci-info: 3.9.0 enquirer: 2.4.1 fs-extra: 7.0.1 mri: 1.2.0 - p-limit: 2.3.0 package-manager-detector: 0.2.11 picocolors: 1.1.1 resolve-from: 5.0.0 @@ -2328,11 +2293,12 @@ snapshots: transitivePeerDependencies: - '@types/node' - '@changesets/config@3.1.2': + '@changesets/config@3.1.3': dependencies: '@changesets/errors': 0.2.0 '@changesets/get-dependents-graph': 2.1.3 '@changesets/logger': 0.1.1 + '@changesets/should-skip-package': 0.1.2 '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 @@ -2349,12 +2315,12 @@ snapshots: picocolors: 1.1.1 semver: 7.7.4 - '@changesets/get-release-plan@4.0.14': + '@changesets/get-release-plan@4.0.15': dependencies: '@changesets/assemble-release-plan': 6.0.9 - '@changesets/config': 3.1.2 + '@changesets/config': 3.1.3 '@changesets/pre': 2.0.2 - '@changesets/read': 0.6.6 + '@changesets/read': 0.6.7 '@changesets/types': 6.1.0 '@manypkg/get-packages': 1.1.3 @@ -2372,7 +2338,7 @@ snapshots: dependencies: picocolors: 1.1.1 - '@changesets/parse@0.4.2': + '@changesets/parse@0.4.3': dependencies: '@changesets/types': 6.1.0 js-yaml: 4.1.1 @@ -2384,11 +2350,11 @@ snapshots: '@manypkg/get-packages': 1.1.3 fs-extra: 7.0.1 - '@changesets/read@0.6.6': + '@changesets/read@0.6.7': dependencies: '@changesets/git': 3.0.4 '@changesets/logger': 0.1.1 - '@changesets/parse': 0.4.2 + '@changesets/parse': 0.4.3 '@changesets/types': 6.1.0 fs-extra: 7.0.1 p-filter: 2.1.0 @@ -2410,58 +2376,59 @@ snapshots: human-id: 4.1.3 prettier: 2.8.8 - '@clack/core@1.0.1': + '@clack/core@1.2.0': dependencies: - picocolors: 1.1.1 + fast-wrap-ansi: 0.1.6 sisteransi: 1.0.5 - '@clack/prompts@1.0.1': + '@clack/prompts@1.2.0': dependencies: - '@clack/core': 1.0.1 - picocolors: 1.1.1 + '@clack/core': 1.2.0 + fast-string-width: 1.1.0 + fast-wrap-ansi: 0.1.6 sisteransi: 1.0.5 '@cloudflare/kv-asset-handler@0.4.2': {} - '@cloudflare/unenv-preset@2.14.0(unenv@2.0.0-rc.24)(workerd@1.20260219.0)': + '@cloudflare/unenv-preset@2.16.0(unenv@2.0.0-rc.24)(workerd@1.20260401.1)': dependencies: unenv: 2.0.0-rc.24 optionalDependencies: - workerd: 1.20260219.0 + workerd: 1.20260401.1 - '@cloudflare/vite-plugin@1.25.2(vite@6.4.1(@types/node@25.3.0))(workerd@1.20260219.0)(wrangler@4.67.0)': + '@cloudflare/vite-plugin@1.31.0(vite@6.4.2(@types/node@25.5.2))(workerd@1.20260401.1)(wrangler@4.80.0)': dependencies: - '@cloudflare/unenv-preset': 2.14.0(unenv@2.0.0-rc.24)(workerd@1.20260219.0) - miniflare: 4.20260219.0 + '@cloudflare/unenv-preset': 2.16.0(unenv@2.0.0-rc.24)(workerd@1.20260401.1) + miniflare: 4.20260401.0 unenv: 2.0.0-rc.24 - vite: 6.4.1(@types/node@25.3.0) - wrangler: 4.67.0 + vite: 6.4.2(@types/node@25.5.2) + wrangler: 4.80.0 ws: 8.18.0 transitivePeerDependencies: - bufferutil - utf-8-validate - workerd - '@cloudflare/workerd-darwin-64@1.20260219.0': + '@cloudflare/workerd-darwin-64@1.20260401.1': optional: true - '@cloudflare/workerd-darwin-arm64@1.20260219.0': + '@cloudflare/workerd-darwin-arm64@1.20260401.1': optional: true - '@cloudflare/workerd-linux-64@1.20260219.0': + '@cloudflare/workerd-linux-64@1.20260401.1': optional: true - '@cloudflare/workerd-linux-arm64@1.20260219.0': + '@cloudflare/workerd-linux-arm64@1.20260401.1': optional: true - '@cloudflare/workerd-windows-64@1.20260219.0': + '@cloudflare/workerd-windows-64@1.20260401.1': optional: true '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@emnapi/runtime@1.8.1': + '@emnapi/runtime@1.9.2': dependencies: tslib: 2.8.1 optional: true @@ -2622,12 +2589,12 @@ snapshots: '@esbuild/win32-x64@0.27.3': optional: true - '@hono/zod-validator@0.7.6(hono@4.12.0)(zod@4.3.6)': + '@hono/zod-validator@0.7.6(hono@4.12.12)(zod@4.3.6)': dependencies: - hono: 4.12.0 + hono: 4.12.12 zod: 4.3.6 - '@img/colour@1.0.0': {} + '@img/colour@1.1.0': {} '@img/sharp-darwin-arm64@0.34.5': optionalDependencies: @@ -2711,7 +2678,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.8.1 + '@emnapi/runtime': 1.9.2 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -2723,12 +2690,12 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@inquirer/external-editor@1.0.3(@types/node@25.3.0)': + '@inquirer/external-editor@1.0.3(@types/node@25.5.2)': dependencies: chardet: 2.1.1 iconv-lite: 0.7.2 optionalDependencies: - '@types/node': 25.3.0 + '@types/node': 25.5.2 '@jridgewell/gen-mapping@0.3.13': dependencies: @@ -2756,14 +2723,14 @@ snapshots: '@manypkg/find-root@1.1.0': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 '@types/node': 12.20.55 find-up: 4.1.0 fs-extra: 8.1.0 '@manypkg/get-packages@1.1.3': dependencies: - '@babel/runtime': 7.28.6 + '@babel/runtime': 7.29.2 '@changesets/types': 4.1.0 '@manypkg/find-root': 1.1.0 fs-extra: 8.1.0 @@ -2790,118 +2757,118 @@ snapshots: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 - '@oxfmt/binding-android-arm-eabi@0.34.0': + '@oxfmt/binding-android-arm-eabi@0.44.0': optional: true - '@oxfmt/binding-android-arm64@0.34.0': + '@oxfmt/binding-android-arm64@0.44.0': optional: true - '@oxfmt/binding-darwin-arm64@0.34.0': + '@oxfmt/binding-darwin-arm64@0.44.0': optional: true - '@oxfmt/binding-darwin-x64@0.34.0': + '@oxfmt/binding-darwin-x64@0.44.0': optional: true - '@oxfmt/binding-freebsd-x64@0.34.0': + '@oxfmt/binding-freebsd-x64@0.44.0': optional: true - '@oxfmt/binding-linux-arm-gnueabihf@0.34.0': + '@oxfmt/binding-linux-arm-gnueabihf@0.44.0': optional: true - '@oxfmt/binding-linux-arm-musleabihf@0.34.0': + '@oxfmt/binding-linux-arm-musleabihf@0.44.0': optional: true - '@oxfmt/binding-linux-arm64-gnu@0.34.0': + '@oxfmt/binding-linux-arm64-gnu@0.44.0': optional: true - '@oxfmt/binding-linux-arm64-musl@0.34.0': + '@oxfmt/binding-linux-arm64-musl@0.44.0': optional: true - '@oxfmt/binding-linux-ppc64-gnu@0.34.0': + '@oxfmt/binding-linux-ppc64-gnu@0.44.0': optional: true - '@oxfmt/binding-linux-riscv64-gnu@0.34.0': + '@oxfmt/binding-linux-riscv64-gnu@0.44.0': optional: true - '@oxfmt/binding-linux-riscv64-musl@0.34.0': + '@oxfmt/binding-linux-riscv64-musl@0.44.0': optional: true - '@oxfmt/binding-linux-s390x-gnu@0.34.0': + '@oxfmt/binding-linux-s390x-gnu@0.44.0': optional: true - '@oxfmt/binding-linux-x64-gnu@0.34.0': + '@oxfmt/binding-linux-x64-gnu@0.44.0': optional: true - '@oxfmt/binding-linux-x64-musl@0.34.0': + '@oxfmt/binding-linux-x64-musl@0.44.0': optional: true - '@oxfmt/binding-openharmony-arm64@0.34.0': + '@oxfmt/binding-openharmony-arm64@0.44.0': optional: true - '@oxfmt/binding-win32-arm64-msvc@0.34.0': + '@oxfmt/binding-win32-arm64-msvc@0.44.0': optional: true - '@oxfmt/binding-win32-ia32-msvc@0.34.0': + '@oxfmt/binding-win32-ia32-msvc@0.44.0': optional: true - '@oxfmt/binding-win32-x64-msvc@0.34.0': + '@oxfmt/binding-win32-x64-msvc@0.44.0': optional: true - '@oxlint/binding-android-arm-eabi@1.49.0': + '@oxlint/binding-android-arm-eabi@1.59.0': optional: true - '@oxlint/binding-android-arm64@1.49.0': + '@oxlint/binding-android-arm64@1.59.0': optional: true - '@oxlint/binding-darwin-arm64@1.49.0': + '@oxlint/binding-darwin-arm64@1.59.0': optional: true - '@oxlint/binding-darwin-x64@1.49.0': + '@oxlint/binding-darwin-x64@1.59.0': optional: true - '@oxlint/binding-freebsd-x64@1.49.0': + '@oxlint/binding-freebsd-x64@1.59.0': optional: true - '@oxlint/binding-linux-arm-gnueabihf@1.49.0': + '@oxlint/binding-linux-arm-gnueabihf@1.59.0': optional: true - '@oxlint/binding-linux-arm-musleabihf@1.49.0': + '@oxlint/binding-linux-arm-musleabihf@1.59.0': optional: true - '@oxlint/binding-linux-arm64-gnu@1.49.0': + '@oxlint/binding-linux-arm64-gnu@1.59.0': optional: true - '@oxlint/binding-linux-arm64-musl@1.49.0': + '@oxlint/binding-linux-arm64-musl@1.59.0': optional: true - '@oxlint/binding-linux-ppc64-gnu@1.49.0': + '@oxlint/binding-linux-ppc64-gnu@1.59.0': optional: true - '@oxlint/binding-linux-riscv64-gnu@1.49.0': + '@oxlint/binding-linux-riscv64-gnu@1.59.0': optional: true - '@oxlint/binding-linux-riscv64-musl@1.49.0': + '@oxlint/binding-linux-riscv64-musl@1.59.0': optional: true - '@oxlint/binding-linux-s390x-gnu@1.49.0': + '@oxlint/binding-linux-s390x-gnu@1.59.0': optional: true - '@oxlint/binding-linux-x64-gnu@1.49.0': + '@oxlint/binding-linux-x64-gnu@1.59.0': optional: true - '@oxlint/binding-linux-x64-musl@1.49.0': + '@oxlint/binding-linux-x64-musl@1.59.0': optional: true - '@oxlint/binding-openharmony-arm64@1.49.0': + '@oxlint/binding-openharmony-arm64@1.59.0': optional: true - '@oxlint/binding-win32-arm64-msvc@1.49.0': + '@oxlint/binding-win32-arm64-msvc@1.59.0': optional: true - '@oxlint/binding-win32-ia32-msvc@1.49.0': + '@oxlint/binding-win32-ia32-msvc@1.59.0': optional: true - '@oxlint/binding-win32-x64-msvc@1.49.0': + '@oxlint/binding-win32-x64-msvc@1.59.0': optional: true '@polka/url@1.0.0-next.29': {} @@ -2920,79 +2887,79 @@ snapshots: '@rolldown/pluginutils@1.0.0-rc.3': {} - '@rollup/rollup-android-arm-eabi@4.57.1': + '@rollup/rollup-android-arm-eabi@4.60.1': optional: true - '@rollup/rollup-android-arm64@4.57.1': + '@rollup/rollup-android-arm64@4.60.1': optional: true - '@rollup/rollup-darwin-arm64@4.57.1': + '@rollup/rollup-darwin-arm64@4.60.1': optional: true - '@rollup/rollup-darwin-x64@4.57.1': + '@rollup/rollup-darwin-x64@4.60.1': optional: true - '@rollup/rollup-freebsd-arm64@4.57.1': + '@rollup/rollup-freebsd-arm64@4.60.1': optional: true - '@rollup/rollup-freebsd-x64@4.57.1': + '@rollup/rollup-freebsd-x64@4.60.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.57.1': + '@rollup/rollup-linux-arm-gnueabihf@4.60.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.57.1': + '@rollup/rollup-linux-arm-musleabihf@4.60.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.57.1': + '@rollup/rollup-linux-arm64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.57.1': + '@rollup/rollup-linux-arm64-musl@4.60.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.57.1': + '@rollup/rollup-linux-loong64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-loong64-musl@4.57.1': + '@rollup/rollup-linux-loong64-musl@4.60.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.57.1': + '@rollup/rollup-linux-ppc64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-ppc64-musl@4.57.1': + '@rollup/rollup-linux-ppc64-musl@4.60.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.57.1': + '@rollup/rollup-linux-riscv64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.57.1': + '@rollup/rollup-linux-riscv64-musl@4.60.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.57.1': + '@rollup/rollup-linux-s390x-gnu@4.60.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.57.1': + '@rollup/rollup-linux-x64-gnu@4.60.1': optional: true - '@rollup/rollup-linux-x64-musl@4.57.1': + '@rollup/rollup-linux-x64-musl@4.60.1': optional: true - '@rollup/rollup-openbsd-x64@4.57.1': + '@rollup/rollup-openbsd-x64@4.60.1': optional: true - '@rollup/rollup-openharmony-arm64@4.57.1': + '@rollup/rollup-openharmony-arm64@4.60.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.57.1': + '@rollup/rollup-win32-arm64-msvc@4.60.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.57.1': + '@rollup/rollup-win32-ia32-msvc@4.60.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.57.1': + '@rollup/rollup-win32-x64-gnu@4.60.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.57.1': + '@rollup/rollup-win32-x64-msvc@4.60.1': optional: true '@scure/base@1.2.6': {} @@ -3010,13 +2977,13 @@ snapshots: '@sindresorhus/is@7.2.0': {} - '@speed-highlight/core@1.2.14': {} + '@speed-highlight/core@1.2.15': {} '@standard-schema/spec@1.1.0': {} '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 @@ -3028,7 +2995,7 @@ snapshots: '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.29.0 + '@babel/parser': 7.29.2 '@babel/types': 7.29.0 '@types/babel__traverse@7.28.0': @@ -3046,7 +3013,7 @@ snapshots: '@types/node@12.20.55': {} - '@types/node@25.3.0': + '@types/node@25.5.2': dependencies: undici-types: 7.18.2 @@ -3058,7 +3025,7 @@ snapshots: dependencies: csstype: 3.2.3 - '@vitejs/plugin-react@5.1.4(vite@6.4.1(@types/node@25.3.0))': + '@vitejs/plugin-react@5.2.0(vite@6.4.2(@types/node@25.5.2))': dependencies: '@babel/core': 7.29.0 '@babel/plugin-transform-react-jsx-self': 7.27.1(@babel/core@7.29.0) @@ -3066,78 +3033,80 @@ snapshots: '@rolldown/pluginutils': 1.0.0-rc.3 '@types/babel__core': 7.20.5 react-refresh: 0.18.0 - vite: 6.4.1(@types/node@25.3.0) + vite: 6.4.2(@types/node@25.5.2) transitivePeerDependencies: - supports-color - '@vitest/browser-playwright@4.0.18(playwright@1.58.2)(vite@7.3.1(@types/node@25.3.0))(vitest@4.0.18)': + '@vitest/browser-playwright@4.1.2(playwright@1.59.1)(vite@6.4.2(@types/node@25.5.2))(vitest@4.1.2)': dependencies: - '@vitest/browser': 4.0.18(vite@7.3.1(@types/node@25.3.0))(vitest@4.0.18) - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)) - playwright: 1.58.2 - tinyrainbow: 3.0.3 - vitest: 4.0.18(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18) + '@vitest/browser': 4.1.2(vite@6.4.2(@types/node@25.5.2))(vitest@4.1.2) + '@vitest/mocker': 4.1.2(vite@6.4.2(@types/node@25.5.2)) + playwright: 1.59.1 + tinyrainbow: 3.1.0 + vitest: 4.1.2(@types/node@25.5.2)(@vitest/browser-playwright@4.1.2)(vite@6.4.2(@types/node@25.5.2)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/browser@4.0.18(vite@7.3.1(@types/node@25.3.0))(vitest@4.0.18)': + '@vitest/browser@4.1.2(vite@6.4.2(@types/node@25.5.2))(vitest@4.1.2)': dependencies: - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)) - '@vitest/utils': 4.0.18 + '@blazediff/core': 1.9.1 + '@vitest/mocker': 4.1.2(vite@6.4.2(@types/node@25.5.2)) + '@vitest/utils': 4.1.2 magic-string: 0.30.21 - pixelmatch: 7.1.0 pngjs: 7.0.0 sirv: 3.0.2 - tinyrainbow: 3.0.3 - vitest: 4.0.18(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18) - ws: 8.19.0 + tinyrainbow: 3.1.0 + vitest: 4.1.2(@types/node@25.5.2)(@vitest/browser-playwright@4.1.2)(vite@6.4.2(@types/node@25.5.2)) + ws: 8.20.0 transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite - '@vitest/expect@4.0.18': + '@vitest/expect@4.1.2': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 + '@vitest/spy': 4.1.2 + '@vitest/utils': 4.1.2 chai: 6.2.2 - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.0 - '@vitest/mocker@4.0.18(vite@7.3.1(@types/node@25.3.0))': + '@vitest/mocker@4.1.2(vite@6.4.2(@types/node@25.5.2))': dependencies: - '@vitest/spy': 4.0.18 + '@vitest/spy': 4.1.2 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 7.3.1(@types/node@25.3.0) + vite: 6.4.2(@types/node@25.5.2) - '@vitest/pretty-format@4.0.18': + '@vitest/pretty-format@4.1.2': dependencies: - tinyrainbow: 3.0.3 + tinyrainbow: 3.1.0 - '@vitest/runner@4.0.18': + '@vitest/runner@4.1.2': dependencies: - '@vitest/utils': 4.0.18 + '@vitest/utils': 4.1.2 pathe: 2.0.3 - '@vitest/snapshot@4.0.18': + '@vitest/snapshot@4.1.2': dependencies: - '@vitest/pretty-format': 4.0.18 + '@vitest/pretty-format': 4.1.2 + '@vitest/utils': 4.1.2 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.0.18': {} + '@vitest/spy@4.1.2': {} - '@vitest/utils@4.0.18': + '@vitest/utils@4.1.2': dependencies: - '@vitest/pretty-format': 4.0.18 - tinyrainbow: 3.0.3 + '@vitest/pretty-format': 4.1.2 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.0 abitype@1.2.3(typescript@5.9.3)(zod@4.3.6): optionalDependencies: @@ -3158,7 +3127,7 @@ snapshots: assertion-error@2.0.1: {} - baseline-browser-mapping@2.10.0: {} + baseline-browser-mapping@2.10.16: {} better-path-resolve@1.0.0: dependencies: @@ -3170,24 +3139,22 @@ snapshots: dependencies: fill-range: 7.1.1 - browserslist@4.28.1: + browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.0 - caniuse-lite: 1.0.30001770 - electron-to-chromium: 1.5.286 - node-releases: 2.0.27 - update-browserslist-db: 1.2.3(browserslist@4.28.1) + baseline-browser-mapping: 2.10.16 + caniuse-lite: 1.0.30001786 + electron-to-chromium: 1.5.332 + node-releases: 2.0.37 + update-browserslist-db: 1.2.3(browserslist@4.28.2) cac@6.7.14: {} - caniuse-lite@1.0.30001770: {} + caniuse-lite@1.0.30001786: {} chai@6.2.2: {} chardet@2.1.1: {} - ci-info@3.9.0: {} - convert-source-map@2.0.0: {} cookie@1.1.1: {} @@ -3212,7 +3179,7 @@ snapshots: dependencies: path-type: 4.0.0 - electron-to-chromium@1.5.286: {} + electron-to-chromium@1.5.332: {} enquirer@2.4.1: dependencies: @@ -3221,7 +3188,7 @@ snapshots: error-stack-parser-es@1.0.5: {} - es-module-lexer@1.7.0: {} + es-module-lexer@2.0.0: {} esbuild@0.25.12: optionalDependencies: @@ -3303,13 +3270,23 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 + fast-string-truncated-width@1.2.1: {} + + fast-string-width@1.1.0: + dependencies: + fast-string-truncated-width: 1.2.1 + + fast-wrap-ansi@0.1.6: + dependencies: + fast-string-width: 1.1.0 + fastq@1.20.1: dependencies: reusify: 1.1.0 - fdir@6.5.0(picomatch@4.0.3): + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: - picomatch: 4.0.3 + picomatch: 4.0.4 fill-range@7.1.1: dependencies: @@ -3355,7 +3332,7 @@ snapshots: graceful-fs@4.2.11: {} - hono@4.12.0: {} + hono@4.12.12: {} human-id@4.1.3: {} @@ -3421,14 +3398,14 @@ snapshots: micromatch@4.0.8: dependencies: braces: 3.0.3 - picomatch: 2.3.1 + picomatch: 2.3.2 - miniflare@4.20260219.0: + miniflare@4.20260401.0: dependencies: '@cspotcode/source-map-support': 0.8.1 sharp: 0.34.5 - undici: 7.18.2 - workerd: 1.20260219.0 + undici: 7.24.4 + workerd: 1.20260401.1 ws: 8.18.0 youch: 4.1.0-beta.10 transitivePeerDependencies: @@ -3443,13 +3420,13 @@ snapshots: nanoid@3.3.11: {} - node-releases@2.0.27: {} + node-releases@2.0.37: {} obug@2.1.1: {} outdent@0.5.0: {} - ox@0.14.11(typescript@5.9.3)(zod@4.3.6): + ox@0.14.15(typescript@5.9.3)(zod@4.3.6): dependencies: '@adraffy/ens-normalize': 1.11.1 '@noble/ciphers': 1.3.0 @@ -3464,51 +3441,51 @@ snapshots: transitivePeerDependencies: - zod - oxfmt@0.34.0: + oxfmt@0.44.0: dependencies: tinypool: 2.1.0 optionalDependencies: - '@oxfmt/binding-android-arm-eabi': 0.34.0 - '@oxfmt/binding-android-arm64': 0.34.0 - '@oxfmt/binding-darwin-arm64': 0.34.0 - '@oxfmt/binding-darwin-x64': 0.34.0 - '@oxfmt/binding-freebsd-x64': 0.34.0 - '@oxfmt/binding-linux-arm-gnueabihf': 0.34.0 - '@oxfmt/binding-linux-arm-musleabihf': 0.34.0 - '@oxfmt/binding-linux-arm64-gnu': 0.34.0 - '@oxfmt/binding-linux-arm64-musl': 0.34.0 - '@oxfmt/binding-linux-ppc64-gnu': 0.34.0 - '@oxfmt/binding-linux-riscv64-gnu': 0.34.0 - '@oxfmt/binding-linux-riscv64-musl': 0.34.0 - '@oxfmt/binding-linux-s390x-gnu': 0.34.0 - '@oxfmt/binding-linux-x64-gnu': 0.34.0 - '@oxfmt/binding-linux-x64-musl': 0.34.0 - '@oxfmt/binding-openharmony-arm64': 0.34.0 - '@oxfmt/binding-win32-arm64-msvc': 0.34.0 - '@oxfmt/binding-win32-ia32-msvc': 0.34.0 - '@oxfmt/binding-win32-x64-msvc': 0.34.0 - - oxlint@1.49.0: + '@oxfmt/binding-android-arm-eabi': 0.44.0 + '@oxfmt/binding-android-arm64': 0.44.0 + '@oxfmt/binding-darwin-arm64': 0.44.0 + '@oxfmt/binding-darwin-x64': 0.44.0 + '@oxfmt/binding-freebsd-x64': 0.44.0 + '@oxfmt/binding-linux-arm-gnueabihf': 0.44.0 + '@oxfmt/binding-linux-arm-musleabihf': 0.44.0 + '@oxfmt/binding-linux-arm64-gnu': 0.44.0 + '@oxfmt/binding-linux-arm64-musl': 0.44.0 + '@oxfmt/binding-linux-ppc64-gnu': 0.44.0 + '@oxfmt/binding-linux-riscv64-gnu': 0.44.0 + '@oxfmt/binding-linux-riscv64-musl': 0.44.0 + '@oxfmt/binding-linux-s390x-gnu': 0.44.0 + '@oxfmt/binding-linux-x64-gnu': 0.44.0 + '@oxfmt/binding-linux-x64-musl': 0.44.0 + '@oxfmt/binding-openharmony-arm64': 0.44.0 + '@oxfmt/binding-win32-arm64-msvc': 0.44.0 + '@oxfmt/binding-win32-ia32-msvc': 0.44.0 + '@oxfmt/binding-win32-x64-msvc': 0.44.0 + + oxlint@1.59.0: optionalDependencies: - '@oxlint/binding-android-arm-eabi': 1.49.0 - '@oxlint/binding-android-arm64': 1.49.0 - '@oxlint/binding-darwin-arm64': 1.49.0 - '@oxlint/binding-darwin-x64': 1.49.0 - '@oxlint/binding-freebsd-x64': 1.49.0 - '@oxlint/binding-linux-arm-gnueabihf': 1.49.0 - '@oxlint/binding-linux-arm-musleabihf': 1.49.0 - '@oxlint/binding-linux-arm64-gnu': 1.49.0 - '@oxlint/binding-linux-arm64-musl': 1.49.0 - '@oxlint/binding-linux-ppc64-gnu': 1.49.0 - '@oxlint/binding-linux-riscv64-gnu': 1.49.0 - '@oxlint/binding-linux-riscv64-musl': 1.49.0 - '@oxlint/binding-linux-s390x-gnu': 1.49.0 - '@oxlint/binding-linux-x64-gnu': 1.49.0 - '@oxlint/binding-linux-x64-musl': 1.49.0 - '@oxlint/binding-openharmony-arm64': 1.49.0 - '@oxlint/binding-win32-arm64-msvc': 1.49.0 - '@oxlint/binding-win32-ia32-msvc': 1.49.0 - '@oxlint/binding-win32-x64-msvc': 1.49.0 + '@oxlint/binding-android-arm-eabi': 1.59.0 + '@oxlint/binding-android-arm64': 1.59.0 + '@oxlint/binding-darwin-arm64': 1.59.0 + '@oxlint/binding-darwin-x64': 1.59.0 + '@oxlint/binding-freebsd-x64': 1.59.0 + '@oxlint/binding-linux-arm-gnueabihf': 1.59.0 + '@oxlint/binding-linux-arm-musleabihf': 1.59.0 + '@oxlint/binding-linux-arm64-gnu': 1.59.0 + '@oxlint/binding-linux-arm64-musl': 1.59.0 + '@oxlint/binding-linux-ppc64-gnu': 1.59.0 + '@oxlint/binding-linux-riscv64-gnu': 1.59.0 + '@oxlint/binding-linux-riscv64-musl': 1.59.0 + '@oxlint/binding-linux-s390x-gnu': 1.59.0 + '@oxlint/binding-linux-x64-gnu': 1.59.0 + '@oxlint/binding-linux-x64-musl': 1.59.0 + '@oxlint/binding-openharmony-arm64': 1.59.0 + '@oxlint/binding-win32-arm64-msvc': 1.59.0 + '@oxlint/binding-win32-ia32-msvc': 1.59.0 + '@oxlint/binding-win32-x64-msvc': 1.59.0 p-filter@2.1.0: dependencies: @@ -3542,27 +3519,23 @@ snapshots: picocolors@1.1.1: {} - picomatch@2.3.1: {} + picomatch@2.3.2: {} - picomatch@4.0.3: {} + picomatch@4.0.4: {} pify@4.0.1: {} - pixelmatch@7.1.0: - dependencies: - pngjs: 7.0.0 + playwright-core@1.59.1: {} - playwright-core@1.58.2: {} - - playwright@1.58.2: + playwright@1.59.1: dependencies: - playwright-core: 1.58.2 + playwright-core: 1.59.1 optionalDependencies: fsevents: 2.3.2 pngjs@7.0.0: {} - postcss@8.5.6: + postcss@8.5.8: dependencies: nanoid: 3.3.11 picocolors: 1.1.1 @@ -3594,35 +3567,35 @@ snapshots: reusify@1.1.0: {} - rollup@4.57.1: + rollup@4.60.1: dependencies: '@types/estree': 1.0.8 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.57.1 - '@rollup/rollup-android-arm64': 4.57.1 - '@rollup/rollup-darwin-arm64': 4.57.1 - '@rollup/rollup-darwin-x64': 4.57.1 - '@rollup/rollup-freebsd-arm64': 4.57.1 - '@rollup/rollup-freebsd-x64': 4.57.1 - '@rollup/rollup-linux-arm-gnueabihf': 4.57.1 - '@rollup/rollup-linux-arm-musleabihf': 4.57.1 - '@rollup/rollup-linux-arm64-gnu': 4.57.1 - '@rollup/rollup-linux-arm64-musl': 4.57.1 - '@rollup/rollup-linux-loong64-gnu': 4.57.1 - '@rollup/rollup-linux-loong64-musl': 4.57.1 - '@rollup/rollup-linux-ppc64-gnu': 4.57.1 - '@rollup/rollup-linux-ppc64-musl': 4.57.1 - '@rollup/rollup-linux-riscv64-gnu': 4.57.1 - '@rollup/rollup-linux-riscv64-musl': 4.57.1 - '@rollup/rollup-linux-s390x-gnu': 4.57.1 - '@rollup/rollup-linux-x64-gnu': 4.57.1 - '@rollup/rollup-linux-x64-musl': 4.57.1 - '@rollup/rollup-openbsd-x64': 4.57.1 - '@rollup/rollup-openharmony-arm64': 4.57.1 - '@rollup/rollup-win32-arm64-msvc': 4.57.1 - '@rollup/rollup-win32-ia32-msvc': 4.57.1 - '@rollup/rollup-win32-x64-gnu': 4.57.1 - '@rollup/rollup-win32-x64-msvc': 4.57.1 + '@rollup/rollup-android-arm-eabi': 4.60.1 + '@rollup/rollup-android-arm64': 4.60.1 + '@rollup/rollup-darwin-arm64': 4.60.1 + '@rollup/rollup-darwin-x64': 4.60.1 + '@rollup/rollup-freebsd-arm64': 4.60.1 + '@rollup/rollup-freebsd-x64': 4.60.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.60.1 + '@rollup/rollup-linux-arm-musleabihf': 4.60.1 + '@rollup/rollup-linux-arm64-gnu': 4.60.1 + '@rollup/rollup-linux-arm64-musl': 4.60.1 + '@rollup/rollup-linux-loong64-gnu': 4.60.1 + '@rollup/rollup-linux-loong64-musl': 4.60.1 + '@rollup/rollup-linux-ppc64-gnu': 4.60.1 + '@rollup/rollup-linux-ppc64-musl': 4.60.1 + '@rollup/rollup-linux-riscv64-gnu': 4.60.1 + '@rollup/rollup-linux-riscv64-musl': 4.60.1 + '@rollup/rollup-linux-s390x-gnu': 4.60.1 + '@rollup/rollup-linux-x64-gnu': 4.60.1 + '@rollup/rollup-linux-x64-musl': 4.60.1 + '@rollup/rollup-openbsd-x64': 4.60.1 + '@rollup/rollup-openharmony-arm64': 4.60.1 + '@rollup/rollup-win32-arm64-msvc': 4.60.1 + '@rollup/rollup-win32-ia32-msvc': 4.60.1 + '@rollup/rollup-win32-x64-gnu': 4.60.1 + '@rollup/rollup-win32-x64-msvc': 4.60.1 fsevents: 2.3.3 run-parallel@1.2.0: @@ -3639,7 +3612,7 @@ snapshots: sharp@0.34.5: dependencies: - '@img/colour': 1.0.0 + '@img/colour': 1.1.0 detect-libc: 2.1.2 semver: 7.7.4 optionalDependencies: @@ -3699,7 +3672,7 @@ snapshots: stackback@0.0.2: {} - std-env@3.10.0: {} + std-env@4.0.0: {} strip-ansi@6.0.1: dependencies: @@ -3713,16 +3686,16 @@ snapshots: tinybench@2.9.0: {} - tinyexec@1.0.2: {} + tinyexec@1.0.4: {} tinyglobby@0.2.15: dependencies: - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 tinypool@2.1.0: {} - tinyrainbow@3.0.3: {} + tinyrainbow@3.1.0: {} to-regex-range@5.0.1: dependencies: @@ -3741,7 +3714,7 @@ snapshots: undici-types@7.18.2: {} - undici@7.18.2: {} + undici@7.24.4: {} unenv@2.0.0-rc.24: dependencies: @@ -3749,73 +3722,51 @@ snapshots: universalify@0.1.2: {} - update-browserslist-db@1.2.3(browserslist@4.28.1): + update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: - browserslist: 4.28.1 + browserslist: 4.28.2 escalade: 3.2.0 picocolors: 1.1.1 - vite@6.4.1(@types/node@25.3.0): + vite@6.4.2(@types/node@25.5.2): dependencies: esbuild: 0.25.12 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.57.1 - tinyglobby: 0.2.15 - optionalDependencies: - '@types/node': 25.3.0 - fsevents: 2.3.3 - - vite@7.3.1(@types/node@25.3.0): - dependencies: - esbuild: 0.27.3 - fdir: 6.5.0(picomatch@4.0.3) - picomatch: 4.0.3 - postcss: 8.5.6 - rollup: 4.57.1 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + postcss: 8.5.8 + rollup: 4.60.1 tinyglobby: 0.2.15 optionalDependencies: - '@types/node': 25.3.0 + '@types/node': 25.5.2 fsevents: 2.3.3 - vitest@4.0.18(@types/node@25.3.0)(@vitest/browser-playwright@4.0.18): + vitest@4.1.2(@types/node@25.5.2)(@vitest/browser-playwright@4.1.2)(vite@6.4.2(@types/node@25.5.2)): dependencies: - '@vitest/expect': 4.0.18 - '@vitest/mocker': 4.0.18(vite@7.3.1(@types/node@25.3.0)) - '@vitest/pretty-format': 4.0.18 - '@vitest/runner': 4.0.18 - '@vitest/snapshot': 4.0.18 - '@vitest/spy': 4.0.18 - '@vitest/utils': 4.0.18 - es-module-lexer: 1.7.0 + '@vitest/expect': 4.1.2 + '@vitest/mocker': 4.1.2(vite@6.4.2(@types/node@25.5.2)) + '@vitest/pretty-format': 4.1.2 + '@vitest/runner': 4.1.2 + '@vitest/snapshot': 4.1.2 + '@vitest/spy': 4.1.2 + '@vitest/utils': 4.1.2 + es-module-lexer: 2.0.0 expect-type: 1.3.0 magic-string: 0.30.21 obug: 2.1.1 pathe: 2.0.3 - picomatch: 4.0.3 - std-env: 3.10.0 + picomatch: 4.0.4 + std-env: 4.0.0 tinybench: 2.9.0 - tinyexec: 1.0.2 + tinyexec: 1.0.4 tinyglobby: 0.2.15 - tinyrainbow: 3.0.3 - vite: 7.3.1(@types/node@25.3.0) + tinyrainbow: 3.1.0 + vite: 6.4.2(@types/node@25.5.2) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 25.3.0 - '@vitest/browser-playwright': 4.0.18(playwright@1.58.2)(vite@7.3.1(@types/node@25.3.0))(vitest@4.0.18) + '@types/node': 25.5.2 + '@vitest/browser-playwright': 4.1.2(playwright@1.59.1)(vite@6.4.2(@types/node@25.5.2))(vitest@4.1.2) transitivePeerDependencies: - - jiti - - less - - lightningcss - msw - - sass - - sass-embedded - - stylus - - sugarss - - terser - - tsx - - yaml which@2.0.2: dependencies: @@ -3826,24 +3777,24 @@ snapshots: siginfo: 2.0.0 stackback: 0.0.2 - workerd@1.20260219.0: + workerd@1.20260401.1: optionalDependencies: - '@cloudflare/workerd-darwin-64': 1.20260219.0 - '@cloudflare/workerd-darwin-arm64': 1.20260219.0 - '@cloudflare/workerd-linux-64': 1.20260219.0 - '@cloudflare/workerd-linux-arm64': 1.20260219.0 - '@cloudflare/workerd-windows-64': 1.20260219.0 + '@cloudflare/workerd-darwin-64': 1.20260401.1 + '@cloudflare/workerd-darwin-arm64': 1.20260401.1 + '@cloudflare/workerd-linux-64': 1.20260401.1 + '@cloudflare/workerd-linux-arm64': 1.20260401.1 + '@cloudflare/workerd-windows-64': 1.20260401.1 - wrangler@4.67.0: + wrangler@4.80.0: dependencies: '@cloudflare/kv-asset-handler': 0.4.2 - '@cloudflare/unenv-preset': 2.14.0(unenv@2.0.0-rc.24)(workerd@1.20260219.0) + '@cloudflare/unenv-preset': 2.16.0(unenv@2.0.0-rc.24)(workerd@1.20260401.1) blake3-wasm: 2.1.5 esbuild: 0.27.3 - miniflare: 4.20260219.0 + miniflare: 4.20260401.0 path-to-regexp: 6.3.0 unenv: 2.0.0-rc.24 - workerd: 1.20260219.0 + workerd: 1.20260401.1 optionalDependencies: fsevents: 2.3.3 transitivePeerDependencies: @@ -3852,7 +3803,7 @@ snapshots: ws@8.18.0: {} - ws@8.19.0: {} + ws@8.20.0: {} yallist@3.1.1: {} @@ -3865,13 +3816,13 @@ snapshots: dependencies: '@poppinss/colors': 4.1.6 '@poppinss/dumper': 0.6.5 - '@speed-highlight/core': 1.2.14 + '@speed-highlight/core': 1.2.15 cookie: 1.1.1 youch-core: 0.3.3 - zile@0.0.21(typescript@5.9.3): + zile@0.0.24(typescript@5.9.3): dependencies: - '@clack/prompts': 1.0.1 + '@clack/prompts': 1.2.0 cac: 6.7.14 tsconfck: 3.1.6(typescript@5.9.3) optionalDependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 890977d..8ccb4a4 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -8,7 +8,7 @@ catalog: '@changesets/cli': latest '@types/node': latest '@vitest/browser-playwright': ^4.0.18 - typescript: latest + typescript: ^5 vitest: latest zile: latest diff --git a/src/_test/Registration.browser.test.ts b/src/_test/Registration.browser.test.ts index edce625..65fe4f1 100644 --- a/src/_test/Registration.browser.test.ts +++ b/src/_test/Registration.browser.test.ts @@ -1,6 +1,6 @@ import { expect, test } from 'vitest' import { Registration as Registration_client } from 'webauthx/client' -import { Registration as Registration_server } from 'webauthx/server' +import { Registration as Registration_server, Aaguid } from 'webauthx/server' import { rpId, rpName } from '../../test/constants.js' @@ -28,6 +28,8 @@ test('default', async () => { expect(result.credential.id).toBe(credential.id) expect(result.credential.publicKey).toBeDefined() expect(result.counter).toBeTypeOf('number') + expect(result.aaguid).toMatch(/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/) + expect(Aaguid.extract(credential)).toBe(result.aaguid) }) test('behavior: with user object', async () => { @@ -48,3 +50,29 @@ test('behavior: with user object', async () => { expect(result.credential.publicKey).toBeDefined() }) + +test('aaguid: lookup', async () => { + const fetchFn: typeof fetch = async () => + new Response( + JSON.stringify({ + '08987058-cadc-4b81-b6e1-30de50dcbe96': { name: 'Windows Hello' }, + }), + ) + + await expect( + Aaguid.lookup({ + fetchFn, + id: '08987058-CADC-4B81-B6E1-30DE50DCBE96', + remoteList: 'https://example.com/aaguid.json', + }), + ).resolves.toMatchObject({ + name: 'Windows Hello', + }) + await expect( + Aaguid.lookup({ + fetchFn, + id: '00000000-0000-0000-0000-000000000000', + remoteList: 'https://example.com/aaguid.json', + }), + ).resolves.toBeNull() +}) diff --git a/src/server/Aaguid.test.ts b/src/server/Aaguid.test.ts new file mode 100644 index 0000000..5c43ac0 --- /dev/null +++ b/src/server/Aaguid.test.ts @@ -0,0 +1,95 @@ +import { Base64, Hex, P256, PublicKey } from 'ox' +import { Authenticator } from 'ox/webauthn' +import { expect, test } from 'vitest' +import { Registration, Aaguid } from 'webauthx/server' + +test('extracts aaguid from verified registration credentials', () => { + const challenge = Hex.random(32) + const credentialId = Uint8Array.from([1, 2, 3, 4]) + const clientDataJSON = Authenticator.getClientDataJSON({ + challenge, + origin: 'https://example.com', + type: 'webauthn.create', + }) + const { publicKey } = P256.createKeyPair() + const authData = Authenticator.getAuthenticatorData({ + credential: { id: credentialId, publicKey }, + flag: 0x45, + rpId: 'example.com', + }) + const attestationObject = Authenticator.getAttestationObject({ authData }) + const id = Base64.fromBytes(credentialId, { pad: false, url: true }) + const response = { + attestationObject: Base64.fromHex(attestationObject, { pad: false, url: true }), + clientDataJSON: Base64.fromBytes(new TextEncoder().encode(clientDataJSON), { + pad: false, + url: true, + }), + id, + publicKey: PublicKey.toHex(publicKey), + raw: { + authenticatorAttachment: null, + id, + rawId: id, + response: { + attestationObject: Base64.fromHex(attestationObject, { + pad: false, + url: true, + }), + clientDataJSON: Base64.fromBytes(new TextEncoder().encode(clientDataJSON), { + pad: false, + url: true, + }), + }, + type: 'public-key', + } as never, + } satisfies Registration.Credential + + const result = Registration.verify(response, { + challenge, + origin: 'https://example.com', + rpId: 'example.com', + }) + + expect(result.aaguid).toBe('00000000-0000-0000-0000-000000000000') + expect(Aaguid.extract(response)).toBe(result.aaguid) +}) + +test('looks up authenticator metadata by normalized aaguid', async () => { + let calls = 0 + const fetchFn: typeof fetch = async () => ( + (calls += 1), + new Response( + JSON.stringify({ + '08987058-cadc-4b81-b6e1-30de50dcbe96': { name: 'Windows Hello' }, + }), + ) + ) + + await expect( + Aaguid.lookup({ + fetchFn, + id: '08987058-CADC-4B81-B6E1-30DE50DCBE96', + remoteList: 'https://example.com/aaguid.json', + }), + ).resolves.toMatchObject({ + name: 'Windows Hello', + }) + await expect( + Aaguid.lookup({ + fetchFn, + id: '00000000-0000-0000-0000-000000000000', + remoteList: 'https://example.com/aaguid.json', + }), + ).resolves.toBeNull() + await expect( + Aaguid.lookup({ + fetchFn, + id: '08987058-cadc-4b81-b6e1-30DE50DCBE96', + remoteList: 'https://example.com/aaguid.json', + }), + ).resolves.toMatchObject({ + name: 'Windows Hello', + }) + expect(calls).toBe(1) +}) diff --git a/src/server/Aaguid.ts b/src/server/Aaguid.ts new file mode 100644 index 0000000..9a576b2 --- /dev/null +++ b/src/server/Aaguid.ts @@ -0,0 +1,149 @@ +import { Base64, Cbor, Hex } from 'ox' + +import type { Credential } from './Registration.js' + +const cache = new Map>>() + +/** Default remote registry. */ +export const remoteList = + 'https://github.com/passkeydeveloper/passkey-authenticator-aaguids/raw/refs/heads/main/combined_aaguid.json' + +/** + * Extracts the AAGUID from a serialized registration credential. + * + * Returns `undefined` if the attestation object cannot be decoded. + * + * @example + * ```ts + * import { Aaguid } from 'webauthx/server' + * + * const aaguid = Aaguid.extract(credential) + * ``` + */ +export function extract(credential: Aaguid.extract['Options']): Aaguid.extract['ReturnType'] { + try { + const { authData } = Cbor.decode<{ authData: Uint8Array }>( + Base64.toBytes(credential.attestationObject), + ) + if (authData.length < 53) return undefined + return format(authData.subarray(37, 53)) + } catch { + return undefined + } +} + +/** + * Looks up friendly authenticator metadata from a remote AAGUID registry. + * + * @example + * ```ts + * import { Aaguid } from 'webauthx/server' + * + * const info = await Aaguid.lookup({ + * id: '08987058-cadc-4b81-b6e1-30de50dcbe96', + * }) + * ``` + */ +export async function lookup( + options: Aaguid.lookup['Options'], +): Promise { + const registry = await getRegistry(options) + return registry[normalize(options.id)] ?? null +} + +function format(bytes: Uint8Array): Aaguid.Aaguid { + const hex = Hex.fromBytes(bytes).slice(2) + return [ + hex.slice(0, 8), + hex.slice(8, 12), + hex.slice(12, 16), + hex.slice(16, 20), + hex.slice(20, 32), + ].join('-') +} + +export declare namespace Aaguid { + export type Aaguid = string + + /** Authenticator metadata. */ + export type Info = { + name: string + iconLight?: string | undefined + iconDark?: string | undefined + } + + type extract = { + Options: Credential + ReturnType: Aaguid | undefined + ErrorType: Base64.toBytes.ErrorType | Cbor.decode.ErrorType + } + + type lookup = { + Options: { + cache?: boolean | undefined + fetchFn?: typeof fetch | undefined + id: Aaguid + remoteList?: string | undefined + } + ReturnType: Info | null + ErrorType: Error + } +} + +async function fetchRegistry( + options: Pick = {}, +): Promise> { + const fetchFn = options.fetchFn ?? globalThis.fetch + if (!fetchFn) throw new Error('`fetch` is not available in this environment.') + + const response = await fetchFn(options.remoteList ?? remoteList) + if (!response.ok) + throw new Error(`Failed to fetch AAGUID registry: ${response.status} ${response.statusText}`) + + const json = (await response.json()) as Record< + string, + { + name: string + icon_dark?: string | undefined + icon_light?: string | undefined + iconDark?: string | undefined + iconLight?: string | undefined + } + > + + const registry: Record = {} + for (const [id, info] of Object.entries(json)) { + registry[normalize(id)] = { + name: info.name, + ...(info.iconLight || info.icon_light + ? { iconLight: info.iconLight ?? info.icon_light } + : {}), + ...(info.iconDark || info.icon_dark ? { iconDark: info.iconDark ?? info.icon_dark } : {}), + } + } + return registry +} + +async function getRegistry( + options: Pick, +): Promise> { + const list = options.remoteList ?? remoteList + if (options.cache === false) return fetchRegistry({ fetchFn: options.fetchFn, remoteList: list }) + + const cached = cache.get(list) + if (cached) return cached + + const registry = fetchRegistry({ + fetchFn: options.fetchFn, + remoteList: list, + }).catch((error) => { + cache.delete(list) + throw error + }) + cache.set(list, registry) + return registry +} + +function normalize(aaguid: string): Aaguid.Aaguid { + return aaguid.toLowerCase() +} diff --git a/src/server/Registration.ts b/src/server/Registration.ts index 17e8105..bd63cab 100644 --- a/src/server/Registration.ts +++ b/src/server/Registration.ts @@ -1,6 +1,8 @@ import { Hex } from 'ox' import { Credential as Credential_, Registration, type Types } from 'ox/webauthn' +import * as Aaguid from './Aaguid.js' + /** Credential. */ export type Credential = Credential_.Credential @@ -8,7 +10,9 @@ export type Credential = Credential_.Credential export type Options = Types.CredentialCreationOptions /** Registration response. */ -export type Response = Registration.Response +export type Response = Registration.Response & { + aaguid: string | undefined +} /** * Generates serialized `PublicKeyCredentialCreationOptions` for registration. @@ -72,7 +76,10 @@ export declare namespace getOptions { export function verify(credential: Credential, options: verify.Options): Response { const deserialized = Credential_.deserialize(credential) const result = Registration.verify({ ...options, credential: deserialized }) - return Registration.serializeResponse(result) + return { + ...Registration.serializeResponse(result), + aaguid: Aaguid.extract(credential), + } } export declare namespace verify { diff --git a/src/server/index.ts b/src/server/index.ts index b93fb45..9b476fc 100644 --- a/src/server/index.ts +++ b/src/server/index.ts @@ -1,2 +1,3 @@ export * as Authentication from './Authentication.js' export * as Registration from './Registration.js' +export * as Aaguid from './Aaguid.js' diff --git a/vitest.config.ts b/vitest.config.ts index 34fac8e..dd5666a 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -11,6 +11,14 @@ export default defineConfig({ }, globals: true, projects: [ + { + extends: true, + test: { + name: 'unit', + include: ['src/**/*.test.ts'], + exclude: ['src/**/*.browser.test.ts'], + }, + }, { extends: true, test: {