From eb017961831a35b3c00d25b0ace869c7fc2d05d5 Mon Sep 17 00:00:00 2001 From: Mike Rudat Date: Sat, 18 Jul 2026 20:49:15 +0000 Subject: [PATCH 01/11] feat: implement SDKCore.startLogin() for DPoP authorization code grant (ENG-4786) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add Pkce module (generateCodeVerifier, generateCodeChallenge) with RFC 7636 Appendix B test vector coverage; runs under @vitest-environment node - Extend RedirectHelper to persist code_verifier as a second colon-delimited segment alongside state; add public getCodeVerifier() getter; add test file - SDKCore: construct DPoPManager when config.useDpop is true; startLogin() is now async — DPoP branch calls getOrCreateKeyPair()/getThumbprint() and generates PKCE params then redirects to /oauth2/authorize directly; isLoggedIn delegates to DPoPManager.isLoggedIn in DPoP mode (not app.at_exp cookie) - SDKCore.test.ts: add DPoP-mode describe block with mocked DPoPManager and Pkce (jsdom lacks crypto.subtle); all existing cookie-mode tests unaffected - e2e/dpop-smoke.test.ts: replace local generatePkce() helper with shared Pkce module; add Tier 0 tests exercising SDKCore.startLogin() in DPoP mode end-to-end (no live FusionAuth required for Tier 0) - Export Pkce from packages/core/src/index.ts Note: yarn test:core cannot run in this sandbox environment due to a missing @rollup/rollup-linux-arm64-gnu native binary (arch mismatch); TypeScript compilation (tsc --noEmit) and ESLint/Prettier are clean. --- e2e/tests/dpop-smoke.test.ts | 228 +++++++++++++++--- packages/core/src/Pkce/Pkce.test.ts | 75 ++++++ packages/core/src/Pkce/Pkce.ts | 51 ++++ packages/core/src/Pkce/index.ts | 1 + .../src/RedirectHelper/RedirectHelper.test.ts | 143 +++++++++++ .../core/src/RedirectHelper/RedirectHelper.ts | 63 ++++- packages/core/src/SDKCore/SDKCore.test.ts | 167 ++++++++++++- packages/core/src/SDKCore/SDKCore.ts | 52 +++- packages/core/src/index.ts | 1 + 9 files changed, 741 insertions(+), 40 deletions(-) create mode 100644 packages/core/src/Pkce/Pkce.test.ts create mode 100644 packages/core/src/Pkce/Pkce.ts create mode 100644 packages/core/src/Pkce/index.ts create mode 100644 packages/core/src/RedirectHelper/RedirectHelper.test.ts diff --git a/e2e/tests/dpop-smoke.test.ts b/e2e/tests/dpop-smoke.test.ts index 23a6753..91920d2 100644 --- a/e2e/tests/dpop-smoke.test.ts +++ b/e2e/tests/dpop-smoke.test.ts @@ -1,17 +1,20 @@ /** - * DPoP Smoke Tests — pre-SDKCore wiring + * DPoP Smoke Tests — pre-SDKCore wiring + SDKCore.startLogin() integration * - * Exercises DPoPManager + UrlHelper directly against a real FusionAuth - * Enterprise instance. No quickstart app is needed — the tests drive - * FusionAuth's hosted login UI via Playwright, capture the authorization - * code from the redirect, and perform all token operations in the Node - * test process. + * Tier 0 (new): Exercises SDKCore.startLogin() in DPoP mode without a live + * FusionAuth instance. Stubs window/localStorage/indexedDB to create a real + * SDKCore, calls startLogin(), and asserts the authorize URL shape and + * code_verifier persistence. + * + * Tier 1–2 (existing): Exercise DPoPManager + UrlHelper directly against a + * real FusionAuth Enterprise instance. No quickstart app is needed — the tests + * drive FusionAuth's hosted login UI via Playwright. * * Run with: * npx playwright test e2e/tests/dpop-smoke.test.ts \ * --config playwright.dpop.config.ts * - * Prerequisites: + * Prerequisites (Tier 1–2 only): * - FusionAuth Enterprise instance running at http://localhost:9011 * - Application baf3d520-40d7-4000-9b62-e6a7d0091102 configured with: * proofKeyForCodeExchangePolicy: Required @@ -25,6 +28,12 @@ import { IDBFactory } from 'fake-indexeddb'; import { DPoPManager } from '../../packages/core/src/DPoP/DPoPManager'; import { DPoPTokens } from '../../packages/core/src/DPoP/DPoPTokenStore'; import { UrlHelper } from '../../packages/core/src/UrlHelper/UrlHelper'; +import { SDKCore } from '../../packages/core/src/SDKCore/SDKCore'; +import { RedirectHelper } from '../../packages/core/src/RedirectHelper/RedirectHelper'; +import { + generateCodeVerifier, + generateCodeChallenge, +} from '../../packages/core/src/Pkce/Pkce'; // --------------------------------------------------------------------------- // Config @@ -54,24 +63,6 @@ function decodeJwt(jwt: string): Record { ); } -/** Generate a PKCE code_verifier and code_challenge (SHA-256 / base64url). */ -async function generatePkce(): Promise<{ - verifier: string; - challenge: string; -}> { - const array = new Uint8Array(32); - crypto.getRandomValues(array); - const verifier = Buffer.from(array).toString('base64url'); - - const hash = await crypto.subtle.digest( - 'SHA-256', - Buffer.from(verifier, 'ascii'), - ); - const challenge = Buffer.from(hash).toString('base64url'); - - return { verifier, challenge }; -} - /** Build a fresh DPoPManager backed by fake-indexeddb (no browser required in Node). */ function makeManager(): DPoPManager { // @ts-ignore — Node has no native indexedDB; fake-indexeddb fills the gap. @@ -158,7 +149,186 @@ async function loginAndCaptureCode( } // --------------------------------------------------------------------------- -// Tests +// Tier 0 — SDKCore.startLogin() DPoP mode (no live FusionAuth required) +// --------------------------------------------------------------------------- + +test.describe('Tier 0: SDKCore.startLogin() DPoP mode', () => { + // Provide browser-API polyfills required by SDKCore and its dependencies + // when running in Node (Playwright's test process is Node, not a browser). + test.beforeAll(() => { + // IndexedDB — required by DPoPStorage (via DPoPManager). + // @ts-ignore + globalThis.indexedDB = new IDBFactory(); + + // localStorage — required by RedirectHelper and DPoPTokenStore. + if (typeof globalThis.localStorage === 'undefined') { + const store: Record = {}; + // @ts-ignore + globalThis.localStorage = { + getItem: (k: string) => store[k] ?? null, + setItem: (k: string, v: string) => { + store[k] = v; + }, + removeItem: (k: string) => { + delete store[k]; + }, + clear: () => { + for (const k in store) delete store[k]; + }, + }; + } + + // window — SDKCore calls window.location.assign and DPoPManager uses + // indexedDB / crypto globals that browsers expose via window. We stub + // window with the minimal surface SDKCore touches. + if (typeof globalThis.window === 'undefined') { + // @ts-ignore + globalThis.window = { + location: { assign: () => {} }, + crypto: globalThis.crypto, + }; + } + }); + + test.afterEach(() => { + // Clear localStorage between tests so each starts clean. + globalThis.localStorage.clear(); + // Fresh IndexedDB so key-pair state doesn't leak across tests. + // @ts-ignore + globalThis.indexedDB = new IDBFactory(); + }); + + test('T0-1: startLogin() redirects to /oauth2/authorize with dpop_jkt and code_challenge', async () => { + let assignedUrl: string | null = null; + // @ts-ignore + globalThis.window.location = { + assign: (url: string) => { + assignedUrl = url; + }, + }; + + const core = new SDKCore({ + serverUrl: FA_URL, + clientId: CLIENT_ID, + redirectUri: REDIRECT_URI, + scope: SCOPE, + useDpop: true, + dpopTokenStorage: 'memory', + onTokenExpiration: () => {}, + }); + + await core.startLogin(); + + expect(assignedUrl).not.toBeNull(); + const url = new URL(assignedUrl!); + + expect(url.origin).toBe(FA_URL); + expect(url.pathname).toBe('/oauth2/authorize'); + expect(url.searchParams.get('response_type')).toBe('code'); + expect(url.searchParams.get('client_id')).toBe(CLIENT_ID); + expect(url.searchParams.get('redirect_uri')).toBe(REDIRECT_URI); + expect(url.searchParams.get('code_challenge_method')).toBe('S256'); + + const dpopJkt = url.searchParams.get('dpop_jkt'); + const codeChallenge = url.searchParams.get('code_challenge'); + + // dpop_jkt: base64url JWK thumbprint — 43 chars, valid base64url charset. + expect(dpopJkt).not.toBeNull(); + expect(dpopJkt).toMatch(/^[A-Za-z0-9\-_]{43}$/); + + // code_challenge: base64url SHA-256 — 43 chars, valid base64url charset. + expect(codeChallenge).not.toBeNull(); + expect(codeChallenge).toMatch(/^[A-Za-z0-9\-_]{43}$/); + }); + + test('T0-2: startLogin() persists code_verifier and state via RedirectHelper', async () => { + const STATE = 'e2e-smoke-state'; + let assignedUrl: string | null = null; + // @ts-ignore + globalThis.window.location = { + assign: (url: string) => { + assignedUrl = url; + }, + }; + + const core = new SDKCore({ + serverUrl: FA_URL, + clientId: CLIENT_ID, + redirectUri: REDIRECT_URI, + scope: SCOPE, + useDpop: true, + dpopTokenStorage: 'memory', + onTokenExpiration: () => {}, + }); + + await core.startLogin(STATE); + + expect(assignedUrl).not.toBeNull(); + const url = new URL(assignedUrl!); + + // State is included in the authorize URL. + expect(url.searchParams.get('state')).toBe(STATE); + + // code_verifier is persisted via RedirectHelper so the post-redirect + // handler (ENG-4800) can retrieve it for the token exchange. + const redirectHelper = new RedirectHelper(); + const storedVerifier = redirectHelper.getCodeVerifier(); + expect(storedVerifier).not.toBeUndefined(); + expect(storedVerifier).toMatch(/^[A-Za-z0-9\-_]{43}$/); + + // The stored code_verifier must produce the code_challenge in the URL. + const expectedChallenge = await generateCodeChallenge(storedVerifier!); + expect(url.searchParams.get('code_challenge')).toBe(expectedChallenge); + }); + + test('T0-3: two startLogin() calls produce different key pairs and PKCE values', async () => { + const urls: URL[] = []; + // @ts-ignore + globalThis.window.location = { + assign: (url: string) => { + urls.push(new URL(url)); + }, + }; + + const core1 = new SDKCore({ + serverUrl: FA_URL, + clientId: CLIENT_ID, + redirectUri: REDIRECT_URI, + useDpop: true, + dpopTokenStorage: 'memory', + onTokenExpiration: () => {}, + }); + + // Each SDKCore gets its own DPoPManager with its own key pair. + // @ts-ignore + globalThis.indexedDB = new IDBFactory(); + + const core2 = new SDKCore({ + serverUrl: FA_URL, + clientId: CLIENT_ID, + redirectUri: REDIRECT_URI, + useDpop: true, + dpopTokenStorage: 'memory', + onTokenExpiration: () => {}, + }); + + await core1.startLogin(); + globalThis.localStorage.clear(); + // @ts-ignore + globalThis.indexedDB = new IDBFactory(); + await core2.startLogin(); + + expect(urls).toHaveLength(2); + // Different key pairs → different dpop_jkt. + // Different PKCE verifiers → different code_challenge. + expect(urls[0]!.searchParams.get('code_challenge')).not.toBe( + urls[1]!.searchParams.get('code_challenge'), + ); + }); +}); + +// --------------------------------------------------------------------------- +// Tier 1 — Authorization code flow // --------------------------------------------------------------------------- test.describe('DPoP smoke tests', () => { @@ -191,7 +361,8 @@ test.describe('DPoP smoke tests', () => { test('T1-1: getAuthorizeUrl() produces a URL FusionAuth accepts (login page rendered)', async () => { thumbprint = await manager.getThumbprint(); - const { challenge } = await generatePkce(); + const verifier = generateCodeVerifier(); + const challenge = await generateCodeChallenge(verifier); const urlHelper = new UrlHelper({ serverUrl: FA_URL, @@ -213,7 +384,8 @@ test.describe('DPoP smoke tests', () => { }); test('T1-2: full authorization code exchange — token_type is DPoP, cnf.jkt matches thumbprint', async () => { - const { verifier, challenge } = await generatePkce(); + const verifier = generateCodeVerifier(); + const challenge = await generateCodeChallenge(verifier); thumbprint = await manager.getThumbprint(); const urlHelper = new UrlHelper({ diff --git a/packages/core/src/Pkce/Pkce.test.ts b/packages/core/src/Pkce/Pkce.test.ts new file mode 100644 index 0000000..2ac09b6 --- /dev/null +++ b/packages/core/src/Pkce/Pkce.test.ts @@ -0,0 +1,75 @@ +// @vitest-environment node +// crypto.subtle.digest requires a spec-compliant SubtleCrypto implementation. +// The jsdom environment's crypto only provides getRandomValues/randomUUID. +// The node environment has a full WebCrypto implementation, matching the +// browser target for which this code is written. + +import { describe, it, expect } from 'vitest'; +import { generateCodeVerifier, generateCodeChallenge } from './Pkce'; + +// --------------------------------------------------------------------------- +// RFC 7636 Appendix B test vector +// verifier: dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk +// challenge: E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM +// (Same vector referenced in UrlHelper.test.ts) +// --------------------------------------------------------------------------- +const RFC_VERIFIER = 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'; +const RFC_CHALLENGE = 'E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM'; + +describe('Pkce', () => { + describe('generateCodeVerifier', () => { + it('returns a base64url string of 43 characters (32 bytes encoded)', () => { + const verifier = generateCodeVerifier(); + expect(verifier).toHaveLength(43); + }); + + it('uses only base64url characters [A-Za-z0-9\\-_]', () => { + const verifier = generateCodeVerifier(); + expect(verifier).toMatch(/^[A-Za-z0-9\-_]+$/); + }); + + it('contains no base64 padding characters', () => { + const verifier = generateCodeVerifier(); + expect(verifier).not.toContain('='); + }); + + it('produces a different value on each call (collision-resistant)', () => { + const a = generateCodeVerifier(); + const b = generateCodeVerifier(); + expect(a).not.toBe(b); + }); + }); + + describe('generateCodeChallenge', () => { + it('matches the RFC 7636 Appendix B test vector', async () => { + const challenge = await generateCodeChallenge(RFC_VERIFIER); + expect(challenge).toBe(RFC_CHALLENGE); + }); + + it('returns a base64url string with no padding', async () => { + const verifier = generateCodeVerifier(); + const challenge = await generateCodeChallenge(verifier); + expect(challenge).toMatch(/^[A-Za-z0-9\-_]+$/); + expect(challenge).not.toContain('='); + }); + + it('returns a 43-character string (SHA-256 → 32 bytes → base64url)', async () => { + const verifier = generateCodeVerifier(); + const challenge = await generateCodeChallenge(verifier); + expect(challenge).toHaveLength(43); + }); + + it('produces the same challenge for the same verifier (deterministic)', async () => { + const verifier = generateCodeVerifier(); + const a = await generateCodeChallenge(verifier); + const b = await generateCodeChallenge(verifier); + expect(a).toBe(b); + }); + + it('produces different challenges for different verifiers', async () => { + const a = await generateCodeChallenge(generateCodeVerifier()); + const b = await generateCodeChallenge(generateCodeVerifier()); + expect(a).not.toBe(b); + }); + }); +}); diff --git a/packages/core/src/Pkce/Pkce.ts b/packages/core/src/Pkce/Pkce.ts new file mode 100644 index 0000000..fc05cbb --- /dev/null +++ b/packages/core/src/Pkce/Pkce.ts @@ -0,0 +1,51 @@ +/** + * PKCE (Proof Key for Code Exchange) utilities — RFC 7636. + * + * Uses the bare global `crypto` object (available as `globalThis.crypto` in + * browsers, Node 19+, and the vitest node environment) rather than + * `window.crypto` so that both browser and non-browser environments can call + * these functions without mocking. + */ + +/** + * Generates a cryptographically random PKCE `code_verifier`. + * + * The verifier is 32 random bytes encoded as base64url (43 characters), + * satisfying RFC 7636 §4.1 (43–128 characters from the unreserved character + * set `[A-Z a-z 0-9 - . _ ~]`). + */ +export function generateCodeVerifier(): string { + const array = new Uint8Array(32); + crypto.getRandomValues(array); + return base64urlEncode(array); +} + +/** + * Computes the PKCE `code_challenge` from a `code_verifier`. + * + * `code_challenge = BASE64URL(SHA-256(ASCII(code_verifier)))` — RFC 7636 §4.2. + * + * @param verifier A `code_verifier` generated by {@link generateCodeVerifier}. + */ +export async function generateCodeChallenge(verifier: string): Promise { + const data = new TextEncoder().encode(verifier); + const hash = await crypto.subtle.digest('SHA-256', data); + return base64urlEncode(new Uint8Array(hash)); +} + +// --------------------------------------------------------------------------- +// Private helpers +// --------------------------------------------------------------------------- + +/** Encodes a `Uint8Array` as a base64url string (no padding). */ +function base64urlEncode(bytes: Uint8Array): string { + // Convert bytes to a binary string, then btoa, then convert to base64url. + let binary = ''; + for (let i = 0; i < bytes.length; i++) { + binary += String.fromCharCode(bytes[i]!); + } + return btoa(binary) + .replace(/\+/g, '-') + .replace(/\//g, '_') + .replace(/=+$/, ''); +} diff --git a/packages/core/src/Pkce/index.ts b/packages/core/src/Pkce/index.ts new file mode 100644 index 0000000..5b82d5c --- /dev/null +++ b/packages/core/src/Pkce/index.ts @@ -0,0 +1 @@ +export * from './Pkce'; diff --git a/packages/core/src/RedirectHelper/RedirectHelper.test.ts b/packages/core/src/RedirectHelper/RedirectHelper.test.ts new file mode 100644 index 0000000..f147ee0 --- /dev/null +++ b/packages/core/src/RedirectHelper/RedirectHelper.test.ts @@ -0,0 +1,143 @@ +import { afterEach, describe, expect, it, vi } from 'vitest'; +import { RedirectHelper } from './RedirectHelper'; + +describe('RedirectHelper', () => { + afterEach(() => { + localStorage.clear(); + vi.restoreAllMocks(); + }); + + // --------------------------------------------------------------------------- + // Cookie-mode (non-DPoP) — backward-compatibility + // --------------------------------------------------------------------------- + + describe('handlePreRedirect / handlePostRedirect (cookie mode)', () => { + it('stores a redirect marker in localStorage', () => { + const helper = new RedirectHelper(); + expect(localStorage.getItem('fa-sdk-redirect-value')).toBeNull(); + + helper.handlePreRedirect(); + expect(localStorage.getItem('fa-sdk-redirect-value')).not.toBeNull(); + }); + + it('invokes the callback with undefined when no state was provided', () => { + const helper = new RedirectHelper(); + const callback = vi.fn(); + + helper.handlePreRedirect(); + helper.handlePostRedirect(callback); + + expect(callback).toHaveBeenCalledOnce(); + expect(callback).toHaveBeenCalledWith(undefined); + }); + + it('invokes the callback with the stored state', () => { + const helper = new RedirectHelper(); + const callback = vi.fn(); + + helper.handlePreRedirect('my-state'); + helper.handlePostRedirect(callback); + + expect(callback).toHaveBeenCalledWith('my-state'); + }); + + it('preserves state values that contain colons', () => { + const helper = new RedirectHelper(); + const callback = vi.fn(); + const stateWithColons = 'return:/dashboard?tab=2'; + + helper.handlePreRedirect(stateWithColons); + helper.handlePostRedirect(callback); + + expect(callback).toHaveBeenCalledWith(stateWithColons); + }); + + it('removes the redirect marker from localStorage after post-redirect', () => { + const helper = new RedirectHelper(); + + helper.handlePreRedirect('some-state'); + expect(localStorage.getItem('fa-sdk-redirect-value')).not.toBeNull(); + + helper.handlePostRedirect(); + expect(localStorage.getItem('fa-sdk-redirect-value')).toBeNull(); + }); + + it('does not invoke callback when no redirect was initiated', () => { + const helper = new RedirectHelper(); + const callback = vi.fn(); + + helper.handlePostRedirect(callback); + + expect(callback).not.toHaveBeenCalled(); + }); + + it('does not throw when no callback is provided to handlePostRedirect', () => { + const helper = new RedirectHelper(); + helper.handlePreRedirect('state'); + expect(() => helper.handlePostRedirect()).not.toThrow(); + }); + }); + + // --------------------------------------------------------------------------- + // DPoP mode — code_verifier persistence + // --------------------------------------------------------------------------- + + describe('handlePreRedirect with codeVerifier (DPoP mode)', () => { + it('persists the code_verifier and returns it via getCodeVerifier()', () => { + const helper = new RedirectHelper(); + const verifier = 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'; + + helper.handlePreRedirect(undefined, verifier); + + expect(helper.getCodeVerifier()).toBe(verifier); + }); + + it('persists both code_verifier and state; both are retrievable', () => { + const helper = new RedirectHelper(); + const verifier = 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'; + const state = 'my-state'; + const callback = vi.fn(); + + helper.handlePreRedirect(state, verifier); + + expect(helper.getCodeVerifier()).toBe(verifier); + helper.handlePostRedirect(callback); + expect(callback).toHaveBeenCalledWith(state); + }); + + it('preserves state with colons alongside a code_verifier', () => { + const helper = new RedirectHelper(); + const verifier = 'dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk'; + const stateWithColons = 'return:/dashboard?tab=2'; + const callback = vi.fn(); + + helper.handlePreRedirect(stateWithColons, verifier); + + expect(helper.getCodeVerifier()).toBe(verifier); + helper.handlePostRedirect(callback); + expect(callback).toHaveBeenCalledWith(stateWithColons); + }); + + it('returns undefined from getCodeVerifier() when no redirect was initiated', () => { + const helper = new RedirectHelper(); + expect(helper.getCodeVerifier()).toBeUndefined(); + }); + + it('returns undefined from getCodeVerifier() when no verifier was stored (cookie mode)', () => { + const helper = new RedirectHelper(); + helper.handlePreRedirect('some-state'); + expect(helper.getCodeVerifier()).toBeUndefined(); + }); + + it('getCodeVerifier() still works after a handlePostRedirect call clears storage', () => { + const helper = new RedirectHelper(); + helper.handlePreRedirect('state', 'verifier-value'); + + // Post-redirect removes the marker. + helper.handlePostRedirect(); + + // After cleanup, getCodeVerifier() should return undefined. + expect(helper.getCodeVerifier()).toBeUndefined(); + }); + }); +}); diff --git a/packages/core/src/RedirectHelper/RedirectHelper.ts b/packages/core/src/RedirectHelper/RedirectHelper.ts index b2e9323..d7e5030 100644 --- a/packages/core/src/RedirectHelper/RedirectHelper.ts +++ b/packages/core/src/RedirectHelper/RedirectHelper.ts @@ -1,6 +1,19 @@ -/** A class responsible for storing a redirect value in localStorage and cleanup afterward. */ +/** + * A class responsible for storing pre-redirect values in localStorage and + * cleaning them up afterward. + * + * Storage format: `${randomNonce}:${codeVerifier ?? ''}:${state ?? ''}` + * + * - `randomNonce` — prevents replay; marks that a redirect was initiated. + * - `codeVerifier` — PKCE `code_verifier` persisted for DPoP token exchange + * (ENG-4800). Empty string when not in DPoP mode. + * - `state` — optional caller-supplied OAuth2 state value. May contain + * colons; retrieved by joining all segments after index 1 + * (skipping the verifier segment). + */ export class RedirectHelper { private readonly REDIRECT_VALUE = 'fa-sdk-redirect-value'; + private get storage(): Storage { try { return localStorage; @@ -9,15 +22,24 @@ export class RedirectHelper { return { /* eslint-disable */ setItem(_key: string, _value: string) {}, - getItem(_key: string) {}, + getItem(_key: string) { + return null; + }, removeItem(_key: string) {}, /* eslint-enable */ } as Storage; } } - handlePreRedirect(state?: string) { - const valueForStorage = `${this.generateRandomString()}:${state ?? ''}`; + /** + * Persists a redirect marker, an optional PKCE `code_verifier`, and an + * optional `state` value to localStorage before a redirect is initiated. + * + * @param state Optional OAuth2 state string echoed back post-login. + * @param codeVerifier Optional PKCE `code_verifier` (DPoP mode only). + */ + handlePreRedirect(state?: string, codeVerifier?: string) { + const valueForStorage = `${this.generateRandomString()}:${codeVerifier ?? ''}:${state ?? ''}`; this.storage.setItem(this.REDIRECT_VALUE, valueForStorage); } @@ -32,15 +54,36 @@ export class RedirectHelper { this.storage.removeItem(this.REDIRECT_VALUE); } + /** + * Returns the PKCE `code_verifier` that was persisted by + * {@link handlePreRedirect}, or `undefined` if none was stored (cookie mode) + * or if no redirect has been initiated. + */ + getCodeVerifier(): string | undefined { + const raw = this.storage.getItem(this.REDIRECT_VALUE); + if (!raw) return undefined; + + // Format: randomNonce:codeVerifier:state + const firstColon = raw.indexOf(':'); + if (firstColon === -1) return undefined; + + const afterNonce = raw.slice(firstColon + 1); + const secondColon = afterNonce.indexOf(':'); + if (secondColon === -1) return undefined; + + const verifier = afterNonce.slice(0, secondColon); + return verifier || undefined; + } + private get state() { const redirectValue = this.storage.getItem(this.REDIRECT_VALUE); + if (!redirectValue) return undefined; - if (!redirectValue) { - return; - } - - const [, ...stateValue] = redirectValue.split(':'); - return stateValue.join(':') || undefined; + // Format: randomNonce:codeVerifier:state + // Skip the nonce segment and the codeVerifier segment; join remainder with + // ':' to correctly reconstruct state values that themselves contain colons. + const [, , ...stateSegments] = redirectValue.split(':'); + return stateSegments.join(':') || undefined; } private generateRandomString() { diff --git a/packages/core/src/SDKCore/SDKCore.test.ts b/packages/core/src/SDKCore/SDKCore.test.ts index a427fd3..721be85 100644 --- a/packages/core/src/SDKCore/SDKCore.test.ts +++ b/packages/core/src/SDKCore/SDKCore.test.ts @@ -3,6 +3,8 @@ import { afterEach, describe, it, expect, vi } from 'vitest'; import { SDKConfig } from '../SDKConfig'; import { SDKCore } from '.'; import { RedirectHelper } from '../RedirectHelper'; +import { DPoPManager } from '../DPoP'; +import * as Pkce from '../Pkce'; import { mockIsLoggedIn, mockWindowLocation, removeAt_expCookie } from '..'; @@ -131,10 +133,12 @@ describe('SDKCore', () => { expect(handlePreRedirect).toHaveBeenCalledTimes(0); + // startLogin is async but cookie-mode branch has no awaits — synchronous + // side-effects (handlePreRedirect, window.location.assign) fire immediately. core.startLogin('/login'); core.startRegister(); - expect(handlePreRedirect).toHaveBeenNthCalledWith(1, '/login'); + expect(handlePreRedirect).toHaveBeenNthCalledWith(1, '/login', undefined); expect(handlePreRedirect).toHaveBeenNthCalledWith(2, undefined); }); @@ -165,4 +169,165 @@ describe('SDKCore', () => { expect(onRedirect).not.toHaveBeenCalled(); }); + + // --------------------------------------------------------------------------- + // DPoP mode + // + // DPoPManager methods and Pkce functions are mocked here because jsdom's + // crypto implementation lacks `crypto.subtle`, which is required for both + // DPoP key-pair generation and PKCE SHA-256 challenge derivation. The + // per-module unit tests (DPoPManager.test.ts, Pkce.test.ts) run under + // @vitest-environment node where real WebCrypto is available and verify the + // cryptographic correctness of those operations. + // --------------------------------------------------------------------------- + + describe('DPoP mode', () => { + const MOCK_JKT = 'mock-dpop-jkt-thumbprint'; + const MOCK_VERIFIER = 'mock-code-verifier-43-chars-xxxxxxxxxxxxxxxx'; + const MOCK_CHALLENGE = 'mock-code-challenge-43-chars-xxxxxxxxxxxx'; + + const dpopConfig: SDKConfig = { + ...config, + useDpop: true, + serverUrl: 'http://my-fusionauth-server', + }; + + it('constructs a DPoPManager when useDpop is true', () => { + // The constructor call itself is the assertion: if it throws, the test + // fails. We also verify the DPoP-mode isLoggedIn path is used (not cookie). + vi.spyOn(DPoPManager.prototype, 'getOrCreateKeyPair').mockResolvedValue( + {} as any, + ); + vi.spyOn(DPoPManager.prototype, 'getThumbprint').mockResolvedValue( + MOCK_JKT, + ); + vi.spyOn(Pkce, 'generateCodeVerifier').mockReturnValue(MOCK_VERIFIER); + vi.spyOn(Pkce, 'generateCodeChallenge').mockResolvedValue(MOCK_CHALLENGE); + + // DPoPManager.isLoggedIn returns false by default (no tokens stored). + const core = new SDKCore(dpopConfig); + expect(core.isLoggedIn).toBe(false); + }); + + it('does not construct a DPoPManager when useDpop is false (default)', () => { + // isLoggedIn should fall back to cookie path (no tokens, no cookie → false) + const core = new SDKCore(config); + expect(core.isLoggedIn).toBe(false); + }); + + it('isLoggedIn reads from DPoPTokenStore (not app.at_exp cookie) in DPoP mode', () => { + // Cookie is present but DPoP mode should NOT consult it. + mockIsLoggedIn(); // sets app.at_exp cookie → cookie-mode isLoggedIn = true + vi.spyOn(DPoPManager.prototype, 'getOrCreateKeyPair').mockResolvedValue( + {} as any, + ); + + const core = new SDKCore(dpopConfig); + + // DPoP tokens are not stored, so isLoggedIn is false even though the + // app.at_exp cookie says the user is logged in. + expect(core.isLoggedIn).toBe(false); + }); + + it('startLogin() in DPoP mode redirects to /oauth2/authorize with dpop_jkt and code_challenge', async () => { + vi.spyOn(DPoPManager.prototype, 'getOrCreateKeyPair').mockResolvedValue( + {} as any, + ); + vi.spyOn(DPoPManager.prototype, 'getThumbprint').mockResolvedValue( + MOCK_JKT, + ); + vi.spyOn(Pkce, 'generateCodeVerifier').mockReturnValue(MOCK_VERIFIER); + vi.spyOn(Pkce, 'generateCodeChallenge').mockResolvedValue(MOCK_CHALLENGE); + const location = mockWindowLocation(vi); + + const core = new SDKCore(dpopConfig); + await core.startLogin(); + + expect(location.assign).toHaveBeenCalledOnce(); + const assignedUrl = new URL( + (location.assign as ReturnType).mock.calls[0][0], + ); + expect(assignedUrl.pathname).toBe('/oauth2/authorize'); + expect(assignedUrl.searchParams.get('dpop_jkt')).toBe(MOCK_JKT); + expect(assignedUrl.searchParams.get('code_challenge')).toBe( + MOCK_CHALLENGE, + ); + expect(assignedUrl.searchParams.get('code_challenge_method')).toBe( + 'S256', + ); + expect(assignedUrl.searchParams.get('response_type')).toBe('code'); + }); + + it('startLogin() in DPoP mode persists code_verifier via RedirectHelper', async () => { + vi.spyOn(DPoPManager.prototype, 'getOrCreateKeyPair').mockResolvedValue( + {} as any, + ); + vi.spyOn(DPoPManager.prototype, 'getThumbprint').mockResolvedValue( + MOCK_JKT, + ); + vi.spyOn(Pkce, 'generateCodeVerifier').mockReturnValue(MOCK_VERIFIER); + vi.spyOn(Pkce, 'generateCodeChallenge').mockResolvedValue(MOCK_CHALLENGE); + mockWindowLocation(vi); + + const core = new SDKCore(dpopConfig); + await core.startLogin(); + + const redirectHelper = new RedirectHelper(); + expect(redirectHelper.getCodeVerifier()).toBe(MOCK_VERIFIER); + }); + + it('startLogin() in DPoP mode includes state in the authorize URL', async () => { + vi.spyOn(DPoPManager.prototype, 'getOrCreateKeyPair').mockResolvedValue( + {} as any, + ); + vi.spyOn(DPoPManager.prototype, 'getThumbprint').mockResolvedValue( + MOCK_JKT, + ); + vi.spyOn(Pkce, 'generateCodeVerifier').mockReturnValue(MOCK_VERIFIER); + vi.spyOn(Pkce, 'generateCodeChallenge').mockResolvedValue(MOCK_CHALLENGE); + const location = mockWindowLocation(vi); + + const core = new SDKCore(dpopConfig); + await core.startLogin('my-state'); + + const assignedUrl = new URL( + (location.assign as ReturnType).mock.calls[0][0], + ); + expect(assignedUrl.searchParams.get('state')).toBe('my-state'); + }); + + it('startLogin() in DPoP mode calls getOrCreateKeyPair and getThumbprint', async () => { + const getOrCreateKeyPair = vi + .spyOn(DPoPManager.prototype, 'getOrCreateKeyPair') + .mockResolvedValue({} as any); + const getThumbprint = vi + .spyOn(DPoPManager.prototype, 'getThumbprint') + .mockResolvedValue(MOCK_JKT); + vi.spyOn(Pkce, 'generateCodeVerifier').mockReturnValue(MOCK_VERIFIER); + vi.spyOn(Pkce, 'generateCodeChallenge').mockResolvedValue(MOCK_CHALLENGE); + mockWindowLocation(vi); + + const core = new SDKCore(dpopConfig); + await core.startLogin(); + + expect(getOrCreateKeyPair).toHaveBeenCalledOnce(); + expect(getThumbprint).toHaveBeenCalledOnce(); + }); + + it('startLogin() in cookie mode is unaffected by useDpop: false', async () => { + const location = mockWindowLocation(vi); + const core = new SDKCore(config); // no useDpop + + await core.startLogin('some-state'); + + expect(location.assign).toHaveBeenCalledOnce(); + const assignedUrl = new URL( + (location.assign as ReturnType).mock.calls[0][0], + ); + // Cookie mode uses the app server login path, not /oauth2/authorize. + expect(assignedUrl.pathname).not.toBe('/oauth2/authorize'); + expect(assignedUrl.searchParams.get('dpop_jkt')).toBeNull(); + expect(assignedUrl.searchParams.get('code_challenge')).toBeNull(); + }); + }); }); diff --git a/packages/core/src/SDKCore/SDKCore.ts b/packages/core/src/SDKCore/SDKCore.ts index f8629a4..0903ad6 100644 --- a/packages/core/src/SDKCore/SDKCore.ts +++ b/packages/core/src/SDKCore/SDKCore.ts @@ -3,6 +3,8 @@ import { SDKConfig } from '../SDKConfig'; import { UserInfo } from '../SDKContext'; import { RedirectHelper } from '../RedirectHelper'; import { getAccessTokenExpirationMoment } from '../CookieHelpers'; +import { DPoPManager } from '../DPoP'; +import * as Pkce from '../Pkce'; /** A class containing framework-agnostic SDK methods */ export class SDKCore { @@ -12,6 +14,7 @@ export class SDKCore { private tokenExpirationTimeout?: NodeJS.Timeout; private refreshTokenTimeout?: NodeJS.Timeout; private isDisposed = false; + private dpopManager?: DPoPManager; constructor(config: SDKConfig) { this.config = config; @@ -28,6 +31,14 @@ export class SDKCore { tokenRefreshPath: config.tokenRefreshPath, postLogoutRedirectUri: config.postLogoutRedirectUri, }); + + if (config.useDpop) { + this.dpopManager = new DPoPManager( + config.clientId, + config.dpopTokenStorage, + ); + } + this.scheduleTokenExpiration(); } @@ -37,7 +48,36 @@ export class SDKCore { this.isDisposed = true; } - startLogin(state?: string) { + /** + * Initiates the login flow. + * + * In DPoP mode (`useDpop: true`): + * 1. Loads or generates the DPoP key pair. + * 2. Computes `dpop_jkt` (JWK SHA-256 thumbprint of the public key). + * 3. Generates a PKCE `code_verifier` and derives `code_challenge`. + * 4. Persists `code_verifier` via `RedirectHelper` for later token exchange. + * 5. Redirects to FusionAuth `/oauth2/authorize` directly with `dpop_jkt` + * and `code_challenge` parameters. + * + * In cookie mode: behaves identically to the previous implementation — + * delegates to the companion app server's login path. + * + * @param state Optional OAuth2 state value echoed back post-login. + */ + async startLogin(state?: string): Promise { + if (this.dpopManager) { + await this.dpopManager.getOrCreateKeyPair(); + const dpopJkt = await this.dpopManager.getThumbprint(); + const codeVerifier = Pkce.generateCodeVerifier(); + const codeChallenge = await Pkce.generateCodeChallenge(codeVerifier); + this.redirectHelper.handlePreRedirect(state, codeVerifier); + window.location.assign( + this.urlHelper.getAuthorizeUrl(dpopJkt, codeChallenge, state), + ); + return; + } + + // Cookie mode — unchanged behavior. this.redirectHelper.handlePreRedirect(state); window.location.assign(this.urlHelper.getLoginUrl(state)); } @@ -141,7 +181,17 @@ export class SDKCore { } } + /** + * Whether the user is currently logged in. + * + * - DPoP mode: delegates to `DPoPManager.isLoggedIn` which checks whether + * the stored tokens exist and have not expired. + * - Cookie mode: reads the `app.at_exp` cookie (existing behavior). + */ get isLoggedIn() { + if (this.dpopManager) { + return this.dpopManager.isLoggedIn; + } return this.at_exp > new Date().getTime(); } diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index 9137da2..4d9e6f4 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -4,3 +4,4 @@ export { type SDKContext } from './SDKContext'; export * from './testUtils'; export { type CookieAdapter } from './CookieHelpers'; export * from './DPoP'; +export * from './Pkce'; From ce415453d0efe5a40b333d493ecde4f091373020 Mon Sep 17 00:00:00 2001 From: Mike Rudat Date: Sat, 18 Jul 2026 20:53:14 +0000 Subject: [PATCH 02/11] fix: add @vitest-environment jsdom to SDKCore.test.ts; fix handlePreRedirect assertion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without the explicit jsdom annotation, vitest inherits the 'node' environment from DPoPManager.test.ts when the full suite runs, causing 'document is not defined' and 'window is not defined' failures in all SDKCore tests. Also corrects the handlePreRedirect spy assertion: cookie-mode startLogin() passes one argument (state), not two — the codeVerifier arg is only added in DPoP mode. --- packages/core/src/SDKCore/SDKCore.test.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/core/src/SDKCore/SDKCore.test.ts b/packages/core/src/SDKCore/SDKCore.test.ts index 721be85..bbb4688 100644 --- a/packages/core/src/SDKCore/SDKCore.test.ts +++ b/packages/core/src/SDKCore/SDKCore.test.ts @@ -1,3 +1,9 @@ +// @vitest-environment jsdom +// SDKCore uses document.cookie (via CookieHelpers), window.location.assign, +// and localStorage — all browser-only globals that jsdom provides. +// This annotation is explicit so that importing DPoPManager (which has its own +// @vitest-environment node override) does not cause vitest to run this file in +// the node environment when the full test suite is executed together. import { afterEach, describe, it, expect, vi } from 'vitest'; import { SDKConfig } from '../SDKConfig'; @@ -138,7 +144,7 @@ describe('SDKCore', () => { core.startLogin('/login'); core.startRegister(); - expect(handlePreRedirect).toHaveBeenNthCalledWith(1, '/login', undefined); + expect(handlePreRedirect).toHaveBeenNthCalledWith(1, '/login'); expect(handlePreRedirect).toHaveBeenNthCalledWith(2, undefined); }); From f29df57dfd809f3bc8022d7bf6e8a666e9aefe9c Mon Sep 17 00:00:00 2001 From: Mike Rudat Date: Sat, 18 Jul 2026 21:10:59 +0000 Subject: [PATCH 03/11] fix: suppress cookie console.error noise in Tier 0 e2e tests SDKCore's constructor calls scheduleTokenExpiration() which calls getAccessTokenExpirationMoment(). In a Node/Playwright process document doesn't exist, so CookieHelpers catches the ReferenceError and logs 'Error accessing cookies...' to console.error. The tests still pass, but the stderr noise is confusing. Fix: extract a shared DPOP_CONFIG constant in the Tier 0 describe block that includes a no-op cookieAdapter ({ at_exp: () => undefined }). This causes getAccessTokenExpirationMoment() to take the adapter path and skip document.cookie entirely, eliminating the noise. Also fixes T0-1 where the await core.startLogin() call was accidentally dropped during the previous config refactor. --- e2e/tests/dpop-smoke.test.ts | 55 +++++++++++++----------------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/e2e/tests/dpop-smoke.test.ts b/e2e/tests/dpop-smoke.test.ts index 91920d2..9bd264b 100644 --- a/e2e/tests/dpop-smoke.test.ts +++ b/e2e/tests/dpop-smoke.test.ts @@ -153,6 +153,21 @@ async function loginAndCaptureCode( // --------------------------------------------------------------------------- test.describe('Tier 0: SDKCore.startLogin() DPoP mode', () => { + // SDKConfig used by every T0 test. The cookieAdapter returning undefined + // prevents SDKCore from calling document.cookie (which doesn't exist in the + // Node/Playwright process) and eliminates the "Error accessing cookies" + // console.error noise from CookieHelpers.getAccessTokenExpirationMoment(). + const DPOP_CONFIG = { + serverUrl: FA_URL, + clientId: CLIENT_ID, + redirectUri: REDIRECT_URI, + scope: SCOPE, + useDpop: true as const, + dpopTokenStorage: 'memory' as const, + onTokenExpiration: () => {}, + cookieAdapter: { at_exp: () => undefined }, + }; + // Provide browser-API polyfills required by SDKCore and its dependencies // when running in Node (Playwright's test process is Node, not a browser). test.beforeAll(() => { @@ -207,17 +222,7 @@ test.describe('Tier 0: SDKCore.startLogin() DPoP mode', () => { }, }; - const core = new SDKCore({ - serverUrl: FA_URL, - clientId: CLIENT_ID, - redirectUri: REDIRECT_URI, - scope: SCOPE, - useDpop: true, - dpopTokenStorage: 'memory', - onTokenExpiration: () => {}, - }); - - await core.startLogin(); + await new SDKCore(DPOP_CONFIG).startLogin(); expect(assignedUrl).not.toBeNull(); const url = new URL(assignedUrl!); @@ -251,15 +256,7 @@ test.describe('Tier 0: SDKCore.startLogin() DPoP mode', () => { }, }; - const core = new SDKCore({ - serverUrl: FA_URL, - clientId: CLIENT_ID, - redirectUri: REDIRECT_URI, - scope: SCOPE, - useDpop: true, - dpopTokenStorage: 'memory', - onTokenExpiration: () => {}, - }); + const core = new SDKCore(DPOP_CONFIG); await core.startLogin(STATE); @@ -290,27 +287,13 @@ test.describe('Tier 0: SDKCore.startLogin() DPoP mode', () => { }, }; - const core1 = new SDKCore({ - serverUrl: FA_URL, - clientId: CLIENT_ID, - redirectUri: REDIRECT_URI, - useDpop: true, - dpopTokenStorage: 'memory', - onTokenExpiration: () => {}, - }); + const core1 = new SDKCore(DPOP_CONFIG); // Each SDKCore gets its own DPoPManager with its own key pair. // @ts-ignore globalThis.indexedDB = new IDBFactory(); - const core2 = new SDKCore({ - serverUrl: FA_URL, - clientId: CLIENT_ID, - redirectUri: REDIRECT_URI, - useDpop: true, - dpopTokenStorage: 'memory', - onTokenExpiration: () => {}, - }); + const core2 = new SDKCore(DPOP_CONFIG); await core1.startLogin(); globalThis.localStorage.clear(); From 7babfae7b63c1c7fd10cfa652fbaf5809d790ffd Mon Sep 17 00:00:00 2001 From: Mike Rudat Date: Sat, 18 Jul 2026 15:16:53 -0600 Subject: [PATCH 04/11] feat: update lock file. --- yarn.lock | 1099 ++++++++++++++++++++++++++++------------------------- 1 file changed, 572 insertions(+), 527 deletions(-) diff --git a/yarn.lock b/yarn.lock index 99537c3..6d45406 100644 --- a/yarn.lock +++ b/yarn.lock @@ -606,30 +606,26 @@ resolved "https://registry.yarnpkg.com/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz#333fedabc3fd1a8e5d0100013731cf19e6a8c5d3" integrity sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw== -"@dxup/nuxt@^0.4.1": - version "0.4.1" - resolved "https://registry.yarnpkg.com/@dxup/nuxt/-/nuxt-0.4.1.tgz#cd9845beb849b02cf52adaa6cbf841b705c23a35" - integrity sha512-gtYffW6OfWNvoLW+XD3Mx/K8uUq08PMGLYJoDxc92EzZAWqR0FhcR5iaLm5r/OxyGTKz+P5f5Y7Aoir9+SjYaw== +"@dxup/nuxt@^0.5.3": + version "0.5.3" + resolved "https://registry.yarnpkg.com/@dxup/nuxt/-/nuxt-0.5.3.tgz#31eeb298472bd5fd5d0b3319959f335d98e273a5" + integrity sha512-PRwX3kEDjZF4t+j+lWbhFSZ1WBklwFSus5byNtkCL2PgWoUMbywNtewJcUHVSOQdwEYsbD1H/md3e/CKaTvDyw== dependencies: "@dxup/unimport" "^0.1.2" - "@nuxt/kit" "^4.4.2" + "@nuxt/kit" "^4.4.8" + "@vue/compiler-dom" "^3.5.39" chokidar "^5.0.0" + knitwork "^1.3.0" + magic-string "^0.30.21" pathe "^2.0.3" - tinyglobby "^0.2.16" + tinyglobby "^0.2.17" + unplugin "^3.3.0" "@dxup/unimport@^0.1.2": version "0.1.2" resolved "https://registry.yarnpkg.com/@dxup/unimport/-/unimport-0.1.2.tgz#a08e4039efe41c50d9c36fbb39d9976b88eefa68" integrity sha512-/B8YJGPzaYq1NbsQmwgP8EZqg40NpTw4ZB3suuI0TplbxKHeK94jeaawLmVhCv+YwUnOpiWEz9U6SeThku/8JQ== -"@emnapi/core@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.10.0.tgz#380ccc8f2412ea22d1d972df7f8ee23a3b9c7467" - integrity sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw== - dependencies: - "@emnapi/wasi-threads" "1.2.1" - tslib "^2.4.0" - "@emnapi/core@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.11.1.tgz#b9e1064f3a6b1631e241e638eb48d736bfd372a6" @@ -638,11 +634,12 @@ "@emnapi/wasi-threads" "1.2.2" tslib "^2.4.0" -"@emnapi/runtime@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.10.0.tgz#4b260c0d3534204e98c6110b8db1a987d26ec87c" - integrity sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA== +"@emnapi/core@1.11.2": + version "1.11.2" + resolved "https://registry.yarnpkg.com/@emnapi/core/-/core-1.11.2.tgz#fab0a0f3c492d11f5a9ac9065d0d73955ee1c1c9" + integrity sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA== dependencies: + "@emnapi/wasi-threads" "1.2.2" tslib "^2.4.0" "@emnapi/runtime@1.11.1": @@ -652,10 +649,10 @@ dependencies: tslib "^2.4.0" -"@emnapi/wasi-threads@1.2.1": - version "1.2.1" - resolved "https://registry.yarnpkg.com/@emnapi/wasi-threads/-/wasi-threads-1.2.1.tgz#28fed21a1ba1ce797c44a070abc94d42f3ae8548" - integrity sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w== +"@emnapi/runtime@1.11.2": + version "1.11.2" + resolved "https://registry.yarnpkg.com/@emnapi/runtime/-/runtime-1.11.2.tgz#eb22f04d76febfdf4f87fdaff54c8a53f6bf0dbd" + integrity sha512-kyOl3X0DuTiT1h2ft8r2fYO8JYtU9a9Xis/zBSiGArNaagCOWx90N1k2wxp18czFDH+OgcWGb5ZP/XMt3dcyPA== dependencies: tslib "^2.4.0" @@ -1771,7 +1768,7 @@ "@napi-rs/nice-win32-ia32-msvc" "1.1.1" "@napi-rs/nice-win32-x64-msvc" "1.1.1" -"@napi-rs/wasm-runtime@^1.1.4", "@napi-rs/wasm-runtime@^1.1.6": +"@napi-rs/wasm-runtime@^1.1.6": version "1.1.6" resolved "https://registry.yarnpkg.com/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz#ed33806d0f9be98dc76d0c3d4fd872fda701b5d5" integrity sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg== @@ -1880,7 +1877,7 @@ node-gyp "^12.1.0" proc-log "^6.0.0" -"@nuxt/cli@^3.35.2": +"@nuxt/cli@^3.37.0": version "3.37.0" resolved "https://registry.yarnpkg.com/@nuxt/cli/-/cli-3.37.0.tgz#03078372574de0deb4e9df8d2ab009603fcf77ca" integrity sha512-Zj9NwHjEBzVrgezsgMFjpMhNqwNgROk9DzNi/dyfk1mCbXIRigV11br2xOl0Satek30bmv7oZAfMtCnDe6Ip0Q== @@ -1979,18 +1976,18 @@ which "^6.0.1" ws "^8.19.0" -"@nuxt/kit@3.21.8": - version "3.21.8" - resolved "https://registry.yarnpkg.com/@nuxt/kit/-/kit-3.21.8.tgz#c0377a837e4c8701c5096cb4ff025a72de3bd1e7" - integrity sha512-kg63DUPY5AHPn+9XM7u8rYcdWHXjzwfUscgRDuiC5YUciQ+xdLRhdwXelYFxEAx2nxJHossliiQXbMm/Fleivw== +"@nuxt/kit@3.21.9": + version "3.21.9" + resolved "https://registry.yarnpkg.com/@nuxt/kit/-/kit-3.21.9.tgz#76fc47fe62a9b4b9e5e0da1397e2cf2dc9f93de0" + integrity sha512-SJ3W7INBJLwnmFQPm2BACiqS25enc6QIm87aLQCcxo/TFtRnWanYtgDdwyHqUHgOAGmq9pYjgk83B2bpBKnDTw== dependencies: c12 "^3.3.4" consola "^3.4.2" defu "^6.1.7" destr "^2.0.5" errx "^0.1.0" - exsolve "^1.0.8" - ignore "^7.0.5" + exsolve "^1.1.0" + ignore "^7.0.6" jiti "^2.7.0" klona "^2.0.6" knitwork "^1.3.0" @@ -2000,54 +1997,55 @@ pkg-types "^2.3.1" rc9 "^3.0.1" scule "^1.3.0" - semver "^7.8.0" - tinyglobby "^0.2.16" + semver "^7.8.5" + tinyglobby "^0.2.17" ufo "^1.6.4" unctx "^2.5.0" untyped "^2.0.0" -"@nuxt/kit@^4.4.2": - version "4.4.8" - resolved "https://registry.yarnpkg.com/@nuxt/kit/-/kit-4.4.8.tgz#19e583f8e29c5363fd41f6f25995d2ae00611096" - integrity sha512-ZUlZ5iYfyfJFDPluhn6ZxFWcsuxWbLnZBc8w3MAROcQ4lYfZ+qFpALBLSNlpc0zhOa++33EE+5PEbOAdVIY+dw== +"@nuxt/kit@^4.4.2", "@nuxt/kit@^4.4.8": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@nuxt/kit/-/kit-4.5.0.tgz#33886255e5e9d734100a82ee35d2e9efd355422b" + integrity sha512-VD4GjRjbh7r7ILoXYbiKeQgZuYP/vJ7iMk6fSRak1+xEyXHpQ+OIq/EfIjfsHv7DVimddOB+I6W/wGXjRVZ+Ng== dependencies: c12 "^3.3.4" consola "^3.4.2" defu "^6.1.7" destr "^2.0.5" errx "^0.1.0" - exsolve "^1.0.8" - ignore "^7.0.5" + exsolve "^1.1.0" + ignore "^7.0.6" jiti "^2.7.0" klona "^2.0.6" mlly "^1.8.2" + nostics "^1.1.4" ohash "^2.0.11" pathe "^2.0.3" pkg-types "^2.3.1" rc9 "^3.0.1" scule "^1.3.0" - semver "^7.8.1" + semver "^7.8.5" tinyglobby "^0.2.17" ufo "^1.6.4" - unctx "^2.5.0" + unctx "^3.0.0" untyped "^2.0.0" -"@nuxt/nitro-server@3.21.8": - version "3.21.8" - resolved "https://registry.yarnpkg.com/@nuxt/nitro-server/-/nitro-server-3.21.8.tgz#df1a3a94cd97627e1ac353c9de0f59068bf03ced" - integrity sha512-GWYO2vdZhWekQHFEP7SLNzqHkQ1bVdjFtfJF4SXpl4v3w0jMa2JGIQkuQ0x/YpY1Yt0jAgqJREX8lFud8Cy2gQ== +"@nuxt/nitro-server@3.21.9": + version "3.21.9" + resolved "https://registry.yarnpkg.com/@nuxt/nitro-server/-/nitro-server-3.21.9.tgz#81d2f97528b2f6b896d20f10d5f6461c79ffbb82" + integrity sha512-hr6s76VPInHWyZuqPQbAQp4tKPO9N5I1XUgJNlCpkTX9gn/B+HXxDKWI9jXSw32zUKEWO+USyxrFWOTDN3rQXg== dependencies: "@nuxt/devalue" "^2.0.2" - "@nuxt/kit" "3.21.8" + "@nuxt/kit" "3.21.9" "@unhead/vue" "^2.1.15" - "@vue/shared" "^3.5.34" + "@vue/shared" "^3.5.40" consola "^3.4.2" defu "^6.1.7" destr "^2.0.5" devalue "^5.8.1" errx "^0.1.0" escape-string-regexp "^5.0.0" - exsolve "^1.0.8" + exsolve "^1.1.0" h3 "^1.15.11" impound "^1.1.5" klona "^2.0.6" @@ -2056,25 +2054,25 @@ ohash "^2.0.11" pathe "^2.0.3" pkg-types "^2.3.1" - rou3 "^0.8.1" - std-env "^4.1.0" + rou3 "^0.9.1" + std-env "^4.2.0" ufo "^1.6.4" unctx "^2.5.0" unstorage "^1.17.5" - vue "^3.5.34" - vue-bundle-renderer "^2.2.0" + vue "^3.5.40" + vue-bundle-renderer "^2.3.1" vue-devtools-stub "^0.1.0" -"@nuxt/schema@3.21.8": - version "3.21.8" - resolved "https://registry.yarnpkg.com/@nuxt/schema/-/schema-3.21.8.tgz#da1157c57b6aa6e00bde689abdaaaf9e6ec12294" - integrity sha512-BMxtf2x0E9sFji4Txz1boeMiwkhjQQFixdB6+lZXvCGpExZou4TLz82LDcIAZkpJVL0IEcfs96hTLVVBkjGulQ== +"@nuxt/schema@3.21.9": + version "3.21.9" + resolved "https://registry.yarnpkg.com/@nuxt/schema/-/schema-3.21.9.tgz#e4011cae3bc7484fed276bfb22b0a4fba4ca4d62" + integrity sha512-Di5iWRFey7nwqdAzbHRrkom39cH6VY3cqxMBlGUw3jzVvcfLACkslOQIzhDgf0u7ADxweedABiqSDFaV2KtVxw== dependencies: - "@vue/shared" "^3.5.34" + "@vue/shared" "^3.5.40" defu "^6.1.7" pathe "^2.0.3" pkg-types "^2.3.1" - std-env "^4.1.0" + std-env "^4.2.0" "@nuxt/telemetry@^2.8.0": version "2.8.0" @@ -2087,21 +2085,21 @@ rc9 "^3.0.0" std-env "^4.0.0" -"@nuxt/vite-builder@3.21.8": - version "3.21.8" - resolved "https://registry.yarnpkg.com/@nuxt/vite-builder/-/vite-builder-3.21.8.tgz#aad33e01033d9508bafda9e5a1ce06f1578ad1f9" - integrity sha512-aapUWCQGuLEzUj5hx+J/lfpzqt/fBYV8hmCQ930R4SM4BvEzqMR0wVkbU0ry0CDK+uQzb/2n50qIHcEp0ywc0Q== +"@nuxt/vite-builder@3.21.9": + version "3.21.9" + resolved "https://registry.yarnpkg.com/@nuxt/vite-builder/-/vite-builder-3.21.9.tgz#af84819f66e2124f5e46a7621b4060ef038c0abe" + integrity sha512-EKm//I/5IXrb+iy3yKIMYQA17hHc+DwWkwI+apiETHtMxq3CeUXTW5ixbRMES+oqeB29bRPAScC0ATQOIva6FA== dependencies: - "@nuxt/kit" "3.21.8" + "@nuxt/kit" "3.21.9" "@rollup/plugin-replace" "^6.0.3" - "@vitejs/plugin-vue" "^6.0.7" - "@vitejs/plugin-vue-jsx" "^5.1.5" - autoprefixer "^10.5.0" + "@vitejs/plugin-vue" "^6.0.8" + "@vitejs/plugin-vue-jsx" "^5.1.6" + autoprefixer "^10.5.4" consola "^3.4.2" cssnano "^7.1.9" defu "^6.1.7" escape-string-regexp "^5.0.0" - exsolve "^1.0.8" + exsolve "^1.1.0" externality "^1.0.2" get-port-please "^3.2.0" jiti "^2.7.0" @@ -2109,347 +2107,347 @@ magic-string "^0.30.21" mlly "^1.8.2" mocked-exports "^0.1.1" - nypm "^0.6.6" + nypm "^0.6.8" ohash "^2.0.11" pathe "^2.0.3" perfect-debounce "^2.1.0" pkg-types "^2.3.1" - postcss "^8.5.14" - seroval "^1.5.4" - std-env "^4.1.0" + postcss "^8.5.19" + seroval "^1.5.5" + std-env "^4.2.0" ufo "^1.6.4" unenv "^2.0.0-rc.24" - vite "^7.3.3" + vite "^7.3.6" vite-node "^5.3.0" - vite-plugin-checker "^0.13.0" - vue-bundle-renderer "^2.2.0" + vite-plugin-checker "^0.14.4" + vue-bundle-renderer "^2.3.1" "@one-ini/wasm@0.1.1": version "0.1.1" resolved "https://registry.yarnpkg.com/@one-ini/wasm/-/wasm-0.1.1.tgz#6013659736c9dbfccc96e8a9c2b3de317df39323" integrity sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw== -"@oxc-minify/binding-android-arm-eabi@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-android-arm-eabi/-/binding-android-arm-eabi-0.132.0.tgz#e5adc38e353ab46732a8a2634793916f5e3f3396" - integrity sha512-NXxgL3FNGEBz8r+8iSl8wSdyCEMGisVmn2GVuJc5GycWgGzxiP9V9/svgD039JnO9nRAi0j+hP3FNAuDCP4aQg== - -"@oxc-minify/binding-android-arm64@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-android-arm64/-/binding-android-arm64-0.132.0.tgz#35ff4549b03328f313b088b5054bd4bcf9932e01" - integrity sha512-XYogHG1aSjNEMKWUfWmBWtN9rnpQ2nA4MiecdiAOfofDHTQiU5ybrPH6VvDAtRXf2kr8WtPNX7eenhC3uWFWoA== - -"@oxc-minify/binding-darwin-arm64@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-darwin-arm64/-/binding-darwin-arm64-0.132.0.tgz#20634dc825dcf7edb95d2e25e62ab5f44c138d48" - integrity sha512-gm/M5dgm7IvA/g9tweMqiFyD15yKrxGUX3myjFP+EYIYVW+RYuvwU5MAIZUOxXY0GnjU1/iRN/JkLhwvhZVsDQ== - -"@oxc-minify/binding-darwin-x64@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-darwin-x64/-/binding-darwin-x64-0.132.0.tgz#4f730b09f1613f90887facf8732cb6eedc531f89" - integrity sha512-s7ecbOJeLccy3nqQlkiq9cV0D0q8j1OyHmxRz22m8qZlcKrc3s4gmhwj5ertipA8ePn3FOXv4azf8b5gatDDug== - -"@oxc-minify/binding-freebsd-x64@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-freebsd-x64/-/binding-freebsd-x64-0.132.0.tgz#bd93dcaaa55bcdc93be5f2afec2b6995a8c8e749" - integrity sha512-pdYVNmY9NgKetEWzXlVIUlPm4Z3Gz979nTbZUpHlqpjU/rtulpm0fgROo6rlTk+W0HhZCCZ0Jzy1LBKgn5g3mg== - -"@oxc-minify/binding-linux-arm-gnueabihf@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.132.0.tgz#d64aedc5d891a17185a3147c318353cad802c42e" - integrity sha512-QdV2II2mrbygZO/D+umhb+jMs+kmNO2pvQ+kahY8DN7qZVvaR2CiWBQaAxi3yuI0JvmymcUBEFhRrXsaL/lUqg== - -"@oxc-minify/binding-linux-arm-musleabihf@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.132.0.tgz#bd158b0043e8233107fe42bbc1aad31e6eded51a" - integrity sha512-6OJMBb53luST+xxNSzzg/rRkxMnR4NFQegdu3PCuDEUtP2OEgjmpvvBrHghITpzRsUqnQ/YTl/ItDiLVeoslUA== - -"@oxc-minify/binding-linux-arm64-gnu@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.132.0.tgz#73a794cf929a7fda58a025301206f33e4b0e94a9" - integrity sha512-ND2GZp6StGQWhSBwOfX13kCCG7O/Z6sEL/dBsWSIgZaetEDUPLOWtKIm2f+TuYUSSmU5nJTSSE5psh9kGcCweQ== - -"@oxc-minify/binding-linux-arm64-musl@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.132.0.tgz#9c4879f69ef5f26f8badc08dfbabc1ec8c12ed09" - integrity sha512-3k8ezEKmxs9Wel4N4vfF/8u764mA57j065P8nB4cU2PO/lLKloN0OA41ynfDUrSM1f5jBuF8+mLOj++aNnu4OA== - -"@oxc-minify/binding-linux-ppc64-gnu@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.132.0.tgz#b7d743c575c69faa8e3e4c6a0b72c9e510540cbc" - integrity sha512-vM6jZIxoHoIS5rPb3K3Di0IureL4oU+wOWBy6tLSrjwW2IHqy0442CzO/Ks2U9VCuHV1q0bUGCF0H6AxCEjJHQ== - -"@oxc-minify/binding-linux-riscv64-gnu@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.132.0.tgz#40775fe8b681f252665ff8e07b0ae6d69f90720e" - integrity sha512-KburrmtWpeZg58uo275QRwy5bbNOXQd1WDI2tGxkY2dJBlO7N5V9+Uthvqn6KI/6RBtjd2T5NO4dCC0fgUxGvw== - -"@oxc-minify/binding-linux-riscv64-musl@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.132.0.tgz#3d1aab92cf2de1e14be7b3cf403a1dc2da2f3c9d" - integrity sha512-MnahA2MNEtEdxWdUy24JXkMUNgGPqH285GL2L22Zz7k9ixsguFD+bTbbcR88pNqdb075nazozzv3edF83+azCA== - -"@oxc-minify/binding-linux-s390x-gnu@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.132.0.tgz#2b06307f270f49d56ecedd096382f1680dda22cf" - integrity sha512-VE99UPZyQO2MAG4gLGXzrBumD5PGNaiWe+EakaROGCVbT0YH/d9z2ByYqbdWAMEBiTHjthyZp6UUEFVda+LnpQ== - -"@oxc-minify/binding-linux-x64-gnu@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.132.0.tgz#c58d31143b74c1b75b75d3847b7e00196c8d99f6" - integrity sha512-FKxBkYrSAWNF4V6MacAJ/1E2SJobKKQ2CtW6Aq+pLzzEOjgk2SmxnK7I0bATlFH/O70tbTKDzWb17bySGYRcog== - -"@oxc-minify/binding-linux-x64-musl@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-x64-musl/-/binding-linux-x64-musl-0.132.0.tgz#dcf8f0854c31f0532e77ce405a65d1064824cf5c" - integrity sha512-W8IqA2XRvg/b6l/f+2SdV45/KKmpmwTabrjiMtpg/wzJU5cmKUoHihtJXPc9NA0Ls9S/oP0wB3PMCRQoNr5J1A== - -"@oxc-minify/binding-openharmony-arm64@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-openharmony-arm64/-/binding-openharmony-arm64-0.132.0.tgz#37953c0bb2e7dba82f95b8d1c238f105fc4606a4" - integrity sha512-X1BL65pI9bfOesLdVUcErjbEAUA3qmzjXCwXPCYsFZT7ela7SsK85+sN3m2TJNxmX1mrFKNg5g8bH+d2zHresw== - -"@oxc-minify/binding-wasm32-wasi@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-wasm32-wasi/-/binding-wasm32-wasi-0.132.0.tgz#c03401cf7f180eb8484d8cc7ca892574aadf6876" - integrity sha512-QcIiwBOj+bV5ub5x39Xb+v0boviykxUtVvVJaIEbG/IH97avFzZcBXec8awYlemLDvgG4WKQwr17x7COR5zwFg== - dependencies: - "@emnapi/core" "1.10.0" - "@emnapi/runtime" "1.10.0" - "@napi-rs/wasm-runtime" "^1.1.4" - -"@oxc-minify/binding-win32-arm64-msvc@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.132.0.tgz#4a3b57d10dd14b0ae09c84f1592429f5f20d1b9f" - integrity sha512-ahFMaa84QVTIROWpLhZcS9jKIv+CXzsJaMmgje7JtlVp1Kaar6tzVCt3EH2aPhSc8RvbGqfmnGdQu/kGwPAZVA== - -"@oxc-minify/binding-win32-ia32-msvc@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.132.0.tgz#6b36706fb7b454415c40428b286128cffec2af0f" - integrity sha512-tpBkLklqOnaYtlIh6gjmL60pP0Kn2hwaw1Fw3sJyIKwdkCPHsOPy/MRgBUpM0a/SeGFbsZRQkHnWfZXS1GTbbQ== - -"@oxc-minify/binding-win32-x64-msvc@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-minify/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.132.0.tgz#54eaa2cdd8a49b97ef9cfeb8b40f1b4781f8f3d9" - integrity sha512-a69yKrBl2p9O8cdAHbHih56eKhcpKJRVkRu/S+CwCdR2Zsh4nnqYIllF96Lxg3jDjRQNL3t0xZNdYBDG5Vgq+w== - -"@oxc-parser/binding-android-arm-eabi@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.132.0.tgz#f88d600252349b5e380e695cadf889cea896f676" - integrity sha512-KrLaPWa5c9Y7LkW+rKkaUE3y7DBDrQtaf7rlsSDfv6KAHUjgzAIRA761Lrrp6//Yd/Rlie/yEOt9YENCoJnOcw== - -"@oxc-parser/binding-android-arm64@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.132.0.tgz#ef91deec0305c54fa6c7b519f82da63d36b49788" - integrity sha512-SThDrSeamB/kG2+NxcJ5/wSLcV6dUqDknrPLqFYQ0ST/55mtBP4M7Q/f3QbubH6aAd11wpzZn/nwbVRSdobOpg== - -"@oxc-parser/binding-darwin-arm64@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.132.0.tgz#033a8f2789c3d09509ddd1a219dcbf2fd516125f" - integrity sha512-Lc0f/TYoKBghE5/2Gsv7bLXk+TJZunx2Tf61X8hG4ARXdc8UYI26dCGccFSd1AyFbK3jfaNXtMnupggDbjPXdQ== - -"@oxc-parser/binding-darwin-x64@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.132.0.tgz#56601549bad307fcee2b3e0756769e36598841f4" - integrity sha512-RG2eJIpf7C21z9HSSXFw1bTArdpKe7Y4fwcJTwRq1yCSe1vSavaN9GA1sm9KqzemTLAGVktQ+7qBTGp0vQeUZg== - -"@oxc-parser/binding-freebsd-x64@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.132.0.tgz#68140dd5670556fca3aa094f0cb7e706854b5967" - integrity sha512-wQIPntPLtJ8NcBpvKPbEv3NqzV6k8eP8tP/jE9Rg8HTg/j7urZGFSsTCPCW5k77Qfw2DM4vRvc9p3I4yq/Shvw== - -"@oxc-parser/binding-linux-arm-gnueabihf@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.132.0.tgz#84ef8af25ffb6172b02b1747bbbef668e09235c1" - integrity sha512-PixKEpeSe3yxQWqNyOCBALRYc72+Tj7ILDofUl3iXo25cVOzLA6jHUhmOINRtWIPh7dbUie3QNeabwaQpZTw6w== - -"@oxc-parser/binding-linux-arm-musleabihf@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.132.0.tgz#ca6a2dffed23143c9bcbefd8250832c71fdfb4d7" - integrity sha512-sCR+DzGHlyHKnbA2z9zWjTUhIo8Sy0enJl4RDsBwPmkxYynPatpwOAWe8W5127SlW0boqUWHGtr1NWn5UwIhXQ== - -"@oxc-parser/binding-linux-arm64-gnu@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.132.0.tgz#ed1a4718c61d05836015c8eac7395ffe74c3f94a" - integrity sha512-sQBix5P2cW+IpzTcCwYxnh9yALrKSIkKJThspBvMGcygSMnbzkSvhN7SfuX1hvBk8y1XEChsdkU3ET0V5DmzUw== - -"@oxc-parser/binding-linux-arm64-musl@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.132.0.tgz#ae32a94bb666604728fa48c568ced5bb270d1819" - integrity sha512-WozHg3Kc//8Sk756HXXgMbEAvqtG+Lzb9JOojwQzIGDtN78Az2dLttkb71akWYUF/8IgYfDSlfKh4Uot8is5Vw== - -"@oxc-parser/binding-linux-ppc64-gnu@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.132.0.tgz#0de7511156b2b5d7d4fc3574ab3badd93a07c1ae" - integrity sha512-CmX/ulNBOEwWTyVRmcpYKAcAizW6+OjtLJgo7fXoL9OqQvjF4VER8tPomv44vwzfSCy1BHbsB0ZlZYzYJNj4cA== - -"@oxc-parser/binding-linux-riscv64-gnu@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.132.0.tgz#9a4a3b3261b6ada598b65adc4521581c45aa1003" - integrity sha512-j9oQS+hM90SdhviNGWbPgT4+Rlq+ac++q/zjgwPD1mVHgxHzATvoRGtDx0sXGmFOQ9J9YkwAhYGb5MAHL6TAsA== - -"@oxc-parser/binding-linux-riscv64-musl@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.132.0.tgz#e55c1d671e41617f27535216483ccc01f1ff4a5e" - integrity sha512-bLz+Xi+Agnfmd7kWPEsSVwCn2k4EyIalZkNBcQ0OGIv9rqn8VgCPLNd03tM9mKX/5TdlvDXalz0q71BIrOPNqg== - -"@oxc-parser/binding-linux-s390x-gnu@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.132.0.tgz#2e4b692103d8ee745990c7ed5fd023387e6c93d9" - integrity sha512-U6t2qbJU0ypTfyj9QV3W1Y6mITDTL8ai/OR6NUn85vyHthOvobKWgXzU4tu0EskSzlpuVFz1g0jFGulDIUKHxQ== - -"@oxc-parser/binding-linux-x64-gnu@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.132.0.tgz#2ba1d08aeaed17247dac4cb5b9a3bc83b7bd7501" - integrity sha512-WcEaSNHFk8yz5YFlQQAlhq6jOFmZBB/RKE7uzhyCIf+pF1Lmv9gUH4221mle2Gd9iHyWT3ySNph8yZgb1xYdWg== - -"@oxc-parser/binding-linux-x64-musl@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.132.0.tgz#677889452adb283e791798faf70af0627bd493ad" - integrity sha512-iQrV4iJzQgRwK3BWRmQl1C3C6g3wYpXN2WLdQdyR+efoUnncdShZAVp9OgcojtlD3MDRbuOMGG3SjxF4fL4nlQ== - -"@oxc-parser/binding-openharmony-arm64@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.132.0.tgz#2928bbd0f815a7bf11a86b1bccfb0f352b92a7b3" - integrity sha512-FWzmUGrZ6GUby4U7WIwcCtab6tdmlTO3xTRRKyb5kjIJVEiaUAT8animUG/nK8ZCA8gkRkPOTId4rl6uTqUmJQ== - -"@oxc-parser/binding-wasm32-wasi@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.132.0.tgz#37df389cce33c8664763a402853a73559b882ce2" - integrity sha512-TlbMppxJI5CjWDes0QaP6G3aneVg1yikBu5QYI+DUShF9WDL66ccgKFNNGmi/Wybtszw6hxwAvv76T4DaPKnHw== - dependencies: - "@emnapi/core" "1.10.0" - "@emnapi/runtime" "1.10.0" - "@napi-rs/wasm-runtime" "^1.1.4" - -"@oxc-parser/binding-win32-arm64-msvc@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.132.0.tgz#b1a0913ad2545c30f498ba181c05de3898240976" - integrity sha512-RH/NbFjGKqdUAUi7Oh3LQPxUk2hsWFEEQ38HSnbRQT8QjBZFKqL1fMbmsB3N4jy/KPh9iX94+9dmkEMBBbambw== - -"@oxc-parser/binding-win32-ia32-msvc@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.132.0.tgz#13964f4b59671f7235f4f85866ab3db6e4afd6c5" - integrity sha512-JUr4jQY9jxoIB/YTLXr6XofSi5xikj6p5/Ns1h0VOBDT0j1jKU+kMsv2xxv51RwnETcXpA1Yw/9oUAfcqfaqEA== - -"@oxc-parser/binding-win32-x64-msvc@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.132.0.tgz#468339fb08809ddb856f3bc51db718790fb51f05" - integrity sha512-2dapgHpA5X8DSXF4AU36hJWYf6zP0tKjMXFRAZFBD62pkevW/uhFDXoFH9Y/3Fd2EtDrw5ByNnR1wVE9X9y0SQ== +"@oxc-minify/binding-android-arm-eabi@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-android-arm-eabi/-/binding-android-arm-eabi-0.140.0.tgz#0106c8b7edf0798da327f035cf6e6727afc885d4" + integrity sha512-z+SVtvvaC+qv1oRC0n7LJ6SIuU8xzCWM+MdQIGyUyeb7W0K3QibUg1J5hPFm+a/49mVS6v5CUpJP/07HEZwTjQ== + +"@oxc-minify/binding-android-arm64@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-android-arm64/-/binding-android-arm64-0.140.0.tgz#3b3d5e7a1e71daa1d2cda636f6ad2e8187869d20" + integrity sha512-CUX3s3hz1ZCTv6/UwS6pnQ79XSyRhU2rn0GWKK30BhvvzDv43KSSlXrocgeETzfLYXL+/xnsh08Nw1fq1fxbcQ== + +"@oxc-minify/binding-darwin-arm64@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-darwin-arm64/-/binding-darwin-arm64-0.140.0.tgz#4ab954b79a1eb4896a84e1c7d4d1393efc9d6246" + integrity sha512-R9QvHsauZGCCn6gN38XD/9361re4K4Hf9H8ajWAN4sVJB3w/rjtmF+aRD5HJF7EoK1OkocW4ENJtV3yVHMnWmw== + +"@oxc-minify/binding-darwin-x64@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-darwin-x64/-/binding-darwin-x64-0.140.0.tgz#ee3ab60138d03cbd862c7b9af4fc5387be92c966" + integrity sha512-ux6QN45jlB52GgQ745ZFvSSrSyyEaFTg9xZscVimcQxUFLPB/j8h8BzYeFaxgHqSVdBbJk/pbOaB6P5Mw5lz6w== + +"@oxc-minify/binding-freebsd-x64@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-freebsd-x64/-/binding-freebsd-x64-0.140.0.tgz#ecfd3812d2d3edfbc82997aa38953f67d65f1b0d" + integrity sha512-CveDbUDqrdJSBITHXt/61uhnrthJO8ayje22MrG31WVbbm9Y5XShGZTG7zj/zzG2w/DAprPEkOct97csL8kyXw== + +"@oxc-minify/binding-linux-arm-gnueabihf@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.140.0.tgz#525303c8857c03c22a65ce8cdd873d911fbb9649" + integrity sha512-Vt+n93L++7rVkH4btk6jXdGbZdRYR7QJcLeRj5aDu++Q9DYKho+a5Lu6PoImKIyrghFVAn7Czzz5oBmYM5aAgg== + +"@oxc-minify/binding-linux-arm-musleabihf@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.140.0.tgz#1f9eadbf007ad847593ca1764fcee7522a7e6b09" + integrity sha512-EvD7JEgVDzqiFPvS0rr9jUIEmFR9QsxR18BpbVzLs8Xo4GHqCoA0FrXMNziYWBSLqgBlt+LGs9yk0SjMx12RsA== + +"@oxc-minify/binding-linux-arm64-gnu@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.140.0.tgz#1a7f2ca52cf6600d8efecd32369d404609da60e6" + integrity sha512-Yb8WSpVyEa8UjwkZIyBBZCRKKOr1Hkf44YbT8gHWhJy0AjLHrXGgzJd7hnFXYLkMIllloux3yTNsVo/Q4loy3A== + +"@oxc-minify/binding-linux-arm64-musl@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.140.0.tgz#e982a84638e3bf7b7a215a7660f7ae4f10ca4475" + integrity sha512-1eudG61G/pMZsTB4t86oDjyq8GDa9QoNLSyPMQQPVwcVWqUpI9WOKak5SruZ4XDnGSN0SsRiH0TtKM0sHA0d4A== + +"@oxc-minify/binding-linux-ppc64-gnu@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.140.0.tgz#9fc899ed0ec7cacec29731284d31ffef7988e8ce" + integrity sha512-oISQKJoTYWq1iB8LzkKc09j6E104g1QQAUr+yb1T0is41RFdV11jc5kzT0Iwg167BsPqokmzjNnylBQT7Z16ww== + +"@oxc-minify/binding-linux-riscv64-gnu@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.140.0.tgz#98306620f2e9822ba66f6e5f103ced72b59dba17" + integrity sha512-S33Teg0B3SroYNlD7sFlZ25e9pjj1k7AkFjNOAOjBFBoX0MBP3idhx7k4jCkcK1kWvbRUmT7qUErsoZxtBfF4Q== + +"@oxc-minify/binding-linux-riscv64-musl@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.140.0.tgz#457c3ca6fef84a0ecccd2f2d5b43fea016242393" + integrity sha512-vjMzSh6Yo6stgvSIfWiBAlmu+QbeX7AF16iL5Bhm7xeLIOSbZ7kTAWWADyVRCpUTM2LYRdogWnq9eIp7MOyDrA== + +"@oxc-minify/binding-linux-s390x-gnu@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.140.0.tgz#b7011c0fe1f8c8358631e78579c2b12a94b00166" + integrity sha512-FYo0tG/BoZvs2LpMofMEKf0qHQHJklS3SbD8Xwicj2NgEfW6Y04ucU1MmL8shL5TjHmRcs7myXz3eHr9rGc6dw== + +"@oxc-minify/binding-linux-x64-gnu@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.140.0.tgz#26fbbbf77e53c4d21d7b71a8d5fe7ded61b537e8" + integrity sha512-2dThpMwGQohXph9yzAQNVsSwMarzAD8LkDyKhCbkoRJ/O2knpQNM3d6mMjqNJquFJxuniHNHQsCvV2N1R9/VoQ== + +"@oxc-minify/binding-linux-x64-musl@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-linux-x64-musl/-/binding-linux-x64-musl-0.140.0.tgz#cebe50b3f06892d201f60cbfdaf643dd13f4388d" + integrity sha512-47zuiYiy9fKIPBDBr+XxZwaJcd+ZtDI0ogNpJJunJphpWzBg3iyTyVoDU4TXRddSMkYoTSJJ3pejXOhvf+/Xhg== + +"@oxc-minify/binding-openharmony-arm64@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-openharmony-arm64/-/binding-openharmony-arm64-0.140.0.tgz#116e516e507e9ea2c11b4b3a7ff6451f3097b180" + integrity sha512-q97mzDdkbTUnRbcZtkYt8dwx7hMW0zwIqpaq+hN5cZ5N9VPvmpQ5/hITE2n4zskJ4pAYOWfQepuuagcTo0yCDQ== + +"@oxc-minify/binding-wasm32-wasi@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-wasm32-wasi/-/binding-wasm32-wasi-0.140.0.tgz#e3b3b3e64995a315b1ec76048471b8d799955fe2" + integrity sha512-J5SiqVtBbfujhWql3HZnL2E4nVoqgmoqgkbX9W3ZOLRS9/PMK/pFqVU0A4F86PIjFq2zPsrUliI/+b5NHfx6tQ== + dependencies: + "@emnapi/core" "1.11.2" + "@emnapi/runtime" "1.11.2" + "@napi-rs/wasm-runtime" "^1.1.6" + +"@oxc-minify/binding-win32-arm64-msvc@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.140.0.tgz#12e6c34b73107ffe5461705f27913e9d110242bb" + integrity sha512-v49UNq31wguYKsNZrcR6IJSLTQ0iIkRA3ybOPUCEWXKUcOSAce9juGet0U9kV4MFu24ecN3Dvpl1U6SDBy65wQ== + +"@oxc-minify/binding-win32-ia32-msvc@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.140.0.tgz#4fb716d98ca93dc74d30149fb19d73768d697047" + integrity sha512-+ZCR0ktjrfy1CoJjSDOhNftWBPqvePmEbqtInlhVXPH0yLQM0q3k0yROZ834vXU3py43gVaUElHTc0lTdb5D9A== + +"@oxc-minify/binding-win32-x64-msvc@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-minify/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.140.0.tgz#c9a3cf0d9dc67c047c0b0188c44688da2c10d1ed" + integrity sha512-A+disxuZHYCe1ttJnD+ljiGKDNJfeu9EZKuGIL3aU5pbNVDuwMWShpiyX3+OSQDYrbXF1xtNqrKadgw87Q5Neg== + +"@oxc-parser/binding-android-arm-eabi@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.140.0.tgz#1e263aca7aaf38e989000e50025b2b21834a8eda" + integrity sha512-ZfjDZ422mo7eo3b3VltqNsV9kmv1qt/sPEAMSl64iOSwhVfd0eIZ9LB79Mbs1xYXJnk7WSROwzBCKDIiVxPTvQ== + +"@oxc-parser/binding-android-arm64@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.140.0.tgz#d25051e94aa928163f36ad06616575d04a11f24b" + integrity sha512-Ia8jSvikUX6Sf+Ht+KOCUF/k1HpR0VlmqIYymubmWDebOEGtsyliHDR6JxsZ4IX3/c/GbrB1uh09aVGQv/LQmQ== + +"@oxc-parser/binding-darwin-arm64@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.140.0.tgz#b859c87a7f4a865b00197ab56d5c257d33ca89e7" + integrity sha512-G6VK0nK61pH0d0mBjUqSZbVxGqqO5uzeginLDQj+gOO6ObfJjXRwgkD/ol0w1INcnFeAb6YGGO7qc3ueGHaycQ== + +"@oxc-parser/binding-darwin-x64@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.140.0.tgz#81bc59ef5cdeebd34902626b9887aed12b6e32d0" + integrity sha512-HazBOuZzd2pO1C2uMmp8Gv7mhzMHqKSKDS1OZfcLEvpIcgA+48J92HEtNanVHDIzRD9PRPCV6aS6fkZIWOVl8Q== + +"@oxc-parser/binding-freebsd-x64@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.140.0.tgz#dd34a7927a9394a08ac374e04fc48dd5bc212036" + integrity sha512-9hSUU+HmTUyOe4JzMHxNGgLWNY7rrO+6ShicZwImNJacEAACDMIkuEQQkvXSL+WJN50jaNtLYJv8s4OcBdpyUQ== + +"@oxc-parser/binding-linux-arm-gnueabihf@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.140.0.tgz#4142cb0ce5ec8157208c112b9fd11718233d8bd2" + integrity sha512-RAEuQsYtS0KcDFqN0ABTjyyNlokS91JeuDuoW9tEbG0JTbRNXnpQUdbYc/16JoA6Z/2ALbNrE3KmxtqDiuIjCQ== + +"@oxc-parser/binding-linux-arm-musleabihf@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.140.0.tgz#90e0f998907f32ffbf0136fc29b5d1da1e6fb54a" + integrity sha512-c4CkHvPvqfojouredJ0w3e6+jiBq0SbFyhH61kr/zPb/7XsaYTNKQ54vmlSsopfdQbNDX40ZeK9Abs2Qet6wcw== + +"@oxc-parser/binding-linux-arm64-gnu@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.140.0.tgz#97ec2cb371693c1e9fc5eb660c48666ace786d82" + integrity sha512-yrjmLj8ixPB25yqvPGr28meGjb+keed7m1GqqY/0uqkhZIoT4t9zmfwUgFEtC33C7dtE+UQ7TU0IaVxf97SWJg== + +"@oxc-parser/binding-linux-arm64-musl@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.140.0.tgz#ebad7ee8c4094e51f4d138365b87a51160671bfd" + integrity sha512-ggGMQTN8Agwxp2WiLMpdY671dt0qTDJWiWlJeig3HnUwTnerRl0J2JdGVghWBeDcss2D9S2V2Js6dZHEiVabVA== + +"@oxc-parser/binding-linux-ppc64-gnu@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.140.0.tgz#fde39c4a1460fab7144d15025fb8c40e6e976668" + integrity sha512-IgTs8xYAFgAUGNmR65tIqjlJ8vKgrfXzC515e9goSdfMyKQV4aJpd2pUUudU4u51G64H0/DSEJEXKOraxm9ZCA== + +"@oxc-parser/binding-linux-riscv64-gnu@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.140.0.tgz#6990a42ddac6b81b0a00076e35028b514fbaf6ed" + integrity sha512-A1x+PMWZmSGaFVOx2YeNTFau8uD+QO14/vLP4GrcuvUPs3+nBkUOjy9Lus86ftHsDojjYMbvBelmKc3F7Rv08g== + +"@oxc-parser/binding-linux-riscv64-musl@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.140.0.tgz#fe9e6a5f6514ba8bb8c32bbbdb5d11165af64542" + integrity sha512-zBqpfRo2myWPrPo5xUjeZqlnPXPXsX8BcWtWff66/eGRQdbPjhzPgXa/F+AtxT2afUViPxbuDlwscMKzQ5tg+g== + +"@oxc-parser/binding-linux-s390x-gnu@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.140.0.tgz#9ec751995735f9452625fc18a1fdf6f430ea2eed" + integrity sha512-2M1DPm/8w9I//YzFlFC9qXw+r2tJFh5CYwRlYTq2vUJQS7qoQftEDeCZ8EnN7KHtvSiXvYj8mZI5pR7DpXmcEw== + +"@oxc-parser/binding-linux-x64-gnu@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.140.0.tgz#246c6fd05e75a9c81a9cb2c0e7f66c6e148c640f" + integrity sha512-8aRDbZ/U/jO8N7go1MO72jtbpb4uswV8d7vOkMvt/BPgZiyEYvl1VIWK4ESxZZhnJ4tqwVldgX7dNiP/eB1Jdg== + +"@oxc-parser/binding-linux-x64-musl@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.140.0.tgz#6a7861884ebf5236c163b323eda5e8223e6dddb3" + integrity sha512-xRqpeI8U2sQQS1W5BMWRyMTxtagkuLG2dEWruet5lFsWHTvBth11/TpSaJatHdqVVwHN0q3uuoS9zRsGinq8hg== + +"@oxc-parser/binding-openharmony-arm64@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.140.0.tgz#837520388be3cbcb4a5486f9e85598e0bcd1af22" + integrity sha512-GbGRe26MqAKciFRvXeHNQJ6VAHYs9R4miP89sEAncysM3n+f4lnyLWgsa9kklJNpfnxdq2yRoNYHFqwBckVimw== + +"@oxc-parser/binding-wasm32-wasi@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.140.0.tgz#49bd9ac61016154753f3adebf59b72aa3e754c72" + integrity sha512-vFiC1hqys+hkX1GnQkIoiTQJNiUm43Z0lO35ETKXTw0YtpW7+cN58YRRXFAQQ+TgpkIi3lrhcxdlnqz+Oi3ptQ== + dependencies: + "@emnapi/core" "1.11.2" + "@emnapi/runtime" "1.11.2" + "@napi-rs/wasm-runtime" "^1.1.6" + +"@oxc-parser/binding-win32-arm64-msvc@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.140.0.tgz#190b972b637c5a601644fff86561c59e658a4eed" + integrity sha512-fGSQldwEYKhM+H8uLt76Op8hh5+FYaR6lvvQ1Txw3Mhn86DyQXLcI0fi1EkFlTK7F+46OCk/j0AJMzZQm6g5Xg== + +"@oxc-parser/binding-win32-ia32-msvc@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.140.0.tgz#5d2438f810e646ab7e65b69b223ee00656cd6054" + integrity sha512-sDS2Bai+g3ZWYwfZqmosiSuFDBcVnZ3Ta6pszzsiJoLMqsJEWKcxXXbGa7b7yXr++W2lQNPb3ZRJ8czseqL7RA== + +"@oxc-parser/binding-win32-x64-msvc@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.140.0.tgz#fedda07f4cebf668073f8eb37b80031c5250c668" + integrity sha512-kHbE1zWyb5OQgJA6/5P4WjiuB01sYdQwtZnSSyE58FQEXDAMnyeeq4vj7KgN75i5SlBzOs8A5MrtlD3gOlDKqQ== "@oxc-project/types@=0.139.0": version "0.139.0" resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.139.0.tgz#38d76b9dbf934c2a02be174fb32ceebf182fe742" integrity sha512-r9gHphtCs+1M7J0pw6Sn/hh/Wpa/iQrOOkrNAlVLF/gHq+/CJmHIWKKUUhdWjcD6CIa8idarspCsASiXCXvFUw== -"@oxc-project/types@^0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.132.0.tgz#d77243df4fe1a0a1e60e12ac6240fa898d2363ff" - integrity sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ== - -"@oxc-transform/binding-android-arm-eabi@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-android-arm-eabi/-/binding-android-arm-eabi-0.132.0.tgz#930e7b0dcf8285c1902816d9c00bc7c6cb5ebd5f" - integrity sha512-UEC6fwIer1e2H8+KYXfhfYMsDgqxrG93lCj3FkrKkJ2O05rikqiJLYGd9ZntmKne+9bOMMuznVKLGErub++mAg== - -"@oxc-transform/binding-android-arm64@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-android-arm64/-/binding-android-arm64-0.132.0.tgz#77204489bebffb61a2cbda2f7dca6369b320ecac" - integrity sha512-sr2BbEHtc5OkAN2nt5BpWGg/MnDkyQKf6tSjaZZ6k7Bb2FOa2CzZDy2pvH6tYdg+Ch/p/OGXXhENFVV9GU7ASw== - -"@oxc-transform/binding-darwin-arm64@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-darwin-arm64/-/binding-darwin-arm64-0.132.0.tgz#4d7c9c792e1e9506d1b19326ada93a525f755b71" - integrity sha512-yjL1GbN9Bb1HqjE8CS8NSwoZtDWgUGy43VbuFhmT4LEDx4Ph0guzVAyUKhc2CqqA4/x60qDvcH6QxwrguaqEVA== - -"@oxc-transform/binding-darwin-x64@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-darwin-x64/-/binding-darwin-x64-0.132.0.tgz#8b349d8989e6ac3c05261a0f28b13a2e60938b51" - integrity sha512-e3vVXEbNw93aHr3si8eVpUgl+jWF6Ry8RgUihgSxiI+2c/VMxiPsEDghkqPcjujqsMYDRdISWJi23xk+PP72ow== - -"@oxc-transform/binding-freebsd-x64@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-freebsd-x64/-/binding-freebsd-x64-0.132.0.tgz#5dd318e8cc15ec844ba40f3c3bf033a2f6816e68" - integrity sha512-dIhAhkX8/It4IaKI944fN3jmfzunqv2sEG2G4fQdP5/1psycdqUHoVaY23DbpuYRIu4sWAdn/e1zQFP0GMkQOQ== - -"@oxc-transform/binding-linux-arm-gnueabihf@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.132.0.tgz#920fc1da5ed772cdc9a5ca13a09f37d5f8c275d6" - integrity sha512-eR0dfj1us7DNbGZ6eBdAqWnLZRkLqHFqewSHudX4gV7di3By8E05+M+qsGTB/zq/78Z0BYJeK1zGWu9un6jocw== - -"@oxc-transform/binding-linux-arm-musleabihf@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.132.0.tgz#6a60453c0d044633b9682b0bcb1f98b4c8a92d92" - integrity sha512-naNx0WaV70hKtgQ5LUS/jzRTy6XEQZ1krK7KTFZQLI1mEz+GqLrwsLCqEmtrQ6HcqLhvGvA6GAWfFrc/0mWryA== - -"@oxc-transform/binding-linux-arm64-gnu@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.132.0.tgz#a16521ac61aabe6be1f49ae5a435f57d33e5d90d" - integrity sha512-TWk1p0tbtE1tkMEABftfgXhMEfuoz3QieqBtMBXXyijizw/2YKNzbVSndG+vV73cSZgbyfoZ346pmuz0tQMzyw== - -"@oxc-transform/binding-linux-arm64-musl@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.132.0.tgz#f3da9374d6f81a9fa46759b4d28da90ca4916965" - integrity sha512-LxURDI0Wm2KCQm/3ynNlI+nTgPdfmAfmrl54XPx+gaIqty8S/XWNCCTvLJWaCb0e5eKqnzrcTuhMDOdawqoYIA== - -"@oxc-transform/binding-linux-ppc64-gnu@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.132.0.tgz#05cbe73a62a2e9524e2f9755ba9e399f7b65a2bd" - integrity sha512-eKEeG6SLtj01iDvi5QgMNzyEXt/K2BNWafZ0jGECmvqTWWaO2l4qBxUW+X+sAXp5vZBoT2WO3ZnshvIWXWjtKw== - -"@oxc-transform/binding-linux-riscv64-gnu@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.132.0.tgz#3dadb185b6cfbe96daf40947484d58113628f00f" - integrity sha512-Kz6tg1Msra7+2iGV8K5xANLO2SmpP6n+91/Yy+JJh9EagU4hvMm7loReszzz2bwhs6Xs4HPrglxIngMdqnHpXw== - -"@oxc-transform/binding-linux-riscv64-musl@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.132.0.tgz#3862422b28e0c0b45686287bceb498d1c4ff505f" - integrity sha512-dtUSp80ElrxUhfBNmFWGkFQQ51j3tRoZkKBXxEWh+hb+S6bbEdZCW/VuCYo/gCTH3DywwyTeWiG+dtZfJiHKvg== - -"@oxc-transform/binding-linux-s390x-gnu@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.132.0.tgz#ae0fde8122a80533927c7a1e7ce00cfd830a7a02" - integrity sha512-9qVyCbYSs8dwVPpqKKWxuUAnLJ1+LyC5A4oNMZTzymRhuQr3coqAP/XWfJ8LlhQqI9GvhK0SWCOK0iM3HFUAnA== - -"@oxc-transform/binding-linux-x64-gnu@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.132.0.tgz#872025ca5f86135b69f60d5df4e544295601d162" - integrity sha512-dUtJkDCYndDaxcuiSMyRoSb7sXmTbcJ61rDsUjIakghP6BkKwH57lyHYvSUhT1ZswXWwCjf3ksxlT8nA0iU6ag== - -"@oxc-transform/binding-linux-x64-musl@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-x64-musl/-/binding-linux-x64-musl-0.132.0.tgz#5ddd8f631c6f57c85918762f3f32102e311e9484" - integrity sha512-I7BkkktnrriiO7o1dF3RDgKZoSmFKX9IE0W2LE1WdfmpZcAa3fbv5BW6oVbzk40iD29hWSP69A65WT9l6dxuzg== - -"@oxc-transform/binding-openharmony-arm64@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-openharmony-arm64/-/binding-openharmony-arm64-0.132.0.tgz#264d58c13bdaf827643755a0fe33527b16b5a9c5" - integrity sha512-yiXaRYqgySJguURNZUFLDzSI1NTkP1jJKrowr8lQCKwY5N8DsESbQJ1RpSlEbeXGiy201puA+QC2fdr+ywQM/A== - -"@oxc-transform/binding-wasm32-wasi@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-wasm32-wasi/-/binding-wasm32-wasi-0.132.0.tgz#1e06a0310ffe082e4f19f850fe602944af1724ce" - integrity sha512-KNago0Mv+zl2yl5hK2G9V4Yb7Tgpn+z6lgzgaHXkGp7S+iuUtN3av+QqPCD/J+Odq6EjjyXJrFPfmyjbXXbf4Q== - dependencies: - "@emnapi/core" "1.10.0" - "@emnapi/runtime" "1.10.0" - "@napi-rs/wasm-runtime" "^1.1.4" - -"@oxc-transform/binding-win32-arm64-msvc@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.132.0.tgz#392f0dc62312c742276d57693c8d2b2bedbcea95" - integrity sha512-3fprECrLHwPP809a1SRzszDxp8Fpp8IOg0V2EO49wS+3JmRFOo090h5c37faZvym5VnRZ12DH2tkT6ZVXwlOsA== - -"@oxc-transform/binding-win32-ia32-msvc@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.132.0.tgz#e1a4f3128d4b07446541785ae4557e941898d98e" - integrity sha512-n616QqZ3hXasHytVoFjo6pLzIfo6hQwBEir0kOcaObKaAw0ZbncIe1h5a6IMnCOJGLP30WwnhwLW20tIV78MAA== - -"@oxc-transform/binding-win32-x64-msvc@0.132.0": - version "0.132.0" - resolved "https://registry.yarnpkg.com/@oxc-transform/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.132.0.tgz#e829a4a388c410013617804bb811d66b2bd8c6bf" - integrity sha512-P7A4Cz/0C0Oxa2zH/oCruzA/5EHr5RRz0x6KXYz3wwhS+dFqIBxP9yo8FKjXhKXHRKa+M+QHo+bqYiqqlVsEQg== +"@oxc-project/types@^0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-project/types/-/types-0.140.0.tgz#2acc1bd45ff24951059d01ce7552d26e1574c5ac" + integrity sha512-h5LUOzGArYemnW1NMz/DuuQhBi96J6JL2Bk8zE4kvqxB5Sg3jxmCiH4uyOWHDkiKSt5vWlG4FIwCR/DbstcNRQ== + +"@oxc-transform/binding-android-arm-eabi@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-android-arm-eabi/-/binding-android-arm-eabi-0.140.0.tgz#6bfaeadb56164c0d0c68d5213003031068442441" + integrity sha512-mYtsuGD5wCPzUVQHCywcWwIAgGkRJ+nCLCqR9TXh+WoZf6LlgluAncWkDxihufylcyjI2gjEtxjkMd7PHAVcMg== + +"@oxc-transform/binding-android-arm64@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-android-arm64/-/binding-android-arm64-0.140.0.tgz#29658b3883575605306a08ba033bfb9ffb09ebce" + integrity sha512-dgH1dyIQNTIEvxf58EIAMf8RljgZnECf0OxMgDo6JVpwV9PYGEyQduONPUQIuF/zx986HAXHbyHRL4C44HHYOA== + +"@oxc-transform/binding-darwin-arm64@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-darwin-arm64/-/binding-darwin-arm64-0.140.0.tgz#d8604d197443f848fb80661442b91945e9172223" + integrity sha512-emqYWDHQAV2wBlMUWZGmBL8aD1KtIr3OcckrJrwQ/Dmq6W8Tj/JNuCgHyXgx3OOBJZrBKVX6IB3cDHlN2+VgBg== + +"@oxc-transform/binding-darwin-x64@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-darwin-x64/-/binding-darwin-x64-0.140.0.tgz#947218c2e38d1dc0eb97e49324ac85ef08a6eb58" + integrity sha512-GxhzL/bl9o/kb1Qs8EZW1SBTuzsFfQ5syKgg3VuGTNpeP4LHyTS2h/BfW1N5P9Obcgw7zfGcMIRZXLXvbgNF4w== + +"@oxc-transform/binding-freebsd-x64@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-freebsd-x64/-/binding-freebsd-x64-0.140.0.tgz#c72025199b0f40d0ad9b62659b8cfc8117da8404" + integrity sha512-aHKNp2i3f5dewyDNS+3CzlqJ2EBB0L1bGI4VfjGMxhBGYmPyD5bRYmO4IN3cxxu1BFtHxC5RqF/lALpLmEXD0Q== + +"@oxc-transform/binding-linux-arm-gnueabihf@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.140.0.tgz#a234f1fc874be8157b2009c99c10d364e323188d" + integrity sha512-aDxttllXtdTczve8J5zmVckTjca96XVGbn1Bq7FBSgjjvPjzZeDh1N3f1SdYCg/6O2GG5uYoLgx2nNla2Q9qBg== + +"@oxc-transform/binding-linux-arm-musleabihf@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.140.0.tgz#ff381273b14546957c2868044d0543ae0ec01164" + integrity sha512-Kh6ebkfN25+8tJ37eg8/GP4KKHdFb8sV0nQxvtpal1HZ4h1sSXsuIYXP/0U07lKYsWpmkhkI+TLfuIOR8G2Cfw== + +"@oxc-transform/binding-linux-arm64-gnu@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.140.0.tgz#7d4aa3a1f6d44293ae281557d0046ed689f9c3d4" + integrity sha512-7wiDxRJfqeNaFtS4d/k4JvYbxH6F6N0mmeI+kA87iVzid3zqvS9eYwBCC5WWusLsgoLl+AElA/7jvohSpzEMMg== + +"@oxc-transform/binding-linux-arm64-musl@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.140.0.tgz#cc5d5b61b7410d93a09d99329ff446bdbaef1935" + integrity sha512-UxLuPaZcdhUnWhvJgBA5Uk2wyWS7VEXeYHzA6R+9Zh3Sv7mdY5iCzDFW6VWW/UIl0PQ+ifQVnFZ0TrEvfgyJmQ== + +"@oxc-transform/binding-linux-ppc64-gnu@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.140.0.tgz#95acecb0e059e6f6e80518e01dc391b079ff5d9a" + integrity sha512-WnRLduIoNb74DZGbJ9h/fnV0vwRkWRado2dqSzCgN7S5smAhKJJi4t8IgvW0KKza/qU0Ik4I1zYREnzbTQE5Wg== + +"@oxc-transform/binding-linux-riscv64-gnu@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.140.0.tgz#9b8f5374cf570710f0f972619a8e05771697b5c7" + integrity sha512-fJt6DwQk7BjDsBWu+wqf4SVZN7AGgC7UPbKLZud250o8GXvcQmk4BxmDHDDvGi2b12qx4aFwUqucmWryfpGKTQ== + +"@oxc-transform/binding-linux-riscv64-musl@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.140.0.tgz#a5e29167a0d923bedec01b82d058f7b430266ad1" + integrity sha512-5YMLMnXhEnL4ANpbAnjJ6ZoJtq5gqFREFrsGzePnaGQeHinA2Dgm9SmtC7rhpRYobw+nwOLcR4uGK0HNwydvbg== + +"@oxc-transform/binding-linux-s390x-gnu@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.140.0.tgz#92c3822d2d04e80437dd936946514a6a0f06a6c7" + integrity sha512-3aDuklBqnQzJfPISfhYbNq4INIRFhGUxm/4GoggMlaaqzLFoqbsyDkeckPMB+cIG2ygokshjA0+2REMZ1lzFqQ== + +"@oxc-transform/binding-linux-x64-gnu@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.140.0.tgz#e2e05bc4ebd984d33d604516cda3bad721eb60bb" + integrity sha512-E0qSFOMRArliAiASRwz55sU1XQxx2neimvhtfhvwUiVZf3OZqoIJfOz+W5nE/nGC3JGZgr8j6/rpWTgkwy1dFw== + +"@oxc-transform/binding-linux-x64-musl@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-linux-x64-musl/-/binding-linux-x64-musl-0.140.0.tgz#a7eda321eb7dd805f6380c05276e4ad7de96095c" + integrity sha512-LisGfSfrAgl06ve9w4r/yzG4rZaCRr5vmZbnPv7WJ5NzqejmaWbAhc5iMcCnkvo8t3NxBFwyNZcVBJfRbTmRXQ== + +"@oxc-transform/binding-openharmony-arm64@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-openharmony-arm64/-/binding-openharmony-arm64-0.140.0.tgz#e5d82bef8e916e2991d2f71997cced690aa87dbd" + integrity sha512-JKqdNAUvAj1RNhNgUPAuM9JqbsFh0dfdefVm6rfmNU+fprZHSqbIJZKgyGFtmssaWuNU+cd1PtM6Od8cV7zEMA== + +"@oxc-transform/binding-wasm32-wasi@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-wasm32-wasi/-/binding-wasm32-wasi-0.140.0.tgz#961d44b7a9d1bd5bcd66e47da60df9e24525f69c" + integrity sha512-dxXH7MULb3M8WyW3IG/MAwEspnHaa7Jbu7zW/bZL28FrHe7UcExWCFPB3BruOenyXc3NfKlY7W1r7MgXKzTmaA== + dependencies: + "@emnapi/core" "1.11.2" + "@emnapi/runtime" "1.11.2" + "@napi-rs/wasm-runtime" "^1.1.6" + +"@oxc-transform/binding-win32-arm64-msvc@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.140.0.tgz#3f672ba984888e7b2ed490f56d2bed6887b83d8c" + integrity sha512-ZaZeRNTMunUzhrGnDdySwAm+itB9Itoa+JixWVX3cg9+PW+HUY1yjqsA3EfoX5bqb6ZEfGYdAdBVUYJrwsy6/A== + +"@oxc-transform/binding-win32-ia32-msvc@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.140.0.tgz#cb62ccecffd9798e79967203b2c5dc4ede23342d" + integrity sha512-Wa6LI6pZGVHkUv+xFnZom9GB0EaOu8C2TJ2gUHAJQ30irNM6YWt4aekvbGDjVlhD+LvtzmB5Gut1Xb6yXvyWHw== + +"@oxc-transform/binding-win32-x64-msvc@0.140.0": + version "0.140.0" + resolved "https://registry.yarnpkg.com/@oxc-transform/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.140.0.tgz#ca75e7ffa062079bdf8164862491bee00ff69436" + integrity sha512-2jiUKUeQplXxx7nTwRObFbZy3xd8f92UN2Xmq0iIbDhuQIzX32xChfBhQanKZWFXyeFIVI1H7+jD9Y2BnE9RDQ== "@parcel/watcher-android-arm64@2.5.6": version "2.5.6" @@ -3561,7 +3559,7 @@ "@rolldown/pluginutils" "1.0.0-beta.27" "@swc/core" "^1.12.11" -"@vitejs/plugin-vue-jsx@^5.1.5": +"@vitejs/plugin-vue-jsx@^5.1.6": version "5.1.6" resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue-jsx/-/plugin-vue-jsx-5.1.6.tgz#a0bdaeaf9217bf57144c4c91c29336695a47cf2a" integrity sha512-YXvi4as2clxt6DFw5+a0tTA97ntiQXm/raR8ofNj3aNwwdlVGTiG2gp7EvfZW17P50acL/9bP0ccF4XnqNmlgA== @@ -3572,7 +3570,7 @@ "@rolldown/pluginutils" "^1.0.1" "@vue/babel-plugin-jsx" "^2.0.1" -"@vitejs/plugin-vue@^6.0.7": +"@vitejs/plugin-vue@^6.0.7", "@vitejs/plugin-vue@^6.0.8": version "6.0.8" resolved "https://registry.yarnpkg.com/@vitejs/plugin-vue/-/plugin-vue-6.0.8.tgz#1809d090b7c93b8f4ae83d3e7536655cbbb1c793" integrity sha512-0ZjgOg7oO6farnNGup7yvoM/YXZV84OZxHAwtflItNa/6zzQyVb5LNxyea3FEKEX2XlagIKzrlH7wwxkKgtiew== @@ -3840,7 +3838,7 @@ estree-walker "^2.0.2" source-map-js "^1.2.1" -"@vue/compiler-dom@3.5.40", "@vue/compiler-dom@^3.2.0", "@vue/compiler-dom@^3.3.0", "@vue/compiler-dom@^3.5.0": +"@vue/compiler-dom@3.5.40", "@vue/compiler-dom@^3.2.0", "@vue/compiler-dom@^3.3.0", "@vue/compiler-dom@^3.5.0", "@vue/compiler-dom@^3.5.39": version "3.5.40" resolved "https://registry.yarnpkg.com/@vue/compiler-dom/-/compiler-dom-3.5.40.tgz#ac0579d4dc257bf5e10ce324a7ce0a7f9fc5c338" integrity sha512-pwkx4vqlqOspFstrcmzwkKLePVMD3PT65imRzLhanU2V1Fj4K13g6OXjanOyzw3aTAuRk84BOmY8f3rEHqPaVA== @@ -3983,7 +3981,7 @@ "@vue/runtime-dom" "3.5.40" "@vue/shared" "3.5.40" -"@vue/shared@3.5.40", "@vue/shared@^3.3.0", "@vue/shared@^3.5.0", "@vue/shared@^3.5.22", "@vue/shared@^3.5.34": +"@vue/shared@3.5.40", "@vue/shared@^3.3.0", "@vue/shared@^3.5.0", "@vue/shared@^3.5.22", "@vue/shared@^3.5.40": version "3.5.40" resolved "https://registry.yarnpkg.com/@vue/shared/-/shared-3.5.40.tgz#fc09d1871d66cd9b01959a597b41d7cb61f716a8" integrity sha512-WxnBtruIqOoV3rA4jeKDWzrYI5h7Cp4+pjwDi8kWGHz+IslhiN+wguLVVhtv2l8VoU02rzDCVfDjgCl1lNpZVg== @@ -4418,7 +4416,7 @@ asynckit@^0.4.0: resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== -autoprefixer@^10.5.0: +autoprefixer@^10.5.4: version "10.5.4" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-10.5.4.tgz#3b7dd848b4155514a95ad1f6ce598844bea09697" integrity sha512-MaU0U/za7N3r6brxD4YB/l4NSrFzLPlANv6wEuQVaIPlD3L4W9rFcQPbL/EilY9BHhHvhfcz3gInDLrEtWT4EA== @@ -4798,7 +4796,7 @@ chokidar@^3.4.2: optionalDependencies: fsevents "~2.3.2" -chokidar@^4.0.0, chokidar@^4.0.3: +chokidar@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-4.0.3.tgz#7be37a4c03c9aee1ecfe862a4a23b2c70c205d30" integrity sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA== @@ -5288,6 +5286,13 @@ de-indent@^1.0.2: resolved "https://registry.yarnpkg.com/de-indent/-/de-indent-1.0.2.tgz#b2038e846dc33baa5796128d0804b455b8c1e21d" integrity sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg== +debug@2: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + debug@4, debug@4.4.3, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4, debug@^4.4.0, debug@^4.4.1, debug@^4.4.3: version "4.4.3" resolved "https://registry.yarnpkg.com/debug/-/debug-4.4.3.tgz#c6ae432d9bd9662582fce08709b038c58e9e3d6a" @@ -5295,7 +5300,7 @@ debug@4, debug@4.4.3, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, de dependencies: ms "^2.1.3" -debug@^3.2.7: +debug@^3.2.6, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -6942,6 +6947,13 @@ iconv-lite@0.6.3, iconv-lite@^0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +iconv-lite@^0.4.4: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + iconv-lite@^0.7.2, iconv-lite@~0.7.0: version "0.7.3" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.7.3.tgz#84ee12f963e7de50bc01a13e160a078b3b0f415f" @@ -6966,7 +6978,7 @@ ignore@^5.1.1, ignore@^5.2.0, ignore@^5.3.1: resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== -ignore@^7.0.5: +ignore@^7.0.5, ignore@^7.0.6: version "7.0.6" resolved "https://registry.yarnpkg.com/ignore/-/ignore-7.0.6.tgz#6a57aaef4c90df27ac3590875d29e8f11988c88e" integrity sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw== @@ -6976,11 +6988,6 @@ image-meta@^0.2.2: resolved "https://registry.yarnpkg.com/image-meta/-/image-meta-0.2.2.tgz#a88dbdf1983d7c23a80c3e71d3b8acdb5379f5e0" integrity sha512-3MOLanc3sb3LNGWQl1RlQlNWURE5g32aUphrDyFeCsxBTk08iE3VNe4CwsUZ0Qs1X+EfX0+r29Sxdpza4B+yRA== -image-size@~0.5.0: - version "0.5.5" - resolved "https://registry.yarnpkg.com/image-size/-/image-size-0.5.5.tgz#09dfd4ab9d20e29eb1c3e80b8990378df9e3cb9c" - integrity sha512-6TDAlDPZxUFCv+fuOkIoXT/V/f3Qbq8e37p+YOiYrUv3v9cc3/6x78VdfPgFVaB9dZYeLUfKgHRebpkm/oP2VQ== - immutable@^5.1.5: version "5.1.9" resolved "https://registry.yarnpkg.com/immutable/-/immutable-5.1.9.tgz#ac23c3a01992ab665e14ac9ffff298f28cd74a0c" @@ -7698,19 +7705,19 @@ lazystream@^1.0.0: readable-stream "^2.0.5" less@^4.2.0: - version "4.6.7" - resolved "https://registry.yarnpkg.com/less/-/less-4.6.7.tgz#15adef08189a6d06ca16876ab2de174d18e3ac59" - integrity sha512-o3UxHBPPVY1HtCXx15/z1NlknQiWyafRNbtLEv+6xFaDRI2g2xPKIH43do9dSwt8bGLTsjNSaifa48N3d6odsQ== + version "4.7.0" + resolved "https://registry.yarnpkg.com/less/-/less-4.7.0.tgz#78c2d42718338e89acb04c8424b26de469d56435" + integrity sha512-i7dAlT3+boO0mMh1G4cex0vz1lLAScmBbikm1VEDNv+cy0ore1CUo2UtX8m3N9QLE5WYDr4ISbiCRzHNGyFkrA== dependencies: copy-anything "^3.0.5" parse-node-version "^1.0.1" optionalDependencies: errno "^0.1.1" graceful-fs "^4.1.2" - image-size "~0.5.0" make-dir "^5.1.0" mime "^1.4.1" needle "^3.1.0" + probe-image-size "^7.2.3" source-map "~0.6.0" levn@^0.4.1: @@ -8302,6 +8309,11 @@ mrmime@2.0.1, mrmime@^2.0.0: resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-2.0.1.tgz#bc3e87f7987853a54c9850eeb1f1078cd44adddc" integrity sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ== +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + ms@^2.1.1, ms@^2.1.3: version "2.1.3" resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" @@ -8363,6 +8375,15 @@ natural-compare@^1.4.0: resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== +needle@^2.5.2: + version "2.9.1" + resolved "https://registry.yarnpkg.com/needle/-/needle-2.9.1.tgz#22d1dffbe3490c2b83e301f7709b6736cd8f2684" + integrity sha512-6R9fqJ5Zcmf+uYaFgdIHmLwNldn5HbK8L5ybn7Uz+ylX/rnOsSp1AHcvQSrCaFN+qNM1wpymHqD7mVasEOlHGQ== + dependencies: + debug "^3.2.6" + iconv-lite "^0.4.4" + sax "^1.2.4" + needle@^3.1.0: version "3.5.0" resolved "https://registry.yarnpkg.com/needle/-/needle-3.5.0.tgz#aa2023642cb41b11a11babb733fd8fa952919112" @@ -8593,6 +8614,11 @@ normalize-path@^3.0.0, normalize-path@~3.0.0: resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== +nostics@^1.1.4: + version "1.2.0" + resolved "https://registry.yarnpkg.com/nostics/-/nostics-1.2.0.tgz#1362df6de2ca8456b521914501abf6c52c4d7fb5" + integrity sha512-FGqEfhQjrvo1lL8KFifdTQiNwwQHJxC1jtYE1Rc54qF/jxONUNL+kC9gS1krX8Q65PgrQ5fCqH/I4NhWBvdSqg== + npm-bundled@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/npm-bundled/-/npm-bundled-5.0.0.tgz#5025d847cfd06c7b8d9432df01695d0133d9ee80" @@ -8677,20 +8703,20 @@ nth-check@^2.0.1, nth-check@^2.1.1: boolbase "^1.0.0" nuxt@^3.12.1: - version "3.21.8" - resolved "https://registry.yarnpkg.com/nuxt/-/nuxt-3.21.8.tgz#bc5617d926822f3e606697fe7720e95a63856801" - integrity sha512-RRB/MpZhdEhb/A21qaUaSI1UYDOy9bgK0vZvRRjDsA8HGY+eFBr2EvUkdeYQHOT8WKuByxWlg5ckz3H4A+QQ1w== + version "3.21.9" + resolved "https://registry.yarnpkg.com/nuxt/-/nuxt-3.21.9.tgz#da409326e2901b83f45c15d1db431ee47186e922" + integrity sha512-rvlYcUBnnjDdQPpog4DaVuSiwOgJDL7+JIUhwQ6XXzDX0q9kMQuk8ZZkIBYAOD1LEFEDIkt8/ckel+wzI1jojQ== dependencies: - "@dxup/nuxt" "^0.4.1" - "@nuxt/cli" "^3.35.2" + "@dxup/nuxt" "^0.5.3" + "@nuxt/cli" "^3.37.0" "@nuxt/devtools" "^3.2.4" - "@nuxt/kit" "3.21.8" - "@nuxt/nitro-server" "3.21.8" - "@nuxt/schema" "3.21.8" + "@nuxt/kit" "3.21.9" + "@nuxt/nitro-server" "3.21.9" + "@nuxt/schema" "3.21.9" "@nuxt/telemetry" "^2.8.0" - "@nuxt/vite-builder" "3.21.8" + "@nuxt/vite-builder" "3.21.9" "@unhead/vue" "^2.1.15" - "@vue/shared" "^3.5.34" + "@vue/shared" "^3.5.40" c12 "^3.3.4" chokidar "^5.0.0" compatx "^0.2.0" @@ -8701,10 +8727,10 @@ nuxt@^3.12.1: devalue "^5.8.1" errx "^0.1.0" escape-string-regexp "^5.0.0" - exsolve "^1.0.8" + exsolve "^1.1.0" h3 "^1.15.11" hookable "^5.5.3" - ignore "^7.0.5" + ignore "^7.0.6" impound "^1.1.5" jiti "^2.7.0" klona "^2.0.6" @@ -8712,31 +8738,31 @@ nuxt@^3.12.1: magic-string "^0.30.21" mlly "^1.8.2" nanotar "^0.3.0" - nypm "^0.6.6" + nypm "^0.6.8" ofetch "^1.5.1" ohash "^2.0.11" on-change "^6.0.2" - oxc-minify "^0.132.0" - oxc-parser "^0.132.0" - oxc-transform "^0.132.0" + oxc-minify "^0.140.0" + oxc-parser "^0.140.0" + oxc-transform "^0.140.0" oxc-walker "^1.0.0" pathe "^2.0.3" perfect-debounce "^2.1.0" pkg-types "^2.3.1" - rou3 "^0.8.1" + rou3 "^0.9.1" scule "^1.3.0" - semver "^7.8.0" - std-env "^4.1.0" - tinyglobby "^0.2.16" + semver "^7.8.5" + std-env "^4.2.0" + tinyglobby "^0.2.17" ufo "^1.6.4" - ultrahtml "^1.6.0" + ultrahtml "^1.7.0" uncrypto "^0.1.3" unctx "^2.5.0" unimport "^6.3.0" - unplugin "^3.0.0" + unplugin "^3.3.0" unplugin-vue-router "^0.19.2" untyped "^2.0.0" - vue "^3.5.34" + vue "^3.5.40" vue-router "^4.6.4" nwsapi@^2.2.12: @@ -8744,7 +8770,7 @@ nwsapi@^2.2.12: resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.24.tgz#f8927043d4c9b516abdebe804a32c8d1f9484d1f" integrity sha512-7YRhZ3jS45LwmSCT4b2sVFHt/WuovaktDU07QrtOBY2PXskss5a9jfmR9jptyumwXST+rFjrmppMY1KT/yn35A== -nypm@^0.6.5, nypm@^0.6.6, nypm@^0.6.8: +nypm@^0.6.5, nypm@^0.6.8: version "0.6.8" resolved "https://registry.yarnpkg.com/nypm/-/nypm-0.6.8.tgz#8fc62cf5aee4cdaebd487e593fe7b246862be01d" integrity sha512-Q9K4Diu6l5u6xJQogeFSs/zKtyMSgFKFtRQV+tHP4kL7KPm2grpBU0dFIwFaXwNxN0MtfKWc43VpCugAa+LPsw== @@ -8950,85 +8976,85 @@ own-keys@^1.0.1: object-keys "^1.1.1" safe-push-apply "^1.0.0" -oxc-minify@^0.132.0: - version "0.132.0" - resolved "https://registry.yarnpkg.com/oxc-minify/-/oxc-minify-0.132.0.tgz#e85778854443cbef67c0acd4ec754eb994d00934" - integrity sha512-7h3fOlgDwkIYxxKfGwCNejaLhH90Pvx+fttdPN7nRbWHxm6QSYcxW3IKjfxQVUeg+z1X2HZdOSY3rHkVqgxH1g== +oxc-minify@^0.140.0: + version "0.140.0" + resolved "https://registry.yarnpkg.com/oxc-minify/-/oxc-minify-0.140.0.tgz#9d077b2bea5a3647b0fd0f7b5cd305d85f78e806" + integrity sha512-WWZgfS0FoojXPCAQLimENEv9EJ4AmSnY+wU8PepebcYg+4SY8cjrB3nDUuQwWSVfLIcZ6F++ZecFudJZdLR71Q== optionalDependencies: - "@oxc-minify/binding-android-arm-eabi" "0.132.0" - "@oxc-minify/binding-android-arm64" "0.132.0" - "@oxc-minify/binding-darwin-arm64" "0.132.0" - "@oxc-minify/binding-darwin-x64" "0.132.0" - "@oxc-minify/binding-freebsd-x64" "0.132.0" - "@oxc-minify/binding-linux-arm-gnueabihf" "0.132.0" - "@oxc-minify/binding-linux-arm-musleabihf" "0.132.0" - "@oxc-minify/binding-linux-arm64-gnu" "0.132.0" - "@oxc-minify/binding-linux-arm64-musl" "0.132.0" - "@oxc-minify/binding-linux-ppc64-gnu" "0.132.0" - "@oxc-minify/binding-linux-riscv64-gnu" "0.132.0" - "@oxc-minify/binding-linux-riscv64-musl" "0.132.0" - "@oxc-minify/binding-linux-s390x-gnu" "0.132.0" - "@oxc-minify/binding-linux-x64-gnu" "0.132.0" - "@oxc-minify/binding-linux-x64-musl" "0.132.0" - "@oxc-minify/binding-openharmony-arm64" "0.132.0" - "@oxc-minify/binding-wasm32-wasi" "0.132.0" - "@oxc-minify/binding-win32-arm64-msvc" "0.132.0" - "@oxc-minify/binding-win32-ia32-msvc" "0.132.0" - "@oxc-minify/binding-win32-x64-msvc" "0.132.0" - -oxc-parser@^0.132.0: - version "0.132.0" - resolved "https://registry.yarnpkg.com/oxc-parser/-/oxc-parser-0.132.0.tgz#4f0ffad5ccfd0235a8ba79f7e6fc988be6f45476" - integrity sha512-+0LAPHaqtfQlvWdpaAa09SmOaZZgP8C552xosEkGJ4+ruEwP1Vgx+sqBgcBCNfR6KDCmagGOZTde8wmAvcI/Hg== - dependencies: - "@oxc-project/types" "^0.132.0" + "@oxc-minify/binding-android-arm-eabi" "0.140.0" + "@oxc-minify/binding-android-arm64" "0.140.0" + "@oxc-minify/binding-darwin-arm64" "0.140.0" + "@oxc-minify/binding-darwin-x64" "0.140.0" + "@oxc-minify/binding-freebsd-x64" "0.140.0" + "@oxc-minify/binding-linux-arm-gnueabihf" "0.140.0" + "@oxc-minify/binding-linux-arm-musleabihf" "0.140.0" + "@oxc-minify/binding-linux-arm64-gnu" "0.140.0" + "@oxc-minify/binding-linux-arm64-musl" "0.140.0" + "@oxc-minify/binding-linux-ppc64-gnu" "0.140.0" + "@oxc-minify/binding-linux-riscv64-gnu" "0.140.0" + "@oxc-minify/binding-linux-riscv64-musl" "0.140.0" + "@oxc-minify/binding-linux-s390x-gnu" "0.140.0" + "@oxc-minify/binding-linux-x64-gnu" "0.140.0" + "@oxc-minify/binding-linux-x64-musl" "0.140.0" + "@oxc-minify/binding-openharmony-arm64" "0.140.0" + "@oxc-minify/binding-wasm32-wasi" "0.140.0" + "@oxc-minify/binding-win32-arm64-msvc" "0.140.0" + "@oxc-minify/binding-win32-ia32-msvc" "0.140.0" + "@oxc-minify/binding-win32-x64-msvc" "0.140.0" + +oxc-parser@^0.140.0: + version "0.140.0" + resolved "https://registry.yarnpkg.com/oxc-parser/-/oxc-parser-0.140.0.tgz#71a7219cd9e18caa06782a276336b5835d38fc3a" + integrity sha512-h6QFWd6lBMfjESqgQ27GjzrSDb0qbznp7VDQqp2zvgsrWut4vcchyMIzOVXvGQ2GMZgKw9RWrFNWv9WqGL0p7Q== + dependencies: + "@oxc-project/types" "^0.140.0" optionalDependencies: - "@oxc-parser/binding-android-arm-eabi" "0.132.0" - "@oxc-parser/binding-android-arm64" "0.132.0" - "@oxc-parser/binding-darwin-arm64" "0.132.0" - "@oxc-parser/binding-darwin-x64" "0.132.0" - "@oxc-parser/binding-freebsd-x64" "0.132.0" - "@oxc-parser/binding-linux-arm-gnueabihf" "0.132.0" - "@oxc-parser/binding-linux-arm-musleabihf" "0.132.0" - "@oxc-parser/binding-linux-arm64-gnu" "0.132.0" - "@oxc-parser/binding-linux-arm64-musl" "0.132.0" - "@oxc-parser/binding-linux-ppc64-gnu" "0.132.0" - "@oxc-parser/binding-linux-riscv64-gnu" "0.132.0" - "@oxc-parser/binding-linux-riscv64-musl" "0.132.0" - "@oxc-parser/binding-linux-s390x-gnu" "0.132.0" - "@oxc-parser/binding-linux-x64-gnu" "0.132.0" - "@oxc-parser/binding-linux-x64-musl" "0.132.0" - "@oxc-parser/binding-openharmony-arm64" "0.132.0" - "@oxc-parser/binding-wasm32-wasi" "0.132.0" - "@oxc-parser/binding-win32-arm64-msvc" "0.132.0" - "@oxc-parser/binding-win32-ia32-msvc" "0.132.0" - "@oxc-parser/binding-win32-x64-msvc" "0.132.0" - -oxc-transform@^0.132.0: - version "0.132.0" - resolved "https://registry.yarnpkg.com/oxc-transform/-/oxc-transform-0.132.0.tgz#34b275dedabf1af41445085284a8be8b1431b8ab" - integrity sha512-DmP0+4kzpXoMvv08qPCD4aI6mAIzrEq15Yt9e6wXCNtOz6jEDHPpueusDa2/pvjRAqtNV37YxUUeX7cfCI4dpA== + "@oxc-parser/binding-android-arm-eabi" "0.140.0" + "@oxc-parser/binding-android-arm64" "0.140.0" + "@oxc-parser/binding-darwin-arm64" "0.140.0" + "@oxc-parser/binding-darwin-x64" "0.140.0" + "@oxc-parser/binding-freebsd-x64" "0.140.0" + "@oxc-parser/binding-linux-arm-gnueabihf" "0.140.0" + "@oxc-parser/binding-linux-arm-musleabihf" "0.140.0" + "@oxc-parser/binding-linux-arm64-gnu" "0.140.0" + "@oxc-parser/binding-linux-arm64-musl" "0.140.0" + "@oxc-parser/binding-linux-ppc64-gnu" "0.140.0" + "@oxc-parser/binding-linux-riscv64-gnu" "0.140.0" + "@oxc-parser/binding-linux-riscv64-musl" "0.140.0" + "@oxc-parser/binding-linux-s390x-gnu" "0.140.0" + "@oxc-parser/binding-linux-x64-gnu" "0.140.0" + "@oxc-parser/binding-linux-x64-musl" "0.140.0" + "@oxc-parser/binding-openharmony-arm64" "0.140.0" + "@oxc-parser/binding-wasm32-wasi" "0.140.0" + "@oxc-parser/binding-win32-arm64-msvc" "0.140.0" + "@oxc-parser/binding-win32-ia32-msvc" "0.140.0" + "@oxc-parser/binding-win32-x64-msvc" "0.140.0" + +oxc-transform@^0.140.0: + version "0.140.0" + resolved "https://registry.yarnpkg.com/oxc-transform/-/oxc-transform-0.140.0.tgz#6233bef1470c3ac979df2ee43fbfde084516f1e4" + integrity sha512-gE9ZjYloCbFZ96N5Gnn0JytzlysHQ6L8pWDc0xU7+FEpsYWBLLAFnCMojbOZhHEA+wpUOSyKQZ4jnYB65XY+yg== optionalDependencies: - "@oxc-transform/binding-android-arm-eabi" "0.132.0" - "@oxc-transform/binding-android-arm64" "0.132.0" - "@oxc-transform/binding-darwin-arm64" "0.132.0" - "@oxc-transform/binding-darwin-x64" "0.132.0" - "@oxc-transform/binding-freebsd-x64" "0.132.0" - "@oxc-transform/binding-linux-arm-gnueabihf" "0.132.0" - "@oxc-transform/binding-linux-arm-musleabihf" "0.132.0" - "@oxc-transform/binding-linux-arm64-gnu" "0.132.0" - "@oxc-transform/binding-linux-arm64-musl" "0.132.0" - "@oxc-transform/binding-linux-ppc64-gnu" "0.132.0" - "@oxc-transform/binding-linux-riscv64-gnu" "0.132.0" - "@oxc-transform/binding-linux-riscv64-musl" "0.132.0" - "@oxc-transform/binding-linux-s390x-gnu" "0.132.0" - "@oxc-transform/binding-linux-x64-gnu" "0.132.0" - "@oxc-transform/binding-linux-x64-musl" "0.132.0" - "@oxc-transform/binding-openharmony-arm64" "0.132.0" - "@oxc-transform/binding-wasm32-wasi" "0.132.0" - "@oxc-transform/binding-win32-arm64-msvc" "0.132.0" - "@oxc-transform/binding-win32-ia32-msvc" "0.132.0" - "@oxc-transform/binding-win32-x64-msvc" "0.132.0" + "@oxc-transform/binding-android-arm-eabi" "0.140.0" + "@oxc-transform/binding-android-arm64" "0.140.0" + "@oxc-transform/binding-darwin-arm64" "0.140.0" + "@oxc-transform/binding-darwin-x64" "0.140.0" + "@oxc-transform/binding-freebsd-x64" "0.140.0" + "@oxc-transform/binding-linux-arm-gnueabihf" "0.140.0" + "@oxc-transform/binding-linux-arm-musleabihf" "0.140.0" + "@oxc-transform/binding-linux-arm64-gnu" "0.140.0" + "@oxc-transform/binding-linux-arm64-musl" "0.140.0" + "@oxc-transform/binding-linux-ppc64-gnu" "0.140.0" + "@oxc-transform/binding-linux-riscv64-gnu" "0.140.0" + "@oxc-transform/binding-linux-riscv64-musl" "0.140.0" + "@oxc-transform/binding-linux-s390x-gnu" "0.140.0" + "@oxc-transform/binding-linux-x64-gnu" "0.140.0" + "@oxc-transform/binding-linux-x64-musl" "0.140.0" + "@oxc-transform/binding-openharmony-arm64" "0.140.0" + "@oxc-transform/binding-wasm32-wasi" "0.140.0" + "@oxc-transform/binding-win32-arm64-msvc" "0.140.0" + "@oxc-transform/binding-win32-ia32-msvc" "0.140.0" + "@oxc-transform/binding-win32-x64-msvc" "0.140.0" oxc-walker@^1.0.0: version "1.0.0" @@ -9524,7 +9550,7 @@ postcss-value-parser@^4.2.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz#723c09920836ba6d3e5af019f92bc0971c02e514" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^8.4.43, postcss@^8.4.47, postcss@^8.4.49, postcss@^8.5.14, postcss@^8.5.17, postcss@^8.5.19, postcss@^8.5.3, postcss@^8.5.6: +postcss@^8.4.43, postcss@^8.4.47, postcss@^8.4.49, postcss@^8.5.17, postcss@^8.5.19, postcss@^8.5.3, postcss@^8.5.6: version "8.5.19" resolved "https://registry.yarnpkg.com/postcss/-/postcss-8.5.19.tgz#45ad5cfde499408e20147348237551381a922037" integrity sha512-Mz8SaolMd8nB+G13WkORcxQKHZ/NE4xXevtkJHVuG+guo9/wYKlIMTKAqGdEmYOXR2ijPjTYNHssizdaVSUNdQ== @@ -9571,6 +9597,15 @@ pretty-format@^29.7.0: ansi-styles "^5.0.0" react-is "^18.0.0" +probe-image-size@^7.2.3: + version "7.3.0" + resolved "https://registry.yarnpkg.com/probe-image-size/-/probe-image-size-7.3.0.tgz#a07df2e2cffc1057026d5a1bda3a5b11ee970034" + integrity sha512-7CaDeBwiAbh6ohXsvLbAZhO7wzsZAmaevfxe39qvCwRh8LyaZfDlBGGLU1CCTgrTLtCOdwBBhjOrIHaIIimHfQ== + dependencies: + lodash.merge "^4.6.2" + needle "^2.5.2" + stream-parser "~0.3.1" + proc-log@^6.0.0, proc-log@^6.1.0: version "6.1.0" resolved "https://registry.yarnpkg.com/proc-log/-/proc-log-6.1.0.tgz#18519482a37d5198e231133a70144a50f21f0215" @@ -10136,10 +10171,10 @@ rollup@^4.20.0, rollup@^4.24.0, rollup@^4.34.9, rollup@^4.43.0, rollup@^4.60.2: "@rollup/rollup-win32-x64-msvc" "4.62.2" fsevents "~2.3.2" -rou3@^0.8.1: - version "0.8.1" - resolved "https://registry.yarnpkg.com/rou3/-/rou3-0.8.1.tgz#d18c9dae42bdd9cd4fffa77bc6731d5cfe92129a" - integrity sha512-ePa+XGk00/3HuCqrEnK3LxJW7I0SdNg6EFzKUJG73hMAdDcOUC/i/aSz7LSDwLrGr33kal/rqOGydzwl6U7zBA== +rou3@^0.9.1: + version "0.9.1" + resolved "https://registry.yarnpkg.com/rou3/-/rou3-0.9.1.tgz#12e9a0d00aa513443e7fa4d171dfd38db0324ea8" + integrity sha512-z/sSmzvtwMDDnxsPVhfWMuG6F6mbmhFDXoVqLmMfbpDD9qfV3GDmSQpf0+W296/ZDIpW2wcMmBfpVFzcnOi/nA== router@^2.2.0: version "2.2.0" @@ -10219,7 +10254,7 @@ safe-regex-test@^1.1.0: es-errors "^1.3.0" is-regex "^1.2.1" -"safer-buffer@>= 2.1.2 < 3.0.0": +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": version "2.1.2" resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== @@ -10278,7 +10313,7 @@ semver@^6.1.0, semver@^6.3.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.0.0, semver@^7.1.1, semver@^7.3.5, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.7.4, semver@^7.8.0, semver@^7.8.1, semver@^7.8.5: +semver@^7.0.0, semver@^7.1.1, semver@^7.3.5, semver@^7.5.3, semver@^7.5.4, semver@^7.6.0, semver@^7.7.4, semver@^7.8.5: version "7.8.5" resolved "https://registry.yarnpkg.com/semver/-/semver-7.8.5.tgz#39b646037dd50c14fb451e7e4cac58ed8b863f69" integrity sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA== @@ -10312,7 +10347,7 @@ serialize-javascript@^7.0.3: resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-7.0.7.tgz#06ec40576d4cea96d68010a534520bff1f948a72" integrity sha512-YAy8Od6KV+uuwUuU50np8fGB/Aues6Y0nAhA9y/hId74PlKUcme4pXcBD46NWKr1Q4osN/iseZ17YqO1XfmI8g== -seroval@^1.5.4: +seroval@^1.5.5: version "1.5.5" resolved "https://registry.yarnpkg.com/seroval/-/seroval-1.5.5.tgz#53d941cef71c8850a41f4e25b6b637530153bd5b" integrity sha512-bSjOuPcwPKLSJNhr9+bZxA20nQxVle5J5MNsYRVE6cIg7KpRLXGupymePavu0jrxlPiPsr4xGZSB8yUY2sH2sw== @@ -10646,6 +10681,13 @@ stop-iteration-iterator@^1.0.0, stop-iteration-iterator@^1.1.0: es-errors "^1.3.0" internal-slot "^1.1.0" +stream-parser@~0.3.1: + version "0.3.1" + resolved "https://registry.yarnpkg.com/stream-parser/-/stream-parser-0.3.1.tgz#1618548694420021a1182ff0af1911c129761773" + integrity sha512-bJ/HgKq41nlKvlhccD5kaCr/P+Hu0wPNKPJOH7en+YrJu/9EgqUF+88w5Jb6KNcjOFMhfX4B2asfeAtIGuHObQ== + dependencies: + debug "2" + streamx@^2.12.5, streamx@^2.15.0, streamx@^2.25.0: version "2.28.0" resolved "https://registry.yarnpkg.com/streamx/-/streamx-2.28.0.tgz#035ab56057b7ed2211b51d532e6973f0f99fbf11" @@ -11243,7 +11285,7 @@ uglify-js@^3.1.4: resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.19.3.tgz#82315e9bbc6f2b25888858acd1fff8441035b77f" integrity sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ== -ultrahtml@^1.6.0: +ultrahtml@^1.6.0, ultrahtml@^1.7.0: version "1.7.0" resolved "https://registry.yarnpkg.com/ultrahtml/-/ultrahtml-1.7.0.tgz#8e7d597e338a481ea82852a9ba5e8021c54aed10" integrity sha512-2xRd0VHoAQE4M+vF/DvFFB7pUV0ZxTW1TLi7lHQWnF/Sb5TPeEUV/l+hxcNnGO00ZXGnR0voCMmYRKQf+rvJ2g== @@ -11273,6 +11315,11 @@ unctx@^2.5.0: magic-string "^0.30.21" unplugin "^2.3.11" +unctx@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/unctx/-/unctx-3.0.0.tgz#82e2f20c8f9db13a443871caddfdd1af7af2eff1" + integrity sha512-DoXdZVeyi2jyEsn86i8MO5RTItm1kffUkH9/+DQORn3Q688AMOy2551CIl6AdGL2UpwD675wtbNOl75wIQN/uA== + undici-types@~5.26.4: version "5.26.5" resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" @@ -11393,7 +11440,7 @@ unplugin@^2.3.11: picomatch "^4.0.3" webpack-virtual-modules "^0.6.2" -unplugin@^3.0.0: +unplugin@^3.0.0, unplugin@^3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/unplugin/-/unplugin-3.3.0.tgz#18de476b6130a6cde94db87f70ac32b97757f3c9" integrity sha512-qa66K+crbfyE6JK10GjvbJeRrOsuC/JpbnHctfyp/i4oBTxWOzJfRZyDiOk1PtErMFRu8JhsU/wPvOdBNWe5Rg== @@ -11542,20 +11589,18 @@ vite-node@^5.3.0: pathe "^2.0.3" vite "^7.3.1" -vite-plugin-checker@^0.13.0: - version "0.13.0" - resolved "https://registry.yarnpkg.com/vite-plugin-checker/-/vite-plugin-checker-0.13.0.tgz#5593c19281a201546c81ee66c79e0c097ba5140a" - integrity sha512-14EkOZmfinVZNxRmg2uCNDwtqGc/33lU/UEJansHgu27+ad+r6mMBf1Xtnq57jGZWiO/xzwtiEKPYsganw7ZFQ== +vite-plugin-checker@^0.14.4: + version "0.14.4" + resolved "https://registry.yarnpkg.com/vite-plugin-checker/-/vite-plugin-checker-0.14.4.tgz#1cb7ee321a95c82f89d960413862dd67e7cc6703" + integrity sha512-Tw0U9UgHIRiZ+Yoe4Gh0RrYoBiCVmO9j4tomVdYr0KUjUsqXMPhqW8ouoSWmOzGp5Iimipbl3bNXZcK7OeP7Qg== dependencies: - "@babel/code-frame" "^7.27.1" - chokidar "^4.0.3" + "@babel/code-frame" "^7.29.0" + chokidar "^5.0.0" npm-run-path "^6.0.0" picocolors "^1.1.1" picomatch "^4.0.4" proper-lockfile "^4.1.2" tiny-invariant "^1.3.3" - tinyglobby "^0.2.15" - vscode-uri "^3.1.0" vite-plugin-dts@^3.7.1, vite-plugin-dts@^3.7.3, vite-plugin-dts@^3.8.0: version "3.9.1" @@ -11636,7 +11681,7 @@ vite@^5.0.0, vite@^5.0.12, vite@^5.2.0, vite@^5.4.17: optionalDependencies: fsevents "~2.3.3" -"vite@^5.0.0 || ^6.0.0 || ^7.0.0-0", vite@^7.3.1, vite@^7.3.3: +"vite@^5.0.0 || ^6.0.0 || ^7.0.0-0", vite@^7.3.1, vite@^7.3.6: version "7.3.6" resolved "https://registry.yarnpkg.com/vite/-/vite-7.3.6.tgz#0547a395e68d3746e9a505f1fd4469fe09b49cc4" integrity sha512-4XP60spRGjSZFf1qYH+dJIkK2znL3zQfl9KkOV9MkkRR/3Dls0dxaBsQPTloEc5BLXWPL9vsOxopxyKoMmDueg== @@ -11773,12 +11818,12 @@ vscode-textmate@^8.0.0: resolved "https://registry.yarnpkg.com/vscode-textmate/-/vscode-textmate-8.0.0.tgz#2c7a3b1163ef0441097e0b5d6389cd5504b59e5d" integrity sha512-AFbieoL7a5LMqcnOF04ji+rpXadgOXnZsxQr//r83kLPr7biP7am3g9zbaZIaBGwBRWeSvoMD4mgPdX3e4NWBg== -vscode-uri@^3.0.8, vscode-uri@^3.1.0: +vscode-uri@^3.0.8: version "3.1.0" resolved "https://registry.yarnpkg.com/vscode-uri/-/vscode-uri-3.1.0.tgz#dd09ec5a66a38b5c3fffc774015713496d14e09c" integrity sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ== -vue-bundle-renderer@^2.2.0: +vue-bundle-renderer@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/vue-bundle-renderer/-/vue-bundle-renderer-2.3.1.tgz#e14cfd4fc0b05fd91ad67bbe67f2ea6c77c23ed7" integrity sha512-7F4LNMopUw5RgYWo4zCmVUHCc6aQRC6dCKHUYkM/n+fux4AUGdL1x6m5A515WWyFysRRN7cx3hBzVqoisfRfzw== @@ -11861,7 +11906,7 @@ vue-tsc@^3.3.5: "@volar/typescript" "2.4.28" "@vue/language-core" "3.3.7" -vue@^3.5.34, vue@^3.5.38: +vue@^3.5.38, vue@^3.5.40: version "3.5.40" resolved "https://registry.yarnpkg.com/vue/-/vue-3.5.40.tgz#c4a9d4c62f98ea33aa811a75c3fbd476dc782184" integrity sha512-+8PJ4SJXdn/cHGImF4CKdxlWHIN5Dkt7DoufRREM6h6uVCx2m7QxgcEQmmzyOK8A9mcafg7sFbJFYsdFVubTig== From bfc666af4648363240640e9af992c3487cc6947a Mon Sep 17 00:00:00 2001 From: Mike Rudat Date: Sat, 18 Jul 2026 21:31:14 +0000 Subject: [PATCH 05/11] fix: update Angular onRedirect test to use 3-segment redirect-value format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RedirectHelper now stores nonce:codeVerifier:state (three colon-delimited segments) instead of the previous nonce:state (two segments). The Angular sdkcore/ directory is generated by 'yarn get-sdk-core' which copies packages/core/src/ verbatim — so in CI the Angular RedirectHelper picks up the updated parser automatically. The test was writing the old two-segment format 'abc123:/welcome-page', which the new parser splits as [nonce='abc123', codeVerifier='/welcome-page', state=''] — returning undefined for state instead of '/welcome-page'. Fix: write 'abc123::/welcome-page' (empty codeVerifier segment, matching cookie mode where no verifier is stored). --- .../fusionauth-angular-sdk/src/lib/fusion-auth.service.spec.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/fusion-auth.service.spec.ts b/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/fusion-auth.service.spec.ts index e48d08b..753b410 100644 --- a/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/fusion-auth.service.spec.ts +++ b/packages/sdk-angular/projects/fusionauth-angular-sdk/src/lib/fusion-auth.service.spec.ts @@ -132,7 +132,8 @@ describe('FusionAuthService', () => { mockIsLoggedIn(); const stateValue = '/welcome-page'; - localStorage.setItem('fa-sdk-redirect-value', `abc123:${stateValue}`); + // Format: nonce:codeVerifier:state (empty verifier in cookie mode) + localStorage.setItem('fa-sdk-redirect-value', `abc123::${stateValue}`); const onRedirect = vi.fn(); configureTestingModule({ ...config, onRedirect }); From 27b9ab63d3af0c3bc1b23f9c665cfbfe464b7c73 Mon Sep 17 00:00:00 2001 From: Mike Rudat Date: Sat, 18 Jul 2026 21:39:12 +0000 Subject: [PATCH 06/11] fix: update Vue and React onRedirect tests to use 3-segment redirect-value format MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RedirectHelper now stores nonce:codeVerifier:state (three colon-delimited segments) instead of nonce:state (two segments). Both sdk-vue and sdk-react import SDKCore directly from @fusionauth-sdk/core (via the @fusionauth-sdk/* tsconfig path alias), so their tests exercise the live, current RedirectHelper — same root cause as the earlier Angular fix. - packages/sdk-vue/src/createFusionAuth/createFusionAuth.test.ts: was seeding the old 2-segment format ('rAnd0mStR1ng:'), causing the new state getter to return undefined instead of the expected state value. Fixed to 'rAnd0mStR1ng::' (empty codeVerifier segment). - packages/sdk-react/src/components/providers/FusionAuthProvider.test.tsx: had the same stale 2-segment seed, but wasn't caught by CI because the assertion only checked toHaveBeenCalled() (no argument check). Fixed the seed format and strengthened the assertion to toHaveBeenCalledWith(stateValue) to restore real coverage of the callback argument. --- .../src/components/providers/FusionAuthProvider.test.tsx | 5 +++-- .../sdk-vue/src/createFusionAuth/createFusionAuth.test.ts | 3 ++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/sdk-react/src/components/providers/FusionAuthProvider.test.tsx b/packages/sdk-react/src/components/providers/FusionAuthProvider.test.tsx index e162335..9bcf21b 100644 --- a/packages/sdk-react/src/components/providers/FusionAuthProvider.test.tsx +++ b/packages/sdk-react/src/components/providers/FusionAuthProvider.test.tsx @@ -121,12 +121,13 @@ describe('FusionAuthProvider', () => { mockIsLoggedIn(); const stateValue = 'hello-world'; - localStorage.setItem('fa-sdk-redirect-value', `abc123:${stateValue}`); + // Format: nonce:codeVerifier:state (empty verifier in cookie mode) + localStorage.setItem('fa-sdk-redirect-value', `abc123::${stateValue}`); const onRedirect = vi.fn(); renderWithWrapper({ ...TEST_CONFIG, onRedirect }); - expect(onRedirect).toHaveBeenCalled(); + expect(onRedirect).toHaveBeenCalledWith(stateValue); }); test('Will not invoke onRedirect if no redirect value is found in localStorage', () => { diff --git a/packages/sdk-vue/src/createFusionAuth/createFusionAuth.test.ts b/packages/sdk-vue/src/createFusionAuth/createFusionAuth.test.ts index ec14ab3..26e72be 100644 --- a/packages/sdk-vue/src/createFusionAuth/createFusionAuth.test.ts +++ b/packages/sdk-vue/src/createFusionAuth/createFusionAuth.test.ts @@ -64,9 +64,10 @@ describe('createFusionAuth', () => { mockIsLoggedIn(); const onRedirect = vi.fn(); const expectedStateValue = 'redirect-callback-test'; + // Format: nonce:codeVerifier:state (empty verifier in cookie mode) localStorage.setItem( 'fa-sdk-redirect-value', - `rAnd0mStR1ng:${expectedStateValue}`, + `rAnd0mStR1ng::${expectedStateValue}`, ); createFusionAuth({ ...config, onRedirect }); From 920081dbdd4ea4523183f343911b26c98317bb35 Mon Sep 17 00:00:00 2001 From: Mike Rudat Date: Mon, 20 Jul 2026 20:39:15 +0000 Subject: [PATCH 07/11] test: add deterministic nonce-retry smoke test (T2-4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit FusionAuth (as the Authorization Server) never issues a use_dpop_nonce challenge itself — per FusionAuth's DPoP docs, nonce enforcement is a Resource Server responsibility implemented by your own APIs, not something FusionAuth's own endpoints (e.g. /oauth2/userinfo) do. This is why the existing T2-3 test can only assert structurally ('either outcome is a pass') against a real FusionAuth instance. T2-4 adds a self-contained, deterministic test that mocks globalThis.fetch to simulate a Resource Server 401 response with a use_dpop_nonce challenge (WWW-Authenticate + DPoP-Nonce headers), then verifies: - exactly one retry occurs (not zero, not more than one) - the first proof has no nonce claim - the retried proof carries the exact server-issued nonce claim - both proofs target the same htu/htm Uses its own fresh DPoPManager (via the existing makeManager() helper) so it does not depend on shared state/order from the Tier 1 tests, and requires no live FusionAuth instance. --- e2e/tests/dpop-smoke.test.ts | 82 ++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) diff --git a/e2e/tests/dpop-smoke.test.ts b/e2e/tests/dpop-smoke.test.ts index 8363e32..41a248b 100644 --- a/e2e/tests/dpop-smoke.test.ts +++ b/e2e/tests/dpop-smoke.test.ts @@ -592,6 +592,88 @@ test.describe('DPoP smoke tests', () => { } }); + test('T2-4: nonce retry (deterministic) — DPoPManager.fetch() retries with the correct nonce claim when the resource server issues a use_dpop_nonce challenge', async () => { + // FusionAuth (as the Authorization Server) never issues a use_dpop_nonce + // challenge itself — nonce enforcement is explicitly a Resource Server + // responsibility that your own APIs implement (see FusionAuth's DPoP + // docs: "FusionAuth currently does not require nonce handling, but your + // APIs may require one for resource access"). T2-3 above can only assert + // structurally against a real FusionAuth endpoint because it can never + // reliably force the challenge. + // + // This test simulates a Resource Server that DOES require a nonce, by + // mocking globalThis.fetch (DPoPManager.fetch() calls the native fetch + // directly, so this is a faithful substitute for a real RS response). + // It uses its own fresh DPoPManager so it does not depend on shared + // state/order from the Tier 1 tests above. + + const FAKE_RESOURCE_URL = 'https://fake-resource-server.example.com/data'; + const SERVER_NONCE = 'server-issued-nonce-abc123'; + + const nonceManager = makeManager(); + + let callCount = 0; + let firstProof: string | null = null; + let secondProof: string | null = null; + + const originalFetch = globalThis.fetch; + globalThis.fetch = async ( + input: RequestInfo | URL, + init?: RequestInit, + ): Promise => { + callCount++; + const dpopHeader = new Headers(init?.headers).get('DPoP'); + + if (callCount === 1) { + firstProof = dpopHeader; + // Simulate a Resource Server that requires a fresh nonce — per + // RFC 9449 §8, a 401 with a WWW-Authenticate header containing + // 'use_dpop_nonce' and a DPoP-Nonce response header. + return new Response(null, { + status: 401, + headers: { + 'WWW-Authenticate': + 'DPoP error="use_dpop_nonce", error_description="Resource server requires a nonce"', + 'DPoP-Nonce': SERVER_NONCE, + }, + }); + } + + secondProof = dpopHeader; + return new Response(JSON.stringify({ ok: true }), { status: 200 }); + }; + + try { + const response = await nonceManager.fetch(FAKE_RESOURCE_URL); + + expect(response.status).toBe(200); + // Exactly one retry — original call + single nonce retry, no more. + expect(callCount).toBe(2); + + expect(firstProof).not.toBeNull(); + expect(secondProof).not.toBeNull(); + + // The first proof (before the server ever provided a nonce) must NOT + // carry a nonce claim. + const firstPayload = decodeJwt(firstProof!); + expect(firstPayload.nonce).toBeUndefined(); + + // The retried proof MUST carry the server-issued nonce claim, proving + // DPoPManager cached it from the DPoP-Nonce response header and used + // it to regenerate the proof before retrying. + const secondPayload = decodeJwt(secondProof!); + expect(secondPayload.nonce).toBe(SERVER_NONCE); + + // Both proofs must otherwise target the same resource/method. + expect(firstPayload.htu).toBe(FAKE_RESOURCE_URL); + expect(secondPayload.htu).toBe(FAKE_RESOURCE_URL); + expect(firstPayload.htm).toBe('GET'); + expect(secondPayload.htm).toBe('GET'); + } finally { + globalThis.fetch = originalFetch; + } + }); + // ------------------------------------------------------------------------- // Tier 1 — Logout / clear // ------------------------------------------------------------------------- From b711f2de4e80730471c7a4d0696d1358328b875a Mon Sep 17 00:00:00 2001 From: Mike Rudat Date: Mon, 20 Jul 2026 22:14:18 +0000 Subject: [PATCH 08/11] fix: revert startLogin() to void, address Copilot PR review comment (ENG-4786) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reverts SDKCore.startLogin()'s signature from 'async ... Promise' back to plain 'void', matching the public SDKContext/framework-wrapper types exactly (SDKContext.ts, FusionAuthProviderContext.ts, Vue's FusionAuth, Angular's SDKContext.ts all still declare startLogin: (state?) => void). Although tsc --noEmit already reported zero errors thanks to TypeScript's void-returning-function compatibility rule, the underlying concern was real: none of the three framework wrappers (React's useRedirecting, Vue's login(), Angular's startLogin()) awaited or caught the promise, so a DPoP async failure (e.g. crypto.subtle unavailable, IndexedDB blocked) would surface as an unhandled promise rejection. - SDKCore.ts: startLogin() is synchronous again. In DPoP mode it fires a new private async startDpopLogin() and catches failures via the new optional SDKConfig.onLoginFailure callback (falls back to console.error), following the existing onAutoRefreshFailure convention. Cookie mode is unchanged. - SDKConfig.ts: add onLoginFailure?: (error: Error) => void. - SDKCore.test.ts: DPoP startLogin() tests now call startLogin() without awaiting it and use vi.waitFor() to wait for window.location.assign before asserting. Added two new tests covering onLoginFailure and the console.error fallback. - e2e/tests/dpop-smoke.test.ts: added a createAssignWaiter() helper (a deferred promise resolved when window.location.assign is called) and reworked T0-1/T0-2/T0-3 to use it instead of awaiting startLogin() directly. T0-3 now explicitly waits for core1's redirect before swapping IndexedDB for core2, preserving the original sequential-completion guarantee that awaiting startLogin() used to provide implicitly. No changes needed to SDKContext.ts, FusionAuthProviderContext.ts, Vue's types, Angular's types/service, or any framework wrapper implementation — zero blast radius outside packages/core, as intended. --- e2e/tests/dpop-smoke.test.ts | 99 ++++++++++++++++------- packages/core/src/SDKConfig/SDKConfig.ts | 10 +++ packages/core/src/SDKCore/SDKCore.test.ts | 64 ++++++++++++--- packages/core/src/SDKCore/SDKCore.ts | 45 ++++++++--- 4 files changed, 165 insertions(+), 53 deletions(-) diff --git a/e2e/tests/dpop-smoke.test.ts b/e2e/tests/dpop-smoke.test.ts index 41a248b..a41c48b 100644 --- a/e2e/tests/dpop-smoke.test.ts +++ b/e2e/tests/dpop-smoke.test.ts @@ -70,6 +70,44 @@ function makeManager(): DPoPManager { return new DPoPManager(CLIENT_ID, 'memory'); } +/** + * Creates a deferred `window.location.assign` stub paired with a promise + * that resolves with the assigned URL. + * + * `SDKCore.startLogin()` is synchronous (`void`) — in DPoP mode it kicks off + * an async chain (key-pair generation, PKCE, etc.) internally and does not + * return a promise the caller can await. This helper lets Tier 0 tests wait + * deterministically for that async chain to complete (signaled by + * `window.location.assign` being called) instead of awaiting `startLogin()` + * directly. + */ +function createAssignWaiter(timeoutMs = 5_000): { + assign: (url: string) => void; + waitForUrl: () => Promise; +} { + let resolveUrl!: (url: string) => void; + const urlPromise = new Promise(resolve => { + resolveUrl = resolve; + }); + + return { + assign: (url: string) => resolveUrl(url), + waitForUrl: () => + Promise.race([ + urlPromise, + new Promise((_, reject) => + setTimeout( + () => + reject( + new Error('Timed out waiting for window.location.assign()'), + ), + timeoutMs, + ), + ), + ]), + }; +} + /** * Drive FusionAuth's hosted login page through Playwright and return the * authorization `code` captured from the redirect to REDIRECT_URI. @@ -214,18 +252,15 @@ test.describe('Tier 0: SDKCore.startLogin() DPoP mode', () => { }); test('T0-1: startLogin() redirects to /oauth2/authorize with dpop_jkt and code_challenge', async () => { - let assignedUrl: string | null = null; + const { assign, waitForUrl } = createAssignWaiter(); // @ts-ignore - globalThis.window.location = { - assign: (url: string) => { - assignedUrl = url; - }, - }; + globalThis.window.location = { assign }; - await new SDKCore(DPOP_CONFIG).startLogin(); + // startLogin() is synchronous (void) — fire and wait for the redirect. + new SDKCore(DPOP_CONFIG).startLogin(); + const assignedUrl = await waitForUrl(); - expect(assignedUrl).not.toBeNull(); - const url = new URL(assignedUrl!); + const url = new URL(assignedUrl); expect(url.origin).toBe(FA_URL); expect(url.pathname).toBe('/oauth2/authorize'); @@ -248,20 +283,16 @@ test.describe('Tier 0: SDKCore.startLogin() DPoP mode', () => { test('T0-2: startLogin() persists code_verifier and state via RedirectHelper', async () => { const STATE = 'e2e-smoke-state'; - let assignedUrl: string | null = null; + const { assign, waitForUrl } = createAssignWaiter(); // @ts-ignore - globalThis.window.location = { - assign: (url: string) => { - assignedUrl = url; - }, - }; + globalThis.window.location = { assign }; const core = new SDKCore(DPOP_CONFIG); - await core.startLogin(STATE); + core.startLogin(STATE); + const assignedUrl = await waitForUrl(); - expect(assignedUrl).not.toBeNull(); - const url = new URL(assignedUrl!); + const url = new URL(assignedUrl); // State is included in the authorize URL. expect(url.searchParams.get('state')).toBe(STATE); @@ -279,14 +310,6 @@ test.describe('Tier 0: SDKCore.startLogin() DPoP mode', () => { }); test('T0-3: two startLogin() calls produce different key pairs and PKCE values', async () => { - const urls: URL[] = []; - // @ts-ignore - globalThis.window.location = { - assign: (url: string) => { - urls.push(new URL(url)); - }, - }; - const core1 = new SDKCore(DPOP_CONFIG); // Each SDKCore gets its own DPoPManager with its own key pair. @@ -295,17 +318,31 @@ test.describe('Tier 0: SDKCore.startLogin() DPoP mode', () => { const core2 = new SDKCore(DPOP_CONFIG); - await core1.startLogin(); + // Wait for core1's full async chain (including its key pair being + // written to the *first* IndexedDB instance) to complete before + // swapping IndexedDB out for core2 — startLogin() is fire-and-forget, so + // this ordering must be enforced explicitly rather than relying on + // sequential awaits on startLogin() itself. + const waiter1 = createAssignWaiter(); + // @ts-ignore + globalThis.window.location = { assign: waiter1.assign }; + core1.startLogin(); + const url1 = new URL(await waiter1.waitForUrl()); + globalThis.localStorage.clear(); // @ts-ignore globalThis.indexedDB = new IDBFactory(); - await core2.startLogin(); - expect(urls).toHaveLength(2); + const waiter2 = createAssignWaiter(); + // @ts-ignore + globalThis.window.location = { assign: waiter2.assign }; + core2.startLogin(); + const url2 = new URL(await waiter2.waitForUrl()); + // Different key pairs → different dpop_jkt. // Different PKCE verifiers → different code_challenge. - expect(urls[0]!.searchParams.get('code_challenge')).not.toBe( - urls[1]!.searchParams.get('code_challenge'), + expect(url1.searchParams.get('code_challenge')).not.toBe( + url2.searchParams.get('code_challenge'), ); }); }); diff --git a/packages/core/src/SDKConfig/SDKConfig.ts b/packages/core/src/SDKConfig/SDKConfig.ts index d2a0eed..f4797a7 100644 --- a/packages/core/src/SDKConfig/SDKConfig.ts +++ b/packages/core/src/SDKConfig/SDKConfig.ts @@ -112,4 +112,14 @@ export interface SDKConfig { * Defaults to `'localStorage'`. */ dpopTokenStorage?: 'localStorage' | 'memory'; + + /** + * Callback invoked if `startLogin()` fails in DPoP mode (e.g. the DPoP key + * pair could not be generated/loaded, or PKCE parameter generation + * failed). `startLogin()` is synchronous (`void`), so this is the only way + * to observe an async failure in the DPoP login flow. Defaults to logging + * the error via `console.error` if not provided. Only relevant when + * `useDpop: true`. + */ + onLoginFailure?: (error: Error) => void; } diff --git a/packages/core/src/SDKCore/SDKCore.test.ts b/packages/core/src/SDKCore/SDKCore.test.ts index bbb4688..71b104b 100644 --- a/packages/core/src/SDKCore/SDKCore.test.ts +++ b/packages/core/src/SDKCore/SDKCore.test.ts @@ -139,8 +139,8 @@ describe('SDKCore', () => { expect(handlePreRedirect).toHaveBeenCalledTimes(0); - // startLogin is async but cookie-mode branch has no awaits — synchronous - // side-effects (handlePreRedirect, window.location.assign) fire immediately. + // startLogin is synchronous in cookie mode — side-effects + // (handlePreRedirect, window.location.assign) fire immediately. core.startLogin('/login'); core.startRegister(); @@ -247,9 +247,11 @@ describe('SDKCore', () => { const location = mockWindowLocation(vi); const core = new SDKCore(dpopConfig); - await core.startLogin(); + // startLogin() is synchronous (void) — the DPoP chain runs async + // internally. Wait for the redirect to happen before asserting. + core.startLogin(); + await vi.waitFor(() => expect(location.assign).toHaveBeenCalledOnce()); - expect(location.assign).toHaveBeenCalledOnce(); const assignedUrl = new URL( (location.assign as ReturnType).mock.calls[0][0], ); @@ -273,10 +275,11 @@ describe('SDKCore', () => { ); vi.spyOn(Pkce, 'generateCodeVerifier').mockReturnValue(MOCK_VERIFIER); vi.spyOn(Pkce, 'generateCodeChallenge').mockResolvedValue(MOCK_CHALLENGE); - mockWindowLocation(vi); + const location = mockWindowLocation(vi); const core = new SDKCore(dpopConfig); - await core.startLogin(); + core.startLogin(); + await vi.waitFor(() => expect(location.assign).toHaveBeenCalledOnce()); const redirectHelper = new RedirectHelper(); expect(redirectHelper.getCodeVerifier()).toBe(MOCK_VERIFIER); @@ -294,7 +297,8 @@ describe('SDKCore', () => { const location = mockWindowLocation(vi); const core = new SDKCore(dpopConfig); - await core.startLogin('my-state'); + core.startLogin('my-state'); + await vi.waitFor(() => expect(location.assign).toHaveBeenCalledOnce()); const assignedUrl = new URL( (location.assign as ReturnType).mock.calls[0][0], @@ -311,20 +315,58 @@ describe('SDKCore', () => { .mockResolvedValue(MOCK_JKT); vi.spyOn(Pkce, 'generateCodeVerifier').mockReturnValue(MOCK_VERIFIER); vi.spyOn(Pkce, 'generateCodeChallenge').mockResolvedValue(MOCK_CHALLENGE); - mockWindowLocation(vi); + const location = mockWindowLocation(vi); const core = new SDKCore(dpopConfig); - await core.startLogin(); + core.startLogin(); + await vi.waitFor(() => expect(location.assign).toHaveBeenCalledOnce()); expect(getOrCreateKeyPair).toHaveBeenCalledOnce(); expect(getThumbprint).toHaveBeenCalledOnce(); }); - it('startLogin() in cookie mode is unaffected by useDpop: false', async () => { + it('reports a DPoP startLogin() failure via onLoginFailure instead of an unhandled rejection', async () => { + const failure = new Error('crypto.subtle unavailable'); + vi.spyOn(DPoPManager.prototype, 'getOrCreateKeyPair').mockRejectedValue( + failure, + ); + mockWindowLocation(vi); + + const onLoginFailure = vi.fn(); + const core = new SDKCore({ ...dpopConfig, onLoginFailure }); + + core.startLogin(); + await vi.waitFor(() => expect(onLoginFailure).toHaveBeenCalledOnce()); + + expect(onLoginFailure).toHaveBeenCalledWith(failure); + }); + + it('falls back to console.error when a DPoP startLogin() failure occurs and onLoginFailure is not configured', async () => { + const failure = new Error('IndexedDB blocked'); + vi.spyOn(DPoPManager.prototype, 'getOrCreateKeyPair').mockRejectedValue( + failure, + ); + mockWindowLocation(vi); + const consoleError = vi + .spyOn(console, 'error') + .mockImplementation(() => {}); + + const core = new SDKCore(dpopConfig); // no onLoginFailure configured + core.startLogin(); + await vi.waitFor(() => + expect(consoleError).toHaveBeenCalledWith( + 'FusionAuth SDK: startLogin failed', + failure, + ), + ); + }); + + it('startLogin() in cookie mode is unaffected by useDpop: false', () => { const location = mockWindowLocation(vi); const core = new SDKCore(config); // no useDpop - await core.startLogin('some-state'); + // Cookie mode is fully synchronous — no waiting required. + core.startLogin('some-state'); expect(location.assign).toHaveBeenCalledOnce(); const assignedUrl = new URL( diff --git a/packages/core/src/SDKCore/SDKCore.ts b/packages/core/src/SDKCore/SDKCore.ts index 0903ad6..f969204 100644 --- a/packages/core/src/SDKCore/SDKCore.ts +++ b/packages/core/src/SDKCore/SDKCore.ts @@ -51,7 +51,8 @@ export class SDKCore { /** * Initiates the login flow. * - * In DPoP mode (`useDpop: true`): + * In DPoP mode (`useDpop: true`), this synchronously returns after kicking + * off an async chain that: * 1. Loads or generates the DPoP key pair. * 2. Computes `dpop_jkt` (JWK SHA-256 thumbprint of the public key). * 3. Generates a PKCE `code_verifier` and derives `code_challenge`. @@ -59,21 +60,26 @@ export class SDKCore { * 5. Redirects to FusionAuth `/oauth2/authorize` directly with `dpop_jkt` * and `code_challenge` parameters. * + * `startLogin()` itself is `void` (not `async`) so its signature matches + * the public `SDKContext`/framework wrapper types exactly. If the async + * DPoP chain fails, the error is reported via `SDKConfig.onLoginFailure` + * (or `console.error` if not configured) rather than becoming an unhandled + * promise rejection. + * * In cookie mode: behaves identically to the previous implementation — - * delegates to the companion app server's login path. + * delegates to the companion app server's login path, fully synchronously. * * @param state Optional OAuth2 state value echoed back post-login. */ - async startLogin(state?: string): Promise { + startLogin(state?: string): void { if (this.dpopManager) { - await this.dpopManager.getOrCreateKeyPair(); - const dpopJkt = await this.dpopManager.getThumbprint(); - const codeVerifier = Pkce.generateCodeVerifier(); - const codeChallenge = await Pkce.generateCodeChallenge(codeVerifier); - this.redirectHelper.handlePreRedirect(state, codeVerifier); - window.location.assign( - this.urlHelper.getAuthorizeUrl(dpopJkt, codeChallenge, state), - ); + this.startDpopLogin(state).catch(error => { + if (this.config.onLoginFailure) { + this.config.onLoginFailure(error as Error); + } else { + console.error('FusionAuth SDK: startLogin failed', error); + } + }); return; } @@ -82,6 +88,23 @@ export class SDKCore { window.location.assign(this.urlHelper.getLoginUrl(state)); } + /** + * Performs the DPoP-mode login flow. See {@link startLogin} for the full + * step-by-step description. Split out as its own async method so that + * `startLogin()` itself can remain synchronous (`void`) while still + * performing the necessary async key-pair/PKCE work before redirecting. + */ + private async startDpopLogin(state?: string): Promise { + await this.dpopManager!.getOrCreateKeyPair(); + const dpopJkt = await this.dpopManager!.getThumbprint(); + const codeVerifier = Pkce.generateCodeVerifier(); + const codeChallenge = await Pkce.generateCodeChallenge(codeVerifier); + this.redirectHelper.handlePreRedirect(state, codeVerifier); + window.location.assign( + this.urlHelper.getAuthorizeUrl(dpopJkt, codeChallenge, state), + ); + } + startRegister(state?: string) { this.redirectHelper.handlePreRedirect(state); window.location.assign(this.urlHelper.getRegisterUrl(state)); From 8cfc6071684d280bf7afdb78731fd73fc9f1876c Mon Sep 17 00:00:00 2001 From: Mike Rudat Date: Mon, 20 Jul 2026 23:03:49 +0000 Subject: [PATCH 09/11] docs: fix stale/ambiguous state-reconstruction description in RedirectHelper.ts MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Addresses a Copilot PR review comment. The class-level doc comment said state is retrieved by joining segments 'after index 1 (skipping the verifier segment)' — phrasing left over from before the codeVerifier segment existed. The storage format is nonce:codeVerifier:state (3 segments), and the actual implementation (line 85) skips both the nonce (index 0) and codeVerifier (index 1) segments, with state starting at index 2 — not just 'the verifier segment' as the old wording implied. Doc-only change; no logic or test changes needed. --- packages/core/src/RedirectHelper/RedirectHelper.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/core/src/RedirectHelper/RedirectHelper.ts b/packages/core/src/RedirectHelper/RedirectHelper.ts index d7e5030..b0b24a5 100644 --- a/packages/core/src/RedirectHelper/RedirectHelper.ts +++ b/packages/core/src/RedirectHelper/RedirectHelper.ts @@ -8,8 +8,9 @@ * - `codeVerifier` — PKCE `code_verifier` persisted for DPoP token exchange * (ENG-4800). Empty string when not in DPoP mode. * - `state` — optional caller-supplied OAuth2 state value. May contain - * colons; retrieved by joining all segments after index 1 - * (skipping the verifier segment). + * colons; retrieved by skipping the nonce and codeVerifier + * segments (indices 0 and 1) and joining all remaining + * segments (index 2 onward) with `:`. */ export class RedirectHelper { private readonly REDIRECT_VALUE = 'fa-sdk-redirect-value'; From a03dd2e1c44c48a1382cee008dfed2a84e58fceb Mon Sep 17 00:00:00 2001 From: Mike Rudat Date: Tue, 21 Jul 2026 14:59:04 +0000 Subject: [PATCH 10/11] fix: preserve state from legacy 2-segment redirect values (Copilot PR review) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit RedirectHelper.state and getCodeVerifier() always assumed the current 3-segment storage format (nonce:codeVerifier:state). If a user initiates a login redirect on a pre-DPoP SDK version (which wrote the legacy 2-segment nonce:state format) and the app is upgraded to a newer SDK version before they land back — e.g. a deploy that happens while they're on FusionAuth's hosted login page — the leftover legacy value would be misparsed: state would resolve to undefined instead of the real value. Fix: detect the legacy format unambiguously. The current writer (handlePreRedirect) always includes a codeVerifier segment, even when empty, so any value it produces has at least two colons. A stored value with exactly one colon can therefore only be the legacy format. - state getter: if there are exactly 2 segments (1 colon), treat the second segment as the legacy state directly, instead of destructuring past index 1 (which only works for the 3-segment format). - getCodeVerifier(): same legacy-format guard, since a 2-segment value never carried a code_verifier — prevents misreading a fragment of a legacy state value as a verifier. - Documented (as a comment, not a test) the known acceptable limitation: a legacy state value that itself contained a colon is indistinguishable from a current-format value with a non-empty codeVerifier — an inherent ambiguity in a delimiter-based format without a version marker, accepted given the narrow redirect-round-trip window. - RedirectHelper.test.ts: added 4 tests seeding localStorage directly with the legacy format, covering handlePostRedirect's callback value (including empty legacy state), marker cleanup, and getCodeVerifier()'s undefined result. --- .../src/RedirectHelper/RedirectHelper.test.ts | 57 +++++++++++++++++++ .../core/src/RedirectHelper/RedirectHelper.ts | 31 +++++++++- 2 files changed, 86 insertions(+), 2 deletions(-) diff --git a/packages/core/src/RedirectHelper/RedirectHelper.test.ts b/packages/core/src/RedirectHelper/RedirectHelper.test.ts index f147ee0..21af7d6 100644 --- a/packages/core/src/RedirectHelper/RedirectHelper.test.ts +++ b/packages/core/src/RedirectHelper/RedirectHelper.test.ts @@ -140,4 +140,61 @@ describe('RedirectHelper', () => { expect(helper.getCodeVerifier()).toBeUndefined(); }); }); + + // --------------------------------------------------------------------------- + // Legacy 2-segment format (nonce:state) backward-compatibility + // + // Pre-DPoP SDK versions wrote `${nonce}:${state}` (2 segments) instead of + // the current `${nonce}:${codeVerifier}:${state}` (3 segments). A value in + // this legacy format can still be sitting in localStorage if a user + // initiates a login redirect on an older SDK version and the app is + // upgraded to a newer version before they land back (e.g. a deploy that + // happens while they're on FusionAuth's hosted login page). These tests + // seed localStorage directly with the legacy format to simulate that. + // --------------------------------------------------------------------------- + + describe('legacy 2-segment format backward-compatibility', () => { + it('handlePostRedirect() invokes the callback with the legacy state value', () => { + const helper = new RedirectHelper(); + const callback = vi.fn(); + + localStorage.setItem( + 'fa-sdk-redirect-value', + 'legacy-nonce:legacy-state', + ); + helper.handlePostRedirect(callback); + + expect(callback).toHaveBeenCalledWith('legacy-state'); + }); + + it('handlePostRedirect() invokes the callback with undefined for an empty legacy state', () => { + const helper = new RedirectHelper(); + const callback = vi.fn(); + + localStorage.setItem('fa-sdk-redirect-value', 'legacy-nonce:'); + helper.handlePostRedirect(callback); + + expect(callback).toHaveBeenCalledWith(undefined); + }); + + it('removes the legacy redirect marker from localStorage after post-redirect', () => { + localStorage.setItem( + 'fa-sdk-redirect-value', + 'legacy-nonce:legacy-state', + ); + + new RedirectHelper().handlePostRedirect(); + + expect(localStorage.getItem('fa-sdk-redirect-value')).toBeNull(); + }); + + it('getCodeVerifier() returns undefined for a legacy value (never carried a verifier)', () => { + localStorage.setItem( + 'fa-sdk-redirect-value', + 'legacy-nonce:legacy-state', + ); + + expect(new RedirectHelper().getCodeVerifier()).toBeUndefined(); + }); + }); }); diff --git a/packages/core/src/RedirectHelper/RedirectHelper.ts b/packages/core/src/RedirectHelper/RedirectHelper.ts index b0b24a5..3ee0c5a 100644 --- a/packages/core/src/RedirectHelper/RedirectHelper.ts +++ b/packages/core/src/RedirectHelper/RedirectHelper.ts @@ -64,6 +64,12 @@ export class RedirectHelper { const raw = this.storage.getItem(this.REDIRECT_VALUE); if (!raw) return undefined; + // Legacy 2-segment format (nonce:state) from pre-DPoP SDK versions never + // carried a code_verifier. Guard against misreading a fragment of a + // legacy state value as a verifier — see the `state` getter below for the + // full rationale of this legacy-format detection. + if (raw.split(':').length === 2) return undefined; + // Format: randomNonce:codeVerifier:state const firstColon = raw.indexOf(':'); if (firstColon === -1) return undefined; @@ -80,10 +86,31 @@ export class RedirectHelper { const redirectValue = this.storage.getItem(this.REDIRECT_VALUE); if (!redirectValue) return undefined; - // Format: randomNonce:codeVerifier:state + const segments = redirectValue.split(':'); + + // Legacy 2-segment format (nonce:state) from pre-DPoP SDK versions. + // Exactly one colon can only be this legacy format — the current writer + // always produces at least two colons (it always includes a + // codeVerifier segment, even when empty). This preserves `state` for + // users who initiate a login redirect on an older SDK version and land + // back on a newer one (e.g. an app deploy that happens while they're on + // FusionAuth's hosted login page). + // + // Known limitation: if a legacy state value itself contained a colon + // (e.g. old state "return:/page" stored as "nonce:return:/page"), it is + // indistinguishable from a current-format value with a non-empty + // codeVerifier and simple state. This ambiguity is inherent to a + // delimiter-based format without a version marker and is accepted as an + // edge case, given the narrow window (a single redirect round-trip) and + // that state values are typically simple opaque strings/paths. + if (segments.length === 2) { + return segments[1] || undefined; + } + + // Current format: randomNonce:codeVerifier:state // Skip the nonce segment and the codeVerifier segment; join remainder with // ':' to correctly reconstruct state values that themselves contain colons. - const [, , ...stateSegments] = redirectValue.split(':'); + const [, , ...stateSegments] = segments; return stateSegments.join(':') || undefined; } From 50b852f097f68499091ae8001f48849a31d1eefd Mon Sep 17 00:00:00 2001 From: Mike Rudat Date: Tue, 21 Jul 2026 11:14:25 -0600 Subject: [PATCH 11/11] feat: update comments. --- packages/core/src/RedirectHelper/RedirectHelper.ts | 4 ++-- packages/core/src/SDKCore/SDKCore.ts | 2 +- packages/core/src/UrlHelper/UrlHelper.ts | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/core/src/RedirectHelper/RedirectHelper.ts b/packages/core/src/RedirectHelper/RedirectHelper.ts index 3ee0c5a..a0c25f1 100644 --- a/packages/core/src/RedirectHelper/RedirectHelper.ts +++ b/packages/core/src/RedirectHelper/RedirectHelper.ts @@ -5,8 +5,8 @@ * Storage format: `${randomNonce}:${codeVerifier ?? ''}:${state ?? ''}` * * - `randomNonce` — prevents replay; marks that a redirect was initiated. - * - `codeVerifier` — PKCE `code_verifier` persisted for DPoP token exchange - * (ENG-4800). Empty string when not in DPoP mode. + * - `codeVerifier` — PKCE `code_verifier` persisted for DPoP token exchange. + * Empty string when not in DPoP mode. * - `state` — optional caller-supplied OAuth2 state value. May contain * colons; retrieved by skipping the nonce and codeVerifier * segments (indices 0 and 1) and joining all remaining diff --git a/packages/core/src/SDKCore/SDKCore.ts b/packages/core/src/SDKCore/SDKCore.ts index f969204..3a9a411 100644 --- a/packages/core/src/SDKCore/SDKCore.ts +++ b/packages/core/src/SDKCore/SDKCore.ts @@ -67,7 +67,7 @@ export class SDKCore { * promise rejection. * * In cookie mode: behaves identically to the previous implementation — - * delegates to the companion app server's login path, fully synchronously. + * delegates to the Hosted Backend API, fully synchronously. * * @param state Optional OAuth2 state value echoed back post-login. */ diff --git a/packages/core/src/UrlHelper/UrlHelper.ts b/packages/core/src/UrlHelper/UrlHelper.ts index b22880f..ab0bac5 100644 --- a/packages/core/src/UrlHelper/UrlHelper.ts +++ b/packages/core/src/UrlHelper/UrlHelper.ts @@ -75,7 +75,7 @@ export class UrlHelper { /** * Builds the direct `/oauth2/authorize` URL used in DPoP mode. - * Targets FusionAuth directly (not the companion app server). + * Targets FusionAuth directly (not the Hosted Backend API). * * @param dpopJkt The DPoP public key JWK thumbprint for the `dpop_jkt` parameter. * @param codeChallenge The PKCE code challenge value.