From 40aef1ea2e38315358dedf3a16618860d7504091 Mon Sep 17 00:00:00 2001
From: "Abdulmalik A."
Date: Fri, 28 Aug 2026 18:02:57 +0100
Subject: [PATCH 1/4] feat(send): validate destination address, amount minimum,
and balance before submit
Adds client-side validation to the send form so bad input never reaches
the API (Issue #420):
- Extracts the Stellar public-key regex that already existed in
WalletAddressInput into a shared lib/validation/stellar-address.ts
utility, and reuses it in ConnectStep (reject a malformed address
returned by Freighter or restored from localStorage before it can
become the funding/recovery address) and WalletAddressInput itself.
- DetailsStep now enforces a minimum send amount per asset and blocks
amounts above the connected wallet's actual balance, looked up via a
new lib/wallet-balance.ts (Horizon fetch, fails open/never blocks when
the balance can't be determined).
- Field errors are now also validated on blur, not just on submit.
Also fixes a pre-existing, unrelated bug that was blocking this repo's
whole test suite from compiling: lib/create-bridgelet-client.ts had
every template literal written with literally-escaped backticks
(\` / \${) instead of real ones, a syntax error that made the file (and
everything importing it, including the send flow) fail to parse. Fixed
so `npm test` actually exercises the send flow's existing coverage, and
added the missing @testing-library/dom devDependency the test suite
needs.
Tests: lib/validation/stellar-address.test.ts,
lib/wallet-balance.test.ts, and new/updated coverage in
details-step.test.tsx and connect-step.test.tsx (53 tests, all passing).
Co-Authored-By: Claude Sonnet 5
---
.../send-form/steps/connect-step.test.tsx | 29 +
.../send-form/steps/connect-step.tsx | 16 +
.../send-form/steps/details-step.test.tsx | 150 +
.../send-form/steps/details-step.tsx | 70 +-
frontend/components/wallet-address-input.tsx | 10 +-
frontend/lib/create-bridgelet-client.ts | 20 +-
.../lib/validation/stellar-address.test.ts | 45 +
frontend/lib/validation/stellar-address.ts | 25 +
frontend/lib/wallet-balance.test.ts | 87 +
frontend/lib/wallet-balance.ts | 74 +
frontend/package-lock.json | 3629 +++++------------
frontend/package.json | 3 +-
12 files changed, 1538 insertions(+), 2620 deletions(-)
create mode 100644 frontend/components/send-form/steps/details-step.test.tsx
create mode 100644 frontend/lib/validation/stellar-address.test.ts
create mode 100644 frontend/lib/validation/stellar-address.ts
create mode 100644 frontend/lib/wallet-balance.test.ts
create mode 100644 frontend/lib/wallet-balance.ts
diff --git a/frontend/components/send-form/steps/connect-step.test.tsx b/frontend/components/send-form/steps/connect-step.test.tsx
index 5e4f7fcf..5e8cd539 100644
--- a/frontend/components/send-form/steps/connect-step.test.tsx
+++ b/frontend/components/send-form/steps/connect-step.test.tsx
@@ -180,4 +180,33 @@ describe('ConnectStep', () => {
expect(screen.getByRole('button', { name: /connect freighter wallet/i })).toBeEnabled(),
);
});
+
+ // ── Destination address format validation (Issue #420) ──────────────────
+
+ it('rejects a malformed address returned by Freighter instead of connecting', async () => {
+ mockConnectFreighter.mockResolvedValue({ publicKey: 'not-a-real-address' });
+ const onConnected = vi.fn();
+ const user = userEvent.setup();
+
+ render( );
+ await user.click(screen.getByRole('button', { name: /connect freighter wallet/i }));
+
+ expect(await screen.findByRole('alert')).toHaveTextContent(/unexpected format/i);
+ expect(onConnected).not.toHaveBeenCalled();
+ expect(mockPersistWallet).not.toHaveBeenCalled();
+ });
+
+ it('discards a corrupted persisted wallet address instead of auto-connecting it', async () => {
+ mockLoadPersistedWallet.mockReturnValue({ publicKey: 'corrupted-value', type: 'freighter' });
+ const onConnected = vi.fn();
+
+ render( );
+
+ // Give the restore effect time to run.
+ await new Promise((r) => setTimeout(r, 50));
+
+ expect(onConnected).not.toHaveBeenCalled();
+ expect(mockClearPersistedWallet).toHaveBeenCalledTimes(1);
+ expect(screen.getByRole('button', { name: /connect freighter wallet/i })).toBeInTheDocument();
+ });
});
diff --git a/frontend/components/send-form/steps/connect-step.tsx b/frontend/components/send-form/steps/connect-step.tsx
index 2603ce5b..5eb44d35 100644
--- a/frontend/components/send-form/steps/connect-step.tsx
+++ b/frontend/components/send-form/steps/connect-step.tsx
@@ -8,6 +8,7 @@ import {
clearPersistedWallet,
} from '@/lib/wallet';
import { ChainSelector } from '@/components/chain-selector';
+import { isValidStellarAddress } from '@/lib/validation/stellar-address';
type ConnectStepProps = {
publicKey: string;
@@ -64,6 +65,13 @@ export function ConnectStep({ publicKey, onConnected, extensionSupportedOverride
if (publicKey) return; // parent already has a key — nothing to restore
const saved = loadPersistedWallet();
if (saved?.publicKey) {
+ // Issue #420 — never trust a persisted address blindly; a corrupted
+ // or tampered localStorage value should never silently become the
+ // funding/recovery address for a payment.
+ if (!isValidStellarAddress(saved.publicKey)) {
+ clearPersistedWallet();
+ return;
+ }
onConnected(saved.publicKey);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
@@ -74,6 +82,14 @@ export function ConnectStep({ publicKey, onConnected, extensionSupportedOverride
setError(null);
try {
const wallet = await connectFreighter();
+ // Issue #420 — reject a malformed destination/funding address before
+ // it ever reaches the send form, rather than letting an invalid key
+ // silently flow through to submission.
+ if (!isValidStellarAddress(wallet.publicKey)) {
+ setStatus('error');
+ setError('Freighter returned an address in an unexpected format. Please try reconnecting.');
+ return;
+ }
persistWallet(wallet);
setStatus('idle');
onConnected(wallet.publicKey);
diff --git a/frontend/components/send-form/steps/details-step.test.tsx b/frontend/components/send-form/steps/details-step.test.tsx
new file mode 100644
index 00000000..0736728a
--- /dev/null
+++ b/frontend/components/send-form/steps/details-step.test.tsx
@@ -0,0 +1,150 @@
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { useState } from 'react';
+import { DetailsStep, validateDetails } from '@/components/send-form/steps/details-step';
+import type { SendFormState } from '@/components/send-form';
+
+const getXlmUsdRate = vi.fn();
+
+vi.mock('@/lib/xlm-price', async () => {
+ const actual = await vi.importActual('@/lib/xlm-price');
+ return {
+ ...actual,
+ getXlmUsdRate: () => getXlmUsdRate(),
+ };
+});
+
+const getAccountBalance = vi.fn();
+
+vi.mock('@/lib/wallet-balance', () => ({
+ getAccountBalance: (...args: unknown[]) => getAccountBalance(...args),
+}));
+
+const VALID_PUBLIC_KEY = 'G' + 'A'.repeat(55);
+
+const INITIAL_STATE: SendFormState = {
+ publicKey: 'GABC', // not a valid Stellar address on purpose — see below
+ recipientName: '',
+ recipientEmail: '',
+ amountXlm: '',
+ assetCode: 'XLM',
+ memo: '',
+ expiresIn: 7 * 24 * 60 * 60,
+};
+
+function Harness({
+ onNext = vi.fn(),
+ onBack = vi.fn(),
+ initialState = INITIAL_STATE,
+}: {
+ onNext?: () => void;
+ onBack?: () => void;
+ initialState?: SendFormState;
+}) {
+ const [state, setState] = useState(initialState);
+ return (
+ setState((prev) => ({ ...prev, ...patch }))}
+ onBack={onBack}
+ onNext={onNext}
+ />
+ );
+}
+
+describe('validateDetails — minimum amount (Issue #420)', () => {
+ it('rejects an amount below the minimum for the selected asset', () => {
+ expect(
+ validateDetails({ ...INITIAL_STATE, amountXlm: '0.5', assetCode: 'XLM' }).amountXlm,
+ ).toMatch(/minimum amount/i);
+ });
+
+ it('accepts an amount at or above the minimum', () => {
+ expect(
+ validateDetails({ ...INITIAL_STATE, amountXlm: '1', assetCode: 'XLM' }).amountXlm,
+ ).toBeUndefined();
+ expect(
+ validateDetails({ ...INITIAL_STATE, amountXlm: '10', assetCode: 'XLM' }).amountXlm,
+ ).toBeUndefined();
+ });
+});
+
+describe('DetailsStep — sender balance guard (Issue #420)', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ getXlmUsdRate.mockResolvedValue(0.5);
+ getAccountBalance.mockResolvedValue(null);
+ });
+
+ it('never queries the balance for a placeholder/invalid wallet key', async () => {
+ render( );
+ await waitFor(() => expect(getXlmUsdRate).toHaveBeenCalled());
+ expect(getAccountBalance).not.toHaveBeenCalled();
+ });
+
+ it('blocks submission and shows an inline error when the amount exceeds the wallet balance', async () => {
+ getAccountBalance.mockResolvedValue(5);
+ const onNext = vi.fn();
+ const user = userEvent.setup();
+
+ render(
+ ,
+ );
+
+ await user.type(screen.getByLabelText(/amount/i), '10');
+ await waitFor(() => expect(getAccountBalance).toHaveBeenCalledWith(VALID_PUBLIC_KEY, 'XLM'));
+
+ await user.click(screen.getByRole('button', { name: /review payment/i }));
+
+ expect(onNext).not.toHaveBeenCalled();
+ await waitFor(() =>
+ expect(screen.getByText(/exceeds your wallet balance/i)).toBeInTheDocument(),
+ );
+ });
+
+ it('allows submission when the amount is within the wallet balance', async () => {
+ getAccountBalance.mockResolvedValue(100);
+ const onNext = vi.fn();
+ const user = userEvent.setup();
+
+ render(
+ ,
+ );
+
+ await user.type(screen.getByLabelText(/amount/i), '10');
+ await waitFor(() => expect(getAccountBalance).toHaveBeenCalled());
+
+ await user.click(screen.getByRole('button', { name: /review payment/i }));
+ expect(onNext).toHaveBeenCalledTimes(1);
+ });
+});
+
+describe('DetailsStep — inline validation before submit (Issue #420)', () => {
+ beforeEach(() => {
+ vi.clearAllMocks();
+ getXlmUsdRate.mockResolvedValue(0.5);
+ getAccountBalance.mockResolvedValue(null);
+ });
+
+ it('shows the amount error on blur, before any submit attempt', async () => {
+ const user = userEvent.setup();
+ render( );
+
+ const amountInput = screen.getByLabelText(/amount/i);
+ await user.click(amountInput);
+ await user.tab(); // blur without typing anything
+
+ expect(screen.getByRole('alert')).toHaveTextContent(/enter an amount/i);
+ });
+
+ it('shows the malformed-email error on blur, before any submit attempt', async () => {
+ const user = userEvent.setup();
+ render( );
+
+ await user.type(screen.getByLabelText(/recipient email/i), 'not-an-email');
+ await user.tab();
+
+ expect(screen.getByText(/valid email/i)).toBeInTheDocument();
+ });
+});
diff --git a/frontend/components/send-form/steps/details-step.tsx b/frontend/components/send-form/steps/details-step.tsx
index de710b0d..5a05cb57 100644
--- a/frontend/components/send-form/steps/details-step.tsx
+++ b/frontend/components/send-form/steps/details-step.tsx
@@ -4,11 +4,22 @@ import { useEffect, useState } from 'react';
import type { SendFormState } from '../index';
import { ChainSelector } from '../../chain-selector';
import { getXlmUsdRate, formatFiat } from '@/lib/xlm-price';
+import { isValidStellarAddress } from '@/lib/validation/stellar-address';
+import { getAccountBalance } from '@/lib/wallet-balance';
const SUPPORTED_ASSETS = ['XLM', 'USDC'] as const;
const EMAIL_PATTERN = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
+/**
+ * Issue #420 — Minimum send amount per asset. For XLM this reflects
+ * Stellar's ~1 XLM base account reserve (the ephemeral account being
+ * funded must clear the network's minimum balance to exist at all); the
+ * same floor is applied to other supported assets for a simple, predictable
+ * rule rather than tracking a separate reserve model per asset.
+ */
+const MIN_AMOUNT: Record = { XLM: 1, USDC: 1 };
+
type FieldErrors = {
recipientEmail?: string;
amountXlm?: string;
@@ -27,6 +38,11 @@ export function validateDetails(state: SendFormState): FieldErrors {
errors.amountXlm = 'Enter an amount.';
} else if (amount <= 0) {
errors.amountXlm = 'Amount must be greater than 0.';
+ } else {
+ const min = MIN_AMOUNT[state.assetCode] ?? MIN_AMOUNT['XLM']!;
+ if (amount < min) {
+ errors.amountXlm = `Minimum amount is ${min} ${state.assetCode || 'XLM'}.`;
+ }
}
if (!SUPPORTED_ASSETS.includes(state.assetCode as (typeof SUPPORTED_ASSETS)[number])) {
@@ -49,6 +65,12 @@ export function DetailsStep({ state, onChange, onBack, onNext }: DetailsStepProp
const [touched, setTouched] = useState(false);
const [usdRate, setUsdRate] = useState(null);
+ // Issue #420 — sender balance, used to block amounts above what the
+ // connected wallet actually holds. Only looked up once we have a
+ // well-formed Stellar address (the funding source from ConnectStep) —
+ // never fired for a placeholder/invalid key.
+ const [balance, setBalance] = useState(null);
+
useEffect(() => {
let cancelled = false;
getXlmUsdRate().then((rate) => {
@@ -59,19 +81,43 @@ export function DetailsStep({ state, onChange, onBack, onNext }: DetailsStepProp
};
}, []);
+ useEffect(() => {
+ let cancelled = false;
+ if (!isValidStellarAddress(state.publicKey)) {
+ setBalance(null);
+ return;
+ }
+ getAccountBalance(state.publicKey, state.assetCode).then((b) => {
+ if (!cancelled) setBalance(b);
+ });
+ return () => {
+ cancelled = true;
+ };
+ }, [state.publicKey, state.assetCode]);
+
useEffect(() => {
if (touched) setErrors(validateDetails(state));
}, [state.recipientEmail, state.amountXlm, state.assetCode, touched]);
+ const amount = Number(state.amountXlm);
+ const insufficientBalance =
+ balance !== null && !Number.isNaN(amount) && amount > 0 && amount > balance;
+
+ function markTouched() {
+ if (!touched) {
+ setTouched(true);
+ setErrors(validateDetails(state));
+ }
+ }
+
function handleSubmit(e: React.FormEvent) {
e.preventDefault();
const nextErrors = validateDetails(state);
setErrors(nextErrors);
setTouched(true);
- if (Object.keys(nextErrors).length === 0) onNext();
+ if (Object.keys(nextErrors).length === 0 && !insufficientBalance) onNext();
}
- const amount = Number(state.amountXlm);
const showConversion =
state.assetCode === 'XLM' && usdRate !== null && !Number.isNaN(amount) && amount > 0;
@@ -106,6 +152,7 @@ export function DetailsStep({ state, onChange, onBack, onNext }: DetailsStepProp
type="email"
value={state.recipientEmail}
onChange={(e) => onChange({ recipientEmail: e.target.value })}
+ onBlur={markTouched}
placeholder="recipient@example.com"
aria-invalid={errors.recipientEmail ? true : undefined}
aria-describedby={errors.recipientEmail ? 'recipient-email-error' : undefined}
@@ -135,11 +182,14 @@ export function DetailsStep({ state, onChange, onBack, onNext }: DetailsStepProp
step="any"
value={state.amountXlm}
onChange={(e) => onChange({ amountXlm: e.target.value })}
+ onBlur={markTouched}
placeholder="0.00"
- aria-invalid={errors.amountXlm ? true : undefined}
- aria-describedby={errors.amountXlm ? 'amount-error' : undefined}
+ aria-invalid={errors.amountXlm || insufficientBalance ? true : undefined}
+ aria-describedby={
+ errors.amountXlm ? 'amount-error' : insufficientBalance ? 'amount-balance-error' : undefined
+ }
className={`block w-full rounded-lg border px-3 py-2 text-sm text-slate-900 placeholder-slate-400 focus:outline-none focus:ring-1 dark:bg-slate-800 dark:text-slate-100 dark:placeholder-slate-500 ${
- errors.amountXlm
+ errors.amountXlm || insufficientBalance
? 'border-red-400 focus:border-red-500 focus:ring-red-500 dark:border-red-600'
: 'border-slate-300 focus:border-slate-500 focus:ring-slate-500 dark:border-slate-600 dark:focus:border-slate-400 dark:focus:ring-slate-400'
}`}
@@ -165,6 +215,16 @@ export function DetailsStep({ state, onChange, onBack, onNext }: DetailsStepProp
{errors.amountXlm}
)}
+ {/* Issue #420 — amount above sender balance blocked client-side */}
+ {!errors.amountXlm && insufficientBalance && (
+
+ Amount exceeds your wallet balance of {balance} {state.assetCode}.
+
+ )}
{errors.assetCode && (
{errors.assetCode}
diff --git a/frontend/components/wallet-address-input.tsx b/frontend/components/wallet-address-input.tsx
index a1a655a6..ed93de75 100644
--- a/frontend/components/wallet-address-input.tsx
+++ b/frontend/components/wallet-address-input.tsx
@@ -1,8 +1,10 @@
'use client';
import { useState } from 'react';
-
-const STELLAR_ADDRESS = /^G[A-Z2-7]{55}$/;
+import {
+ STELLAR_ADDRESS_PATTERN as STELLAR_ADDRESS,
+ STELLAR_ADDRESS_ERROR,
+} from '@/lib/validation/stellar-address';
type WalletAddressInputProps = {
value: string;
@@ -26,9 +28,7 @@ export function WalletAddressInput({
const [touched, setTouched] = useState(false);
const validationError =
- touched && value.length > 0 && !STELLAR_ADDRESS.test(value)
- ? 'Enter a valid Stellar public key (starts with G, 56 characters).'
- : null;
+ touched && value.length > 0 && !STELLAR_ADDRESS.test(value) ? STELLAR_ADDRESS_ERROR : null;
const displayError = error ?? validationError;
const inputId = 'wallet-address-input';
diff --git a/frontend/lib/create-bridgelet-client.ts b/frontend/lib/create-bridgelet-client.ts
index 973298a5..8952310f 100644
--- a/frontend/lib/create-bridgelet-client.ts
+++ b/frontend/lib/create-bridgelet-client.ts
@@ -30,7 +30,7 @@ export class RateLimitError extends Error {
constructor(retryAfter: number | null) {
super(
retryAfter != null
- ? \`Please wait \${retryAfter} second\${retryAfter !== 1 ? 's' : ''} before retrying.\`
+ ? `Please wait ${retryAfter} second${retryAfter !== 1 ? 's' : ''} before retrying.`
: 'Too many requests. Please wait a moment before retrying.',
);
this.name = 'RateLimitError';
@@ -49,10 +49,10 @@ export class BridgeletApiError extends Error {
if (parsed.error && typeof parsed.error === 'object') {
const nested = parsed.error as Record;
- message = typeof nested.message === 'string' ? nested.message : \`Request failed with status \${statusCode}.\`;
+ message = typeof nested.message === 'string' ? nested.message : `Request failed with status ${statusCode}.`;
errorCode = typeof nested.code === 'string' ? nested.code : undefined;
} else {
- message = typeof parsed.message === 'string' ? parsed.message : \`Request failed with status \${statusCode}.\`;
+ message = typeof parsed.message === 'string' ? parsed.message : `Request failed with status ${statusCode}.`;
errorCode = typeof parsed.error === 'string' ? parsed.error : undefined;
}
@@ -105,7 +105,7 @@ export class BridgeletClient {
init = await this.requestInterceptor(url, init);
}
- const requestId = \`\${options.method ?? 'GET'}:\${url}\`;
+ const requestId = `${options.method ?? 'GET'}:${url}`;
const controller = new AbortController();
this.inflightControllers.set(requestId, controller);
init.signal = controller.signal;
@@ -163,7 +163,7 @@ export class BridgeletClient {
}
createAccount(data: CreateAccountRequest): Promise {
- return this.request(\`\${this.internalBaseUrl}/api/accounts\`, {
+ return this.request(`${this.internalBaseUrl}/api/accounts`, {
method: 'POST',
body: JSON.stringify(data),
});
@@ -171,20 +171,20 @@ export class BridgeletClient {
prepareAccountTransaction(data: CreateAccountRequest): Promise {
return this.request(
- \`\${this.internalBaseUrl}/api/accounts/prepare\`,
+ `${this.internalBaseUrl}/api/accounts/prepare`,
{ method: 'POST', body: JSON.stringify(data) },
);
}
getAccount(accountId: string): Promise {
return this.request(
- \`\${this.internalBaseUrl}/api/accounts/\${encodeURIComponent(accountId)}\`,
+ `${this.internalBaseUrl}/api/accounts/${encodeURIComponent(accountId)}`,
);
}
redeemClaim(claimToken: string, destinationAddress: string): Promise {
const body: RedeemClaimRequest = { claimToken, destinationAddress };
- return this.request(\`\${this.baseUrl}/claims/redeem\`, {
+ return this.request(`${this.baseUrl}/claims/redeem`, {
method: 'POST',
body: JSON.stringify(body),
});
@@ -192,7 +192,7 @@ export class BridgeletClient {
verifyClaim(claimToken: string): Promise {
const body: VerifyClaimRequest = { claimToken };
- return this.request(\`\${this.baseUrl}/claims/verify\`, {
+ return this.request(`${this.baseUrl}/claims/verify`, {
method: 'POST',
body: JSON.stringify(body),
});
@@ -207,7 +207,7 @@ export class BridgeletClient {
async healthCheck(): Promise {
try {
- const response = await fetchWithTimeout(\`\${this.baseUrl}/health\`, { method: 'GET' }, 5000);
+ const response = await fetchWithTimeout(`${this.baseUrl}/health`, { method: 'GET' }, 5000);
return response.ok;
} catch {
return false;
diff --git a/frontend/lib/validation/stellar-address.test.ts b/frontend/lib/validation/stellar-address.test.ts
new file mode 100644
index 00000000..2cfb776e
--- /dev/null
+++ b/frontend/lib/validation/stellar-address.test.ts
@@ -0,0 +1,45 @@
+import { describe, it, expect } from 'vitest';
+import {
+ isValidStellarAddress,
+ STELLAR_ADDRESS_PATTERN,
+ STELLAR_ADDRESS_ERROR,
+} from './stellar-address';
+
+describe('isValidStellarAddress (Issue #420)', () => {
+ it('accepts a well-formed 56-character Stellar public key starting with G', () => {
+ expect(isValidStellarAddress('G' + 'A'.repeat(55))).toBe(true);
+ expect(isValidStellarAddress('G' + 'Z'.repeat(55))).toBe(true);
+ expect(isValidStellarAddress('G' + '2'.repeat(55))).toBe(true);
+ expect(isValidStellarAddress('G' + '7'.repeat(55))).toBe(true);
+ });
+
+ it('rejects an address not starting with G', () => {
+ expect(isValidStellarAddress('S' + 'A'.repeat(55))).toBe(false);
+ });
+
+ it('rejects addresses of the wrong length', () => {
+ expect(isValidStellarAddress('G' + 'A'.repeat(54))).toBe(false); // too short
+ expect(isValidStellarAddress('G' + 'A'.repeat(56))).toBe(false); // too long
+ expect(isValidStellarAddress('')).toBe(false);
+ });
+
+ it('rejects characters outside the base32 alphabet (0, 1, 8, 9 are not valid)', () => {
+ expect(isValidStellarAddress('G' + '0'.repeat(55))).toBe(false);
+ expect(isValidStellarAddress('G' + '1'.repeat(55))).toBe(false);
+ expect(isValidStellarAddress('G' + '8'.repeat(55))).toBe(false);
+ expect(isValidStellarAddress('G' + '9'.repeat(55))).toBe(false);
+ });
+
+ it('rejects lowercase addresses', () => {
+ expect(isValidStellarAddress('g' + 'a'.repeat(55))).toBe(false);
+ });
+
+ it('trims surrounding whitespace before validating', () => {
+ expect(isValidStellarAddress(` ${'G' + 'A'.repeat(55)} `)).toBe(true);
+ });
+
+ it('exposes a matching regex and a human-readable error message', () => {
+ expect(STELLAR_ADDRESS_PATTERN.test('G' + 'A'.repeat(55))).toBe(true);
+ expect(STELLAR_ADDRESS_ERROR).toMatch(/starts with g/i);
+ });
+});
diff --git a/frontend/lib/validation/stellar-address.ts b/frontend/lib/validation/stellar-address.ts
new file mode 100644
index 00000000..94672303
--- /dev/null
+++ b/frontend/lib/validation/stellar-address.ts
@@ -0,0 +1,25 @@
+/**
+ * Issue #420 — Shared Stellar public-key (address) format validation.
+ *
+ * Stellar public keys are StrKey-encoded ed25519 public keys: base32
+ * (RFC 4648, alphabet A-Z2-7), always 56 characters, always starting with
+ * `G`. This mirrors the regex that already existed in
+ * `components/wallet-address-input.tsx` — extracted here so every place
+ * that needs to validate a destination/funding address (the send form,
+ * the wallet address input, etc.) shares one definition instead of each
+ * hand-rolling its own.
+ *
+ * This is a format check only (correct shape), not a checksum/CRC
+ * validation of the StrKey encoding — `@stellar/stellar-sdk`'s
+ * `StrKey.isValidEd25519PublicKey` is available for a full checksum
+ * check where it matters (e.g. before signing), but the lightweight regex
+ * is sufficient — and synchronous/dependency-free — for inline form
+ * validation.
+ */
+export const STELLAR_ADDRESS_PATTERN = /^G[A-Z2-7]{55}$/;
+
+export const STELLAR_ADDRESS_ERROR = 'Enter a valid Stellar public key (starts with G, 56 characters).';
+
+export function isValidStellarAddress(value: string): boolean {
+ return STELLAR_ADDRESS_PATTERN.test(value.trim());
+}
diff --git a/frontend/lib/wallet-balance.test.ts b/frontend/lib/wallet-balance.test.ts
new file mode 100644
index 00000000..af4c2438
--- /dev/null
+++ b/frontend/lib/wallet-balance.test.ts
@@ -0,0 +1,87 @@
+import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
+import { getAccountBalance, clearBalanceCache } from './wallet-balance';
+
+const VALID_PUBLIC_KEY = 'G' + 'A'.repeat(55);
+
+describe('getAccountBalance (Issue #420)', () => {
+ beforeEach(() => {
+ clearBalanceCache();
+ vi.stubGlobal('fetch', vi.fn());
+ });
+
+ afterEach(() => {
+ vi.unstubAllGlobals();
+ });
+
+ it('returns null for an empty public key without calling fetch', async () => {
+ const balance = await getAccountBalance('', 'XLM');
+ expect(balance).toBeNull();
+ expect(fetch).not.toHaveBeenCalled();
+ });
+
+ it('returns the native balance for XLM', async () => {
+ vi.mocked(fetch).mockResolvedValue({
+ ok: true,
+ json: async () => ({
+ balances: [
+ { asset_type: 'native', balance: '42.5000000' },
+ { asset_type: 'credit_alphanum4', asset_code: 'USDC', balance: '10.0000000' },
+ ],
+ }),
+ } as Response);
+
+ const balance = await getAccountBalance(VALID_PUBLIC_KEY, 'XLM');
+ expect(balance).toBe(42.5);
+ });
+
+ it('returns the matching asset_code balance for non-XLM assets', async () => {
+ vi.mocked(fetch).mockResolvedValue({
+ ok: true,
+ json: async () => ({
+ balances: [
+ { asset_type: 'native', balance: '5.0000000' },
+ { asset_type: 'credit_alphanum4', asset_code: 'USDC', balance: '99.0000000' },
+ ],
+ }),
+ } as Response);
+
+ const balance = await getAccountBalance(VALID_PUBLIC_KEY, 'USDC');
+ expect(balance).toBe(99);
+ });
+
+ it('returns 0 when the account exists but holds no line of the requested asset', async () => {
+ vi.mocked(fetch).mockResolvedValue({
+ ok: true,
+ json: async () => ({ balances: [{ asset_type: 'native', balance: '5.0000000' }] }),
+ } as Response);
+
+ const balance = await getAccountBalance(VALID_PUBLIC_KEY, 'USDC');
+ expect(balance).toBe(0);
+ });
+
+ it('returns null (fails open) when the account is not found', async () => {
+ vi.mocked(fetch).mockResolvedValue({ ok: false, status: 404 } as Response);
+
+ const balance = await getAccountBalance(VALID_PUBLIC_KEY, 'XLM');
+ expect(balance).toBeNull();
+ });
+
+ it('returns null (fails open) when the network request throws', async () => {
+ vi.mocked(fetch).mockRejectedValue(new TypeError('network error'));
+
+ const balance = await getAccountBalance(VALID_PUBLIC_KEY, 'XLM');
+ expect(balance).toBeNull();
+ });
+
+ it('caches the account response for repeated lookups', async () => {
+ vi.mocked(fetch).mockResolvedValue({
+ ok: true,
+ json: async () => ({ balances: [{ asset_type: 'native', balance: '5.0000000' }] }),
+ } as Response);
+
+ await getAccountBalance(VALID_PUBLIC_KEY, 'XLM');
+ await getAccountBalance(VALID_PUBLIC_KEY, 'XLM');
+
+ expect(fetch).toHaveBeenCalledTimes(1);
+ });
+});
diff --git a/frontend/lib/wallet-balance.ts b/frontend/lib/wallet-balance.ts
new file mode 100644
index 00000000..96c6bba6
--- /dev/null
+++ b/frontend/lib/wallet-balance.ts
@@ -0,0 +1,74 @@
+/**
+ * Issue #420 — Sender wallet balance lookup, used to block sends that
+ * exceed the connected wallet's balance before they ever reach the API.
+ *
+ * Follows the same pattern as `lib/fee-estimation.ts`: a direct, cached,
+ * client-side fetch against Horizon. Failures are swallowed and surfaced
+ * as `null` rather than thrown — an unknown balance should never block a
+ * send outright (the backend still enforces the real balance check), it
+ * should just skip the client-side "insufficient balance" hint.
+ */
+
+const HORIZON_BASE_URL =
+ process.env['NEXT_PUBLIC_HORIZON_URL'] ?? 'https://horizon-testnet.stellar.org';
+
+const CACHE_MS = 15_000;
+
+interface HorizonBalanceLine {
+ asset_type: string;
+ asset_code?: string;
+ balance: string;
+}
+
+interface HorizonAccountResponse {
+ balances: HorizonBalanceLine[];
+}
+
+const balanceCache = new Map();
+
+async function fetchAccount(publicKey: string): Promise {
+ const cached = balanceCache.get(publicKey);
+ if (cached && Date.now() - cached.at < CACHE_MS) {
+ return cached.data;
+ }
+ try {
+ const res = await fetch(`${HORIZON_BASE_URL}/accounts/${encodeURIComponent(publicKey)}`, {
+ cache: 'no-store',
+ });
+ if (!res.ok) return null;
+ const data = (await res.json()) as HorizonAccountResponse;
+ balanceCache.set(publicKey, { data, at: Date.now() });
+ return data;
+ } catch {
+ return null;
+ }
+}
+
+/**
+ * Returns the available balance of `assetCode` for `publicKey`, or `null`
+ * when it can't be determined (account not found/funded yet, network
+ * error, or Horizon unreachable). `assetCode` of `'XLM'` looks up the
+ * native balance line; anything else matches on `asset_code`.
+ */
+export async function getAccountBalance(
+ publicKey: string,
+ assetCode: string,
+): Promise {
+ if (!publicKey) return null;
+ const account = await fetchAccount(publicKey);
+ if (!account) return null;
+
+ const line =
+ assetCode === 'XLM'
+ ? account.balances.find((b) => b.asset_type === 'native')
+ : account.balances.find((b) => b.asset_code === assetCode);
+
+ if (!line) return 0;
+ const parsed = parseFloat(line.balance);
+ return Number.isFinite(parsed) ? parsed : null;
+}
+
+/** Clears the balance cache — mainly useful for tests. */
+export function clearBalanceCache(): void {
+ balanceCache.clear();
+}
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 58b7697f..c26f91ad 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -16,9 +16,11 @@
"resend": "^6.16.0"
},
"devDependencies": {
+ "@chromatic-com/storybook": "^3.2.0",
"@lhci/cli": "^0.15.1",
"@storybook/nextjs-vite": "^10.4.6",
"@tailwindcss/postcss": "^4.0.0",
+ "@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^14.5.2",
@@ -27,6 +29,7 @@
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.3.4",
+ "axe-core": "^4.10.0",
"eslint": "^9.39.5",
"eslint-config-next": "^16.2.12",
"jsdom": "^25.0.1",
@@ -395,6 +398,56 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@chromatic-com/storybook": {
+ "version": "3.2.7",
+ "resolved": "https://registry.npmjs.org/@chromatic-com/storybook/-/storybook-3.2.7.tgz",
+ "integrity": "sha512-fCGhk4cd3VA8RNg55MZL5CScdHqljsQcL9g6Ss7YuobHpSo9yytEWNdgMd5QxAHSPBlLGFHjnSmliM3G/BeBqw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "chromatic": "^11.15.0",
+ "filesize": "^10.0.12",
+ "jsonfile": "^6.1.0",
+ "react-confetti": "^6.1.0",
+ "strip-ansi": "^7.1.0"
+ },
+ "engines": {
+ "node": ">=16.0.0",
+ "yarn": ">=1.22.18"
+ },
+ "peerDependencies": {
+ "storybook": "^8.2.0 || ^8.3.0-0 || ^8.4.0-0 || ^8.5.0-0 || ^8.6.0-0"
+ }
+ },
+ "node_modules/@chromatic-com/storybook/node_modules/ansi-regex": {
+ "version": "6.3.0",
+ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.3.0.tgz",
+ "integrity": "sha512-WpDfL7NO6j7tH88IDBNVdUJxDh9nmCteAVW9dsep846XdwF4naCBK+/tGLX3KJgcpgMRXCFlTM2hKGoK9FsdrQ==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+ }
+ },
+ "node_modules/@chromatic-com/storybook/node_modules/strip-ansi": {
+ "version": "7.2.0",
+ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz",
+ "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "ansi-regex": "^6.2.2"
+ },
+ "engines": {
+ "node": ">=12"
+ },
+ "funding": {
+ "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+ }
+ },
"node_modules/@csstools/color-helpers": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz",
@@ -543,938 +596,470 @@
"tslib": "^2.4.0"
}
},
- "node_modules/@esbuild/aix-ppc64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.28.1.tgz",
- "integrity": "sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==",
- "cpu": [
- "ppc64"
- ],
+ "node_modules/@eslint-community/eslint-utils": {
+ "version": "4.10.1",
+ "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz",
+ "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "aix"
- ],
- "peer": true,
+ "dependencies": {
+ "eslint-visitor-keys": "^3.4.3"
+ },
"engines": {
- "node": ">=18"
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
}
},
- "node_modules/@esbuild/android-arm": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.28.1.tgz",
- "integrity": "sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==",
- "cpu": [
- "arm"
- ],
+ "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
+ "version": "3.4.3",
+ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+ "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "peer": true,
+ "license": "Apache-2.0",
"engines": {
- "node": ">=18"
+ "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/@esbuild/android-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.28.1.tgz",
- "integrity": "sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@eslint-community/regexpp": {
+ "version": "4.12.2",
+ "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
+ "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "peer": true,
"engines": {
- "node": ">=18"
+ "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
}
},
- "node_modules/@esbuild/android-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.28.1.tgz",
- "integrity": "sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@eslint/config-array": {
+ "version": "0.21.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
+ "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "peer": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/object-schema": "^2.1.7",
+ "debug": "^4.3.1",
+ "minimatch": "^3.1.5"
+ },
"engines": {
- "node": ">=18"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "node_modules/@esbuild/darwin-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.28.1.tgz",
- "integrity": "sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@eslint/config-array/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
+ "license": "MIT"
},
- "node_modules/@esbuild/darwin-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.28.1.tgz",
- "integrity": "sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@eslint/config-array/node_modules/brace-expansion": {
+ "version": "1.1.16",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
+ "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
}
},
- "node_modules/@esbuild/freebsd-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.28.1.tgz",
- "integrity": "sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@eslint/config-array/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "peer": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
"engines": {
- "node": ">=18"
+ "node": "*"
}
},
- "node_modules/@esbuild/freebsd-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.28.1.tgz",
- "integrity": "sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@eslint/config-helpers": {
+ "version": "0.4.2",
+ "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
+ "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "peer": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0"
+ },
"engines": {
- "node": ">=18"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "node_modules/@esbuild/linux-arm": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.28.1.tgz",
- "integrity": "sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==",
- "cpu": [
- "arm"
- ],
+ "node_modules/@eslint/core": {
+ "version": "0.17.0",
+ "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
+ "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@types/json-schema": "^7.0.15"
+ },
"engines": {
- "node": ">=18"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "node_modules/@esbuild/linux-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.28.1.tgz",
- "integrity": "sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@eslint/eslintrc": {
+ "version": "3.3.6",
+ "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz",
+ "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
+ "dependencies": {
+ "ajv": "^6.14.0",
+ "debug": "^4.3.2",
+ "espree": "^10.0.1",
+ "globals": "^14.0.0",
+ "ignore": "^5.2.0",
+ "import-fresh": "^3.2.1",
+ "js-yaml": "^4.3.0",
+ "minimatch": "^3.1.5",
+ "strip-json-comments": "^3.1.1"
+ },
"engines": {
- "node": ">=18"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/eslint"
}
},
- "node_modules/@esbuild/linux-ia32": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.28.1.tgz",
- "integrity": "sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==",
- "cpu": [
- "ia32"
- ],
+ "node_modules/@eslint/eslintrc/node_modules/balanced-match": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
+ "version": "1.1.16",
+ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
+ "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "balanced-match": "^1.0.0",
+ "concat-map": "0.0.1"
}
},
- "node_modules/@esbuild/linux-loong64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.28.1.tgz",
- "integrity": "sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==",
- "cpu": [
- "loong64"
- ],
+ "node_modules/@eslint/eslintrc/node_modules/globals": {
+ "version": "14.0.0",
+ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
+ "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
"engines": {
"node": ">=18"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@esbuild/linux-mips64el": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.28.1.tgz",
- "integrity": "sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==",
- "cpu": [
- "mips64el"
- ],
+ "node_modules/@eslint/eslintrc/node_modules/js-yaml": {
+ "version": "4.3.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
+ "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@esbuild/linux-ppc64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.28.1.tgz",
- "integrity": "sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==",
- "cpu": [
- "ppc64"
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/puzrin"
+ },
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/nodeca"
+ }
],
- "dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "argparse": "^2.0.1"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
}
},
- "node_modules/@esbuild/linux-riscv64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.28.1.tgz",
- "integrity": "sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==",
- "cpu": [
- "riscv64"
- ],
+ "node_modules/@eslint/eslintrc/node_modules/minimatch": {
+ "version": "3.1.5",
+ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
+ "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
+ "license": "ISC",
+ "dependencies": {
+ "brace-expansion": "^1.1.7"
+ },
"engines": {
- "node": ">=18"
+ "node": "*"
}
},
- "node_modules/@esbuild/linux-s390x": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.28.1.tgz",
- "integrity": "sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==",
- "cpu": [
- "s390x"
- ],
+ "node_modules/@eslint/js": {
+ "version": "9.39.5",
+ "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz",
+ "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
"engines": {
- "node": ">=18"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "url": "https://eslint.org/donate"
}
},
- "node_modules/@esbuild/linux-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.28.1.tgz",
- "integrity": "sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@eslint/object-schema": {
+ "version": "2.1.7",
+ "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
+ "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
+ "license": "Apache-2.0",
"engines": {
- "node": ">=18"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "node_modules/@esbuild/netbsd-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.28.1.tgz",
- "integrity": "sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@eslint/plugin-kit": {
+ "version": "0.4.1",
+ "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
+ "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "peer": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@eslint/core": "^0.17.0",
+ "levn": "^0.4.1"
+ },
"engines": {
- "node": ">=18"
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
}
},
- "node_modules/@esbuild/netbsd-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.28.1.tgz",
- "integrity": "sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@formatjs/ecma402-abstract": {
+ "version": "2.3.6",
+ "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz",
+ "integrity": "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "netbsd"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "@formatjs/fast-memoize": "2.2.7",
+ "@formatjs/intl-localematcher": "0.6.2",
+ "decimal.js": "^10.4.3",
+ "tslib": "^2.8.0"
}
},
- "node_modules/@esbuild/openbsd-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.28.1.tgz",
- "integrity": "sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@formatjs/fast-memoize": {
+ "version": "2.2.7",
+ "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz",
+ "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "tslib": "^2.8.0"
}
},
- "node_modules/@esbuild/openbsd-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.28.1.tgz",
- "integrity": "sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@formatjs/icu-messageformat-parser": {
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz",
+ "integrity": "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "openbsd"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "2.3.6",
+ "@formatjs/icu-skeleton-parser": "1.8.16",
+ "tslib": "^2.8.0"
}
},
- "node_modules/@esbuild/openharmony-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.28.1.tgz",
- "integrity": "sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@formatjs/icu-skeleton-parser": {
+ "version": "1.8.16",
+ "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz",
+ "integrity": "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "@formatjs/ecma402-abstract": "2.3.6",
+ "tslib": "^2.8.0"
}
},
- "node_modules/@esbuild/sunos-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.28.1.tgz",
- "integrity": "sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@formatjs/intl-localematcher": {
+ "version": "0.6.2",
+ "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz",
+ "integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "sunos"
- ],
- "peer": true,
- "engines": {
- "node": ">=18"
+ "dependencies": {
+ "tslib": "^2.8.0"
}
},
- "node_modules/@esbuild/win32-arm64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.28.1.tgz",
- "integrity": "sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@humanfs/core": {
+ "version": "0.19.2",
+ "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
+ "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/types": "^0.15.0"
+ },
"engines": {
- "node": ">=18"
+ "node": ">=18.18.0"
}
},
- "node_modules/@esbuild/win32-ia32": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.28.1.tgz",
- "integrity": "sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==",
- "cpu": [
- "ia32"
- ],
+ "node_modules/@humanfs/node": {
+ "version": "0.16.8",
+ "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
+ "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@humanfs/core": "^0.19.2",
+ "@humanfs/types": "^0.15.0",
+ "@humanwhocodes/retry": "^0.4.0"
+ },
"engines": {
- "node": ">=18"
+ "node": ">=18.18.0"
}
},
- "node_modules/@esbuild/win32-x64": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.28.1.tgz",
- "integrity": "sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@humanfs/types": {
+ "version": "0.15.0",
+ "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
+ "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
+ "license": "Apache-2.0",
"engines": {
- "node": ">=18"
+ "node": ">=18.18.0"
}
},
- "node_modules/@eslint-community/eslint-utils": {
- "version": "4.10.1",
- "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.10.1.tgz",
- "integrity": "sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==",
+ "node_modules/@humanwhocodes/module-importer": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+ "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
"dev": true,
- "license": "MIT",
- "dependencies": {
- "eslint-visitor-keys": "^3.4.3"
- },
+ "license": "Apache-2.0",
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": ">=12.22"
},
"funding": {
- "url": "https://opencollective.com/eslint"
- },
- "peerDependencies": {
- "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0"
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
}
},
- "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": {
- "version": "3.4.3",
- "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
- "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+ "node_modules/@humanwhocodes/retry": {
+ "version": "0.4.3",
+ "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
+ "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
"dev": true,
"license": "Apache-2.0",
"engines": {
- "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
+ "node": ">=18.18"
},
"funding": {
- "url": "https://opencollective.com/eslint"
+ "type": "github",
+ "url": "https://github.com/sponsors/nzakas"
}
},
- "node_modules/@eslint-community/regexpp": {
- "version": "4.12.2",
- "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz",
- "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==",
- "dev": true,
+ "node_modules/@img/colour": {
+ "version": "1.1.0",
+ "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
+ "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
"license": "MIT",
+ "optional": true,
"engines": {
- "node": "^12.0.0 || ^14.0.0 || >=16.0.0"
+ "node": ">=18"
}
},
- "node_modules/@eslint/config-array": {
- "version": "0.21.2",
- "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz",
- "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==",
- "dev": true,
+ "node_modules/@img/sharp-darwin-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
+ "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
+ "cpu": [
+ "arm64"
+ ],
"license": "Apache-2.0",
- "dependencies": {
- "@eslint/object-schema": "^2.1.7",
- "debug": "^4.3.1",
- "minimatch": "^3.1.5"
- },
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/config-array/node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@eslint/config-array/node_modules/brace-expansion": {
- "version": "1.1.16",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
- "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@eslint/config-array/node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
- "engines": {
- "node": "*"
- }
- },
- "node_modules/@eslint/config-helpers": {
- "version": "0.4.2",
- "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz",
- "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@eslint/core": "^0.17.0"
+ "funding": {
+ "url": "https://opencollective.com/libvips"
},
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-arm64": "1.2.4"
}
},
- "node_modules/@eslint/core": {
- "version": "0.17.0",
- "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz",
- "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==",
- "dev": true,
+ "node_modules/@img/sharp-darwin-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
+ "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
+ "cpu": [
+ "x64"
+ ],
"license": "Apache-2.0",
- "dependencies": {
- "@types/json-schema": "^7.0.15"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/eslintrc": {
- "version": "3.3.6",
- "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.6.tgz",
- "integrity": "sha512-l2Ul9PrHsPCKcEY/ac7VgFj9D80C7S68sOKc618SyHDPK36s1XcFebXY0iTzUVn4Yq+YbwvSnDmCz9yxjX+QrA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ajv": "^6.14.0",
- "debug": "^4.3.2",
- "espree": "^10.0.1",
- "globals": "^14.0.0",
- "ignore": "^5.2.0",
- "import-fresh": "^3.2.1",
- "js-yaml": "^4.3.0",
- "minimatch": "^3.1.5",
- "strip-json-comments": "^3.1.1"
- },
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
"engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
- "url": "https://opencollective.com/eslint"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/balanced-match": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
- "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@eslint/eslintrc/node_modules/brace-expansion": {
- "version": "1.1.16",
- "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.16.tgz",
- "integrity": "sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "balanced-match": "^1.0.0",
- "concat-map": "0.0.1"
- }
- },
- "node_modules/@eslint/eslintrc/node_modules/globals": {
- "version": "14.0.0",
- "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz",
- "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=18"
+ "url": "https://opencollective.com/libvips"
},
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "optionalDependencies": {
+ "@img/sharp-libvips-darwin-x64": "1.2.4"
}
},
- "node_modules/@eslint/eslintrc/node_modules/js-yaml": {
- "version": "4.3.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.0.tgz",
- "integrity": "sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==",
- "dev": true,
- "funding": [
- {
- "type": "github",
- "url": "https://github.com/sponsors/puzrin"
- },
- {
- "type": "github",
- "url": "https://github.com/sponsors/nodeca"
- }
+ "node_modules/@img/sharp-libvips-darwin-arm64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
+ "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
+ "cpu": [
+ "arm64"
],
- "license": "MIT",
- "dependencies": {
- "argparse": "^2.0.1"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
}
},
- "node_modules/@eslint/eslintrc/node_modules/minimatch": {
- "version": "3.1.5",
- "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz",
- "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "brace-expansion": "^1.1.7"
- },
- "engines": {
- "node": "*"
+ "node_modules/@img/sharp-libvips-darwin-x64": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
+ "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
+ "cpu": [
+ "x64"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "funding": {
+ "url": "https://opencollective.com/libvips"
}
},
- "node_modules/@eslint/js": {
- "version": "9.39.5",
- "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.5.tgz",
- "integrity": "sha512-QywQuszQh77pIXCsq998c8hbhSTI/azTty1Z6N53dmAudKHhy573j3yvRLsX2BSp8YpLtoCEG8E9DJe+8zUh4A==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
+ "node_modules/@img/sharp-libvips-linux-arm": {
+ "version": "1.2.4",
+ "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
+ "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
+ "cpu": [
+ "arm"
+ ],
+ "license": "LGPL-3.0-or-later",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
"funding": {
- "url": "https://eslint.org/donate"
- }
- },
- "node_modules/@eslint/object-schema": {
- "version": "2.1.7",
- "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz",
- "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@eslint/plugin-kit": {
- "version": "0.4.1",
- "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz",
- "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@eslint/core": "^0.17.0",
- "levn": "^0.4.1"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- }
- },
- "node_modules/@formatjs/ecma402-abstract": {
- "version": "2.3.6",
- "resolved": "https://registry.npmjs.org/@formatjs/ecma402-abstract/-/ecma402-abstract-2.3.6.tgz",
- "integrity": "sha512-HJnTFeRM2kVFVr5gr5kH1XP6K0JcJtE7Lzvtr3FS/so5f1kpsqqqxy5JF+FRaO6H2qmcMfAUIox7AJteieRtVw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@formatjs/fast-memoize": "2.2.7",
- "@formatjs/intl-localematcher": "0.6.2",
- "decimal.js": "^10.4.3",
- "tslib": "^2.8.0"
- }
- },
- "node_modules/@formatjs/fast-memoize": {
- "version": "2.2.7",
- "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-2.2.7.tgz",
- "integrity": "sha512-Yabmi9nSvyOMrlSeGGWDiH7rf3a7sIwplbvo/dlz9WCIjzIQAfy1RMf4S0X3yG724n5Ghu2GmEl5NJIV6O9sZQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.8.0"
- }
- },
- "node_modules/@formatjs/icu-messageformat-parser": {
- "version": "2.11.4",
- "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-2.11.4.tgz",
- "integrity": "sha512-7kR78cRrPNB4fjGFZg3Rmj5aah8rQj9KPzuLsmcSn4ipLXQvC04keycTI1F7kJYDwIXtT2+7IDEto842CfZBtw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@formatjs/ecma402-abstract": "2.3.6",
- "@formatjs/icu-skeleton-parser": "1.8.16",
- "tslib": "^2.8.0"
- }
- },
- "node_modules/@formatjs/icu-skeleton-parser": {
- "version": "1.8.16",
- "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-1.8.16.tgz",
- "integrity": "sha512-H13E9Xl+PxBd8D5/6TVUluSpxGNvFSlN/b3coUp0e0JpuWXXnQDiavIpY3NnvSp4xhEMoXyyBvVfdFX8jglOHQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@formatjs/ecma402-abstract": "2.3.6",
- "tslib": "^2.8.0"
- }
- },
- "node_modules/@formatjs/intl-localematcher": {
- "version": "0.6.2",
- "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.6.2.tgz",
- "integrity": "sha512-XOMO2Hupl0wdd172Y06h6kLpBz6Dv+J4okPLl4LPtzbr8f66WbIoy4ev98EBuZ6ZK4h5ydTN6XneT4QVpD7cdA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "tslib": "^2.8.0"
- }
- },
- "node_modules/@humanfs/core": {
- "version": "0.19.2",
- "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.2.tgz",
- "integrity": "sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@humanfs/types": "^0.15.0"
- },
- "engines": {
- "node": ">=18.18.0"
- }
- },
- "node_modules/@humanfs/node": {
- "version": "0.16.8",
- "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.8.tgz",
- "integrity": "sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@humanfs/core": "^0.19.2",
- "@humanfs/types": "^0.15.0",
- "@humanwhocodes/retry": "^0.4.0"
- },
- "engines": {
- "node": ">=18.18.0"
- }
- },
- "node_modules/@humanfs/types": {
- "version": "0.15.0",
- "resolved": "https://registry.npmjs.org/@humanfs/types/-/types-0.15.0.tgz",
- "integrity": "sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=18.18.0"
- }
- },
- "node_modules/@humanwhocodes/module-importer": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
- "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=12.22"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
- }
- },
- "node_modules/@humanwhocodes/retry": {
- "version": "0.4.3",
- "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz",
- "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==",
- "dev": true,
- "license": "Apache-2.0",
- "engines": {
- "node": ">=18.18"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/nzakas"
- }
- },
- "node_modules/@img/colour": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz",
- "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==",
- "license": "MIT",
- "optional": true,
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@img/sharp-darwin-arm64": {
- "version": "0.34.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz",
- "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==",
- "cpu": [
- "arm64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-darwin-arm64": "1.2.4"
- }
- },
- "node_modules/@img/sharp-darwin-x64": {
- "version": "0.34.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz",
- "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-darwin-x64": "1.2.4"
- }
- },
- "node_modules/@img/sharp-libvips-darwin-arm64": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz",
- "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==",
- "cpu": [
- "arm64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "darwin"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-darwin-x64": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz",
- "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==",
- "cpu": [
- "x64"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "darwin"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-libvips-linux-arm": {
- "version": "1.2.4",
- "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz",
- "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==",
- "cpu": [
- "arm"
- ],
- "license": "LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "linux"
- ],
- "funding": {
- "url": "https://opencollective.com/libvips"
+ "url": "https://opencollective.com/libvips"
}
},
"node_modules/@img/sharp-libvips-linux-arm64": {
@@ -1698,1559 +1283,859 @@
"optionalDependencies": {
"@img/sharp-libvips-linux-s390x": "1.2.4"
}
- },
- "node_modules/@img/sharp-linux-x64": {
- "version": "0.34.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
- "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linux-x64": "1.2.4"
- }
- },
- "node_modules/@img/sharp-linuxmusl-arm64": {
- "version": "0.34.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
- "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
- "cpu": [
- "arm64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
- }
- },
- "node_modules/@img/sharp-linuxmusl-x64": {
- "version": "0.34.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
- "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0",
- "optional": true,
- "os": [
- "linux"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- },
- "optionalDependencies": {
- "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
- }
- },
- "node_modules/@img/sharp-wasm32": {
- "version": "0.34.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
- "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
- "cpu": [
- "wasm32"
- ],
- "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
- "optional": true,
- "dependencies": {
- "@emnapi/runtime": "^1.7.0"
- },
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-win32-arm64": {
- "version": "0.34.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
- "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
- "cpu": [
- "arm64"
- ],
- "license": "Apache-2.0 AND LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-win32-ia32": {
- "version": "0.34.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
- "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
- "cpu": [
- "ia32"
- ],
- "license": "Apache-2.0 AND LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@img/sharp-win32-x64": {
- "version": "0.34.5",
- "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
- "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
- "cpu": [
- "x64"
- ],
- "license": "Apache-2.0 AND LGPL-3.0-or-later",
- "optional": true,
- "os": [
- "win32"
- ],
- "engines": {
- "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/libvips"
- }
- },
- "node_modules/@inquirer/ansi": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.7.tgz",
- "integrity": "sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
- }
- },
- "node_modules/@inquirer/confirm": {
- "version": "6.1.1",
- "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.1.1.tgz",
- "integrity": "sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/core": "^11.2.1",
- "@inquirer/type": "^4.0.7"
- },
- "engines": {
- "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/core": {
- "version": "11.2.1",
- "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.2.1.tgz",
- "integrity": "sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@inquirer/ansi": "^2.0.7",
- "@inquirer/figures": "^2.0.7",
- "@inquirer/type": "^4.0.7",
- "cli-width": "^4.1.0",
- "fast-wrap-ansi": "^0.2.0",
- "mute-stream": "^3.0.0",
- "signal-exit": "^4.1.0"
- },
- "engines": {
- "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@inquirer/figures": {
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.7.tgz",
- "integrity": "sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
- }
- },
- "node_modules/@inquirer/type": {
- "version": "4.0.7",
- "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.7.tgz",
- "integrity": "sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
- },
- "peerDependencies": {
- "@types/node": ">=18"
- },
- "peerDependenciesMeta": {
- "@types/node": {
- "optional": true
- }
- }
- },
- "node_modules/@jest/diff-sequences": {
- "version": "30.4.0",
- "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz",
- "integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
- }
- },
- "node_modules/@jest/expect-utils": {
- "version": "30.4.1",
- "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz",
- "integrity": "sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jest/get-type": "30.1.0"
- },
- "engines": {
- "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
- }
- },
- "node_modules/@jest/get-type": {
- "version": "30.1.0",
- "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz",
- "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
- }
- },
- "node_modules/@jest/pattern": {
- "version": "30.4.0",
- "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz",
- "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@types/node": "*",
- "jest-regex-util": "30.4.0"
- },
- "engines": {
- "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
- }
- },
- "node_modules/@jest/schemas": {
- "version": "30.4.1",
- "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz",
- "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@sinclair/typebox": "^0.34.0"
- },
- "engines": {
- "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
- }
- },
- "node_modules/@jest/types": {
- "version": "30.4.1",
- "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz",
- "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jest/pattern": "30.4.0",
- "@jest/schemas": "30.4.1",
- "@types/istanbul-lib-coverage": "^2.0.6",
- "@types/istanbul-reports": "^3.0.4",
- "@types/node": "*",
- "@types/yargs": "^17.0.33",
- "chalk": "^4.1.2"
- },
- "engines": {
- "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
- }
- },
- "node_modules/@joshwooding/vite-plugin-react-docgen-typescript": {
- "version": "0.7.0",
- "resolved": "https://registry.npmjs.org/@joshwooding/vite-plugin-react-docgen-typescript/-/vite-plugin-react-docgen-typescript-0.7.0.tgz",
- "integrity": "sha512-qvsTEwEFefhdirGOPnu9Wp6ChfIwy2dBCRuETU3uE+4cC+PFoxMSiiEhxk4lOluA34eARHA0OxqsEUYDqRMgeQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "glob": "^13.0.1",
- "react-docgen-typescript": "^2.2.2"
- },
- "peerDependencies": {
- "typescript": ">= 4.3.x",
- "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
- },
- "peerDependenciesMeta": {
- "typescript": {
- "optional": true
- }
- }
- },
- "node_modules/@jridgewell/gen-mapping": {
- "version": "0.3.13",
- "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
- "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/sourcemap-codec": "^1.5.0",
- "@jridgewell/trace-mapping": "^0.3.24"
- }
- },
- "node_modules/@jridgewell/remapping": {
- "version": "2.3.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
- "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/gen-mapping": "^0.3.5",
- "@jridgewell/trace-mapping": "^0.3.24"
- }
- },
- "node_modules/@jridgewell/resolve-uri": {
- "version": "3.1.2",
- "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
- "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=6.0.0"
- }
- },
- "node_modules/@jridgewell/sourcemap-codec": {
- "version": "1.5.5",
- "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
- "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@jridgewell/trace-mapping": {
- "version": "0.3.31",
- "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
- "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@jridgewell/resolve-uri": "^3.1.0",
- "@jridgewell/sourcemap-codec": "^1.4.14"
- }
- },
- "node_modules/@lhci/cli": {
- "version": "0.15.1",
- "resolved": "https://registry.npmjs.org/@lhci/cli/-/cli-0.15.1.tgz",
- "integrity": "sha512-yhC0oXnXqGHYy1xl4D8YqaydMZ/khFAnXGY/o2m/J3PqPa/D0nj3V6TLoH02oVMFeEF2AQim7UbmdXMiXx2tOw==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "@lhci/utils": "0.15.1",
- "chrome-launcher": "^0.13.4",
- "compression": "^1.7.4",
- "debug": "^4.3.1",
- "express": "^4.17.1",
- "inquirer": "^6.3.1",
- "isomorphic-fetch": "^3.0.0",
- "lighthouse": "12.6.1",
- "lighthouse-logger": "1.2.0",
- "open": "^7.1.0",
- "proxy-agent": "^6.4.0",
- "tmp": "^0.1.0",
- "uuid": "^8.3.1",
- "yargs": "^15.4.1",
- "yargs-parser": "^13.1.2"
- },
- "bin": {
- "lhci": "src/cli.js"
- }
- },
- "node_modules/@lhci/cli/node_modules/cliui": {
- "version": "6.0.0",
- "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
- "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "string-width": "^4.2.0",
- "strip-ansi": "^6.0.0",
- "wrap-ansi": "^6.2.0"
- }
- },
- "node_modules/@lhci/cli/node_modules/is-docker": {
- "version": "2.2.1",
- "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
- "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
- "dev": true,
- "license": "MIT",
- "bin": {
- "is-docker": "cli.js"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@lhci/cli/node_modules/is-wsl": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
- "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-docker": "^2.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@lhci/cli/node_modules/open": {
- "version": "7.4.2",
- "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz",
- "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "is-docker": "^2.0.0",
- "is-wsl": "^2.1.1"
- },
- "engines": {
- "node": ">=8"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/@lhci/cli/node_modules/wrap-ansi": {
- "version": "6.2.0",
- "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
- "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "ansi-styles": "^4.0.0",
- "string-width": "^4.1.0",
- "strip-ansi": "^6.0.0"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@lhci/cli/node_modules/y18n": {
- "version": "4.0.3",
- "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
- "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
- "dev": true,
- "license": "ISC"
- },
- "node_modules/@lhci/cli/node_modules/yargs": {
- "version": "15.4.1",
- "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
- "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "cliui": "^6.0.0",
- "decamelize": "^1.2.0",
- "find-up": "^4.1.0",
- "get-caller-file": "^2.0.1",
- "require-directory": "^2.1.1",
- "require-main-filename": "^2.0.0",
- "set-blocking": "^2.0.0",
- "string-width": "^4.2.0",
- "which-module": "^2.0.0",
- "y18n": "^4.0.0",
- "yargs-parser": "^18.1.2"
- },
- "engines": {
- "node": ">=8"
- }
- },
- "node_modules/@lhci/cli/node_modules/yargs-parser": {
- "version": "13.1.2",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
- "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
- }
- },
- "node_modules/@lhci/cli/node_modules/yargs/node_modules/yargs-parser": {
- "version": "18.1.3",
- "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
- "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
- "dev": true,
- "license": "ISC",
- "dependencies": {
- "camelcase": "^5.0.0",
- "decamelize": "^1.2.0"
- },
- "engines": {
- "node": ">=6"
- }
- },
- "node_modules/@lhci/utils": {
- "version": "0.15.1",
- "resolved": "https://registry.npmjs.org/@lhci/utils/-/utils-0.15.1.tgz",
- "integrity": "sha512-WclJnUQJeOMY271JSuaOjCv/aA0pgvuHZS29NFNdIeI14id8eiFsjith85EGKYhljgoQhJ2SiW4PsVfFiakNNw==",
- "dev": true,
- "license": "Apache-2.0",
- "dependencies": {
- "debug": "^4.3.1",
- "isomorphic-fetch": "^3.0.0",
- "js-yaml": "^3.13.1",
- "lighthouse": "12.6.1",
- "tree-kill": "^1.2.1"
- }
- },
- "node_modules/@lhci/utils/node_modules/argparse": {
- "version": "1.0.10",
- "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
- "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "sprintf-js": "~1.0.2"
- }
- },
- "node_modules/@lhci/utils/node_modules/js-yaml": {
- "version": "3.15.0",
- "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz",
- "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "argparse": "^1.0.7",
- "esprima": "^4.0.0"
- },
- "bin": {
- "js-yaml": "bin/js-yaml.js"
- }
- },
- "node_modules/@mswjs/interceptors": {
- "version": "0.41.9",
- "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.9.tgz",
- "integrity": "sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@open-draft/deferred-promise": "^2.2.0",
- "@open-draft/logger": "^0.3.0",
- "@open-draft/until": "^2.0.0",
- "is-node-process": "^1.2.0",
- "outvariant": "^1.4.3",
- "strict-event-emitter": "^0.5.1"
- },
- "engines": {
- "node": ">=18"
- }
- },
- "node_modules/@mswjs/interceptors/node_modules/@open-draft/deferred-promise": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz",
- "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@napi-rs/wasm-runtime": {
- "version": "1.1.6",
- "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
- "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==",
- "dev": true,
- "license": "MIT",
- "optional": true,
- "dependencies": {
- "@tybys/wasm-util": "^0.10.3"
- },
- "funding": {
- "type": "github",
- "url": "https://github.com/sponsors/Brooooooklyn"
- },
- "peerDependencies": {
- "@emnapi/core": "^1.7.1",
- "@emnapi/runtime": "^1.7.1"
- }
- },
- "node_modules/@next/env": {
- "version": "16.2.10",
- "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.10.tgz",
- "integrity": "sha512-zLPxg9M0MEHmygpj5OuxjQ+vHMiy/K7cSp74G8ecYolmgUWw0RwN02tF56npup/+qaI8JB97hQgS/r2Hb6QwVA==",
- "license": "MIT"
- },
- "node_modules/@next/eslint-plugin-next": {
- "version": "16.2.12",
- "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.12.tgz",
- "integrity": "sha512-uF2z/qAK2q7B5/6CpnFcBRX6jOq5iCO+Uqh1UkJhXljX1JwLarLYhhoJadO6dPb6moTprOKewMXheBcbIoSbug==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "fast-glob": "3.3.1"
- }
- },
- "node_modules/@next/swc-darwin-arm64": {
- "version": "16.2.10",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.10.tgz",
- "integrity": "sha512-v9IdJCa0H0mbo+8z5zwUpOk1Vj7RjkcI5uNYf5Ws1y6szf/p3Mzl9hLaST8SCt6L9h8NGnruZcd2+o0NTNwDhA==",
- "cpu": [
- "arm64"
- ],
- "license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@next/swc-darwin-x64": {
- "version": "16.2.10",
- "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.10.tgz",
- "integrity": "sha512-17IS0jJRViROGmA9uGdNR8VPJpfbnaVG7E9qhso5jDLkmyd0lSDORWxbcKINzcFqzZqGwGtMSnrFRxBpuUYjLQ==",
+ },
+ "node_modules/@img/sharp-linux-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz",
+ "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==",
"cpu": [
"x64"
],
- "license": "MIT",
+ "license": "Apache-2.0",
"optional": true,
"os": [
- "darwin"
+ "linux"
],
"engines": {
- "node": ">= 10"
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linux-x64": "1.2.4"
}
},
- "node_modules/@next/swc-linux-arm64-gnu": {
- "version": "16.2.10",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.10.tgz",
- "integrity": "sha512-GRQRsRtuciNJvB54AvvuQTiq0oZtFwa1owQqtZD8wwnGpM2L39MV22kpI72YSXLKIyY40LC66EiLFv4PiicXxg==",
+ "node_modules/@img/sharp-linuxmusl-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz",
+ "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==",
"cpu": [
"arm64"
],
- "license": "MIT",
+ "license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
- "node": ">= 10"
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-arm64": "1.2.4"
}
},
- "node_modules/@next/swc-linux-arm64-musl": {
- "version": "16.2.10",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.10.tgz",
- "integrity": "sha512-zkN9MQYS7UQBro+FnISUq1itaQjXI9xqISzuQ+2bc921NcJ1x4yPCqrn77tVN6/dOOXaaWVX3k6/bR07pPwK+A==",
+ "node_modules/@img/sharp-linuxmusl-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz",
+ "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==",
"cpu": [
- "arm64"
+ "x64"
],
- "license": "MIT",
+ "license": "Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
- "node": ">= 10"
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
+ },
+ "optionalDependencies": {
+ "@img/sharp-libvips-linuxmusl-x64": "1.2.4"
}
},
- "node_modules/@next/swc-linux-x64-gnu": {
- "version": "16.2.10",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.10.tgz",
- "integrity": "sha512-iCVJnwvrPYECvA6WM/7+oo+OiTvedIKLxtCLAZP4xZR3nXa1zmzZyLPbYCmWvpd4CvMYF1EMTafd0ii3DygLvA==",
+ "node_modules/@img/sharp-wasm32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz",
+ "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==",
"cpu": [
- "x64"
+ "wasm32"
],
- "license": "MIT",
+ "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT",
"optional": true,
- "os": [
- "linux"
- ],
+ "dependencies": {
+ "@emnapi/runtime": "^1.7.0"
+ },
"engines": {
- "node": ">= 10"
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
}
},
- "node_modules/@next/swc-linux-x64-musl": {
- "version": "16.2.10",
- "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.10.tgz",
- "integrity": "sha512-ov2g4H0dHY9bPoOU83m91hWT7Iq5qy13bUnyyshLU3HGR1Ownn0X9QpmDPc5iIUaahTp7f7LeGAhV4DSFtackw==",
+ "node_modules/@img/sharp-win32-arm64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz",
+ "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==",
"cpu": [
- "x64"
+ "arm64"
],
- "license": "MIT",
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
- "linux"
+ "win32"
],
"engines": {
- "node": ">= 10"
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
}
},
- "node_modules/@next/swc-win32-arm64-msvc": {
- "version": "16.2.10",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.10.tgz",
- "integrity": "sha512-DwAnhLX76HQiFFQNgWlcK+JzlnD1rZ+UK/WY0ZMI/deXpvgnesjNYrqcfo1JzBuz4Kf7o3brIBL0glI1junatA==",
+ "node_modules/@img/sharp-win32-ia32": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz",
+ "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==",
"cpu": [
- "arm64"
+ "ia32"
],
- "license": "MIT",
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
"win32"
],
"engines": {
- "node": ">= 10"
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
+ },
+ "funding": {
+ "url": "https://opencollective.com/libvips"
}
},
- "node_modules/@next/swc-win32-x64-msvc": {
- "version": "16.2.10",
- "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.10.tgz",
- "integrity": "sha512-0JXq3b85Jk9Jg4ntLUbXSPvoDw3gpZou7twuKdoFG2jOw635v7+IiXfTaa0TxVMyx78pUjnrVYwLgjKfX4e6/A==",
+ "node_modules/@img/sharp-win32-x64": {
+ "version": "0.34.5",
+ "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz",
+ "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==",
"cpu": [
"x64"
],
- "license": "MIT",
+ "license": "Apache-2.0 AND LGPL-3.0-or-later",
"optional": true,
"os": [
"win32"
],
"engines": {
- "node": ">= 10"
- }
- },
- "node_modules/@noble/ed25519": {
- "version": "3.1.0",
- "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-3.1.0.tgz",
- "integrity": "sha512-pfcObRY3CtvwfaG9Mt5XqZdKmAQppl37tHUeuBhDUbiwJBCVY4/A4lbMvb1xKhMDx96AqAqZpMWuBX1HulhX4g==",
- "license": "MIT",
- "funding": {
- "url": "https://paulmillr.com/funding/"
- }
- },
- "node_modules/@noble/hashes": {
- "version": "2.2.0",
- "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz",
- "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==",
- "license": "MIT",
- "engines": {
- "node": ">= 20.19.0"
+ "node": "^18.17.0 || ^20.3.0 || >=21.0.0"
},
"funding": {
- "url": "https://paulmillr.com/funding/"
- }
- },
- "node_modules/@nodelib/fs.scandir": {
- "version": "2.1.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
- "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@nodelib/fs.stat": "2.0.5",
- "run-parallel": "^1.1.9"
- },
- "engines": {
- "node": ">= 8"
+ "url": "https://opencollective.com/libvips"
}
},
- "node_modules/@nodelib/fs.stat": {
- "version": "2.0.5",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
- "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+ "node_modules/@inquirer/ansi": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@inquirer/ansi/-/ansi-2.0.7.tgz",
+ "integrity": "sha512-3eTuUO1vH2cZm2ZKHeQxnOqlTi9EfZDGgIe3BL3I4u+rJHocr9Fz86M4fjYABPvFnQG/gGK551HqDiIcETwU6Q==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">= 8"
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
}
},
- "node_modules/@nodelib/fs.walk": {
- "version": "1.2.8",
- "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
- "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+ "node_modules/@inquirer/confirm": {
+ "version": "6.1.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/confirm/-/confirm-6.1.1.tgz",
+ "integrity": "sha512-eb8DBZcz/2qHWQda4rk2JiQk5h9QV/cVHi1yjt0f69WFZMRFn0sJTye3EAP8icut8UDMjQPsaH5KbcOogefrFQ==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@nodelib/fs.scandir": "2.1.5",
- "fastq": "^1.6.0"
+ "@inquirer/core": "^11.2.1",
+ "@inquirer/type": "^4.0.7"
},
"engines": {
- "node": ">= 8"
- }
- },
- "node_modules/@nolyfill/is-core-module": {
- "version": "1.0.39",
- "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz",
- "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">=12.4.0"
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
}
},
- "node_modules/@open-draft/deferred-promise": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-3.0.0.tgz",
- "integrity": "sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@open-draft/logger": {
- "version": "0.3.0",
- "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz",
- "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==",
+ "node_modules/@inquirer/core": {
+ "version": "11.2.1",
+ "resolved": "https://registry.npmjs.org/@inquirer/core/-/core-11.2.1.tgz",
+ "integrity": "sha512-Qd6GJT1yVyrZZCfN8W2qKF5ApmqryXRhRKCuip8h01x2w/esJQ2XIYc6f9abMIHgKQdBfFTSOdbHRLAhuM09UA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "is-node-process": "^1.2.0",
- "outvariant": "^1.4.0"
+ "@inquirer/ansi": "^2.0.7",
+ "@inquirer/figures": "^2.0.7",
+ "@inquirer/type": "^4.0.7",
+ "cli-width": "^4.1.0",
+ "fast-wrap-ansi": "^0.2.0",
+ "mute-stream": "^3.0.0",
+ "signal-exit": "^4.1.0"
+ },
+ "engines": {
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
}
},
- "node_modules/@open-draft/until": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz",
- "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==",
- "dev": true,
- "license": "MIT"
- },
- "node_modules/@oxc-parser/binding-android-arm-eabi": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm-eabi/-/binding-android-arm-eabi-0.127.0.tgz",
- "integrity": "sha512-0LC7ye4hvqbIKxAzThzvswgHLFu2AURKzYLeSVvLdu2TBOYWQDmHnTqPLeA597BcUCxiLqLsS4CJ5uoI5WYWCQ==",
- "cpu": [
- "arm"
- ],
+ "node_modules/@inquirer/figures": {
+ "version": "2.0.7",
+ "resolved": "https://registry.npmjs.org/@inquirer/figures/-/figures-2.0.7.tgz",
+ "integrity": "sha512-aJ8TBPOGB6f/2qziPfElISTCEd5XOYTFckA2SGjhNmiKzfK/u4ot3v0DUzGVdUnKjN10EqnnEPck36BkyfLnJw==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "peer": true,
+ "license": "MIT",
"engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
}
},
- "node_modules/@oxc-parser/binding-android-arm64": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-android-arm64/-/binding-android-arm64-0.127.0.tgz",
- "integrity": "sha512-b5jtVTH6AU5CJXHNdj7Jj9IEiR9yVjjnwHzPJhGyHGPdcsZSzBCkS9GBbV33niRMvKthDwQRFRJfI4a+k4PvYg==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@inquirer/type": {
+ "version": "4.0.7",
+ "resolved": "https://registry.npmjs.org/@inquirer/type/-/type-4.0.7.tgz",
+ "integrity": "sha512-t28inv14nMQ1PhKpsJPY+kEs/c00qzeCOS2gTNRyTjG5d6qsVA2fItxW4hkvGZ5lvanGLdtCzVIx5dwdRpN1+g==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "peer": true,
"engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "node": ">=23.5.0 || ^22.13.0 || ^20.17.0"
+ },
+ "peerDependencies": {
+ "@types/node": ">=18"
+ },
+ "peerDependenciesMeta": {
+ "@types/node": {
+ "optional": true
+ }
}
},
- "node_modules/@oxc-parser/binding-darwin-arm64": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-arm64/-/binding-darwin-arm64-0.127.0.tgz",
- "integrity": "sha512-obCE8B7ISKkJidjlhv9xRGJPOSDG2Yu6PRga9Ruaz35uintHxbp1Ki/Yc71wx4rj3Edrm0a1kzG1TAwit0wFpg==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@jest/diff-sequences": {
+ "version": "30.4.0",
+ "resolved": "https://registry.npmjs.org/@jest/diff-sequences/-/diff-sequences-30.4.0.tgz",
+ "integrity": "sha512-zOpzlfUs45l6u7jm39qr87JCHUDsaeCtvL+kQe/Vn9jSnRB4/5IPXISm0h9I1vZW/o00Kn4UTJ2MOlhnUGwv3g==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "peer": true,
"engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
}
},
- "node_modules/@oxc-parser/binding-darwin-x64": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-darwin-x64/-/binding-darwin-x64-0.127.0.tgz",
- "integrity": "sha512-JL6Xb5IwPQT8rUzlpsX7E+AgfcdNklXNPFp8pjCQQ5MQOQo5rtEB2ui+3Hgg9Sn7Y9Egj6YOLLiHhLpdAe12Aw==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@jest/expect-utils": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-30.4.1.tgz",
+ "integrity": "sha512-ZBn5CglH8fBsQsvs4VWNzD4aWfUYks+IdOOQU3MEK71ol/BcVm+P+rtb1KpiFBpSWSCE27uOahyyf1vfqOVbcQ==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "peer": true,
+ "dependencies": {
+ "@jest/get-type": "30.1.0"
+ },
"engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
}
},
- "node_modules/@oxc-parser/binding-freebsd-x64": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-freebsd-x64/-/binding-freebsd-x64-0.127.0.tgz",
- "integrity": "sha512-SDQ/3MQFw58fqQz3Z1PhSKFF3JoCF4gmlNjziDm8X02tTahCw0qJbd7FGPDKw1i4VTBZene9JPyC3mHtSvi+wA==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@jest/get-type": {
+ "version": "30.1.0",
+ "resolved": "https://registry.npmjs.org/@jest/get-type/-/get-type-30.1.0.tgz",
+ "integrity": "sha512-eMbZE2hUnx1WV0pmURZY9XoXPkUYjpc55mb0CrhtdWLtzMQPFvu/rZkTLZFTsdaVQa+Tr4eWAteqcUzoawq/uA==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "peer": true,
"engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
}
},
- "node_modules/@oxc-parser/binding-linux-arm-gnueabihf": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-0.127.0.tgz",
- "integrity": "sha512-Av+D1MIqzV0YMGPT9we2SIZaMKD7Cxs4CvXSx/yxaWHewZjYEjScpOf5igc8IILASViw4WTnjlwUdI1KzVtDHQ==",
- "cpu": [
- "arm"
- ],
+ "node_modules/@jest/pattern": {
+ "version": "30.4.0",
+ "resolved": "https://registry.npmjs.org/@jest/pattern/-/pattern-30.4.0.tgz",
+ "integrity": "sha512-RAWn3+f9u8BsHijKJ71uHcFp6vmyEt6VvoWXkl6hKF3qVIuWNmudVjg12DlBPGup/frIl5UcUlH5HfEuvHpEXg==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
+ "dependencies": {
+ "@types/node": "*",
+ "jest-regex-util": "30.4.0"
+ },
"engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
}
},
- "node_modules/@oxc-parser/binding-linux-arm-musleabihf": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-0.127.0.tgz",
- "integrity": "sha512-Cs2fdJ8cPpFdeebj6p4dag8A4+56hPvZ0AhQQzlaLswGz1tz7bXt1nETLeorrM9+AMcWFFkqxcXwDGfTVidY8g==",
- "cpu": [
- "arm"
- ],
+ "node_modules/@jest/schemas": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-30.4.1.tgz",
+ "integrity": "sha512-i6b4qw5qnP8c5FEeBJg/uZQ4ddrkN6Ca8qISJh0pr7a5hfn3h3v5x60BEbOC7OYAGZNMs1LfFLwnW2CuK8F57Q==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
+ "dependencies": {
+ "@sinclair/typebox": "^0.34.0"
+ },
"engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
}
},
- "node_modules/@oxc-parser/binding-linux-arm64-gnu": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-0.127.0.tgz",
- "integrity": "sha512-qdOfTcT6SY8gsJrrV92uyEUyjqMGPpIB5JZUG6QN5dukYd+7/j0kX6MwK1DgQj39jtUYixxPiaRUiEN1+0CXgQ==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@jest/types": {
+ "version": "30.4.1",
+ "resolved": "https://registry.npmjs.org/@jest/types/-/types-30.4.1.tgz",
+ "integrity": "sha512-f1x/vJXIfjOlEmejYpbkbgw1gOqpPECwMvMEtBqe47j7H2Hg8h8w3o3ikhSXq3MI15kg+oQ0exWO0uCtTNJLoQ==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
+ "dependencies": {
+ "@jest/pattern": "30.4.0",
+ "@jest/schemas": "30.4.1",
+ "@types/istanbul-lib-coverage": "^2.0.6",
+ "@types/istanbul-reports": "^3.0.4",
+ "@types/node": "*",
+ "@types/yargs": "^17.0.33",
+ "chalk": "^4.1.2"
+ },
"engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "node": "^18.14.0 || ^20.0.0 || ^22.0.0 || >=24.0.0"
}
},
- "node_modules/@oxc-parser/binding-linux-arm64-musl": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-arm64-musl/-/binding-linux-arm64-musl-0.127.0.tgz",
- "integrity": "sha512-EoTCZneNFU/P2qrpEM+RHmQwt+CvDkyGESG6qhr7KaegXLZwePfbrkCDfAk8/rhxbDUVGsZILX+2tqPzFtoFWA==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@joshwooding/vite-plugin-react-docgen-typescript": {
+ "version": "0.7.0",
+ "resolved": "https://registry.npmjs.org/@joshwooding/vite-plugin-react-docgen-typescript/-/vite-plugin-react-docgen-typescript-0.7.0.tgz",
+ "integrity": "sha512-qvsTEwEFefhdirGOPnu9Wp6ChfIwy2dBCRuETU3uE+4cC+PFoxMSiiEhxk4lOluA34eARHA0OxqsEUYDqRMgeQ==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "dependencies": {
+ "glob": "^13.0.1",
+ "react-docgen-typescript": "^2.2.2"
+ },
+ "peerDependencies": {
+ "typescript": ">= 4.3.x",
+ "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0"
+ },
+ "peerDependenciesMeta": {
+ "typescript": {
+ "optional": true
+ }
}
},
- "node_modules/@oxc-parser/binding-linux-ppc64-gnu": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-0.127.0.tgz",
- "integrity": "sha512-zALjmZYgxFLHjXeudcDF0xFGNydTAtkAeXAr2EuC17ywCyFxcmQra4w0BMde0Yi/re4Bi4iwEoEXtYN7l6eBLQ==",
- "cpu": [
- "ppc64"
- ],
+ "node_modules/@jridgewell/gen-mapping": {
+ "version": "0.3.13",
+ "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz",
+ "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "dependencies": {
+ "@jridgewell/sourcemap-codec": "^1.5.0",
+ "@jridgewell/trace-mapping": "^0.3.24"
}
},
- "node_modules/@oxc-parser/binding-linux-riscv64-gnu": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-0.127.0.tgz",
- "integrity": "sha512-fPP8M6zQLS7Jz7o9d5ArUSuAuSK3e+WCYVrCpdzeCOejidtZExJ9tjhDrAd3HEPqARBCPmdpqxESPFqy44vkBQ==",
- "cpu": [
- "riscv64"
- ],
+ "node_modules/@jridgewell/remapping": {
+ "version": "2.3.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz",
+ "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "dependencies": {
+ "@jridgewell/gen-mapping": "^0.3.5",
+ "@jridgewell/trace-mapping": "^0.3.24"
}
},
- "node_modules/@oxc-parser/binding-linux-riscv64-musl": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-0.127.0.tgz",
- "integrity": "sha512-7IcC4Ao02oGpfnjt+X/oF4U2mllo2qoSkw5xxiXNKL9MCTsTiAC6616beOuehdxGcnz1bRoPC1RQ2f1GQDdN+g==",
- "cpu": [
- "riscv64"
- ],
+ "node_modules/@jridgewell/resolve-uri": {
+ "version": "3.1.2",
+ "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+ "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
"engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "node": ">=6.0.0"
}
},
- "node_modules/@oxc-parser/binding-linux-s390x-gnu": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-0.127.0.tgz",
- "integrity": "sha512-pbXIhiNFHoqWeqDNLiJ9JkpHz1IM9k4DXa66x+1GTWMG7iLxtkXgE53iiuKSXwmk3zIYmaPVfBvgcAhS583K4Q==",
- "cpu": [
- "s390x"
- ],
+ "node_modules/@jridgewell/sourcemap-codec": {
+ "version": "1.5.5",
+ "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz",
+ "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@jridgewell/trace-mapping": {
+ "version": "0.3.31",
+ "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz",
+ "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "dependencies": {
+ "@jridgewell/resolve-uri": "^3.1.0",
+ "@jridgewell/sourcemap-codec": "^1.4.14"
}
},
- "node_modules/@oxc-parser/binding-linux-x64-gnu": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-gnu/-/binding-linux-x64-gnu-0.127.0.tgz",
- "integrity": "sha512-MYCguB9RvBvlSd6gbuNI7QwiLoCCAlGnlRJFPrzLI6U1/9wkC/WK6LtBAUln55H1Ctqw45PWmqrobKoMhsYQzQ==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@lhci/cli": {
+ "version": "0.15.1",
+ "resolved": "https://registry.npmjs.org/@lhci/cli/-/cli-0.15.1.tgz",
+ "integrity": "sha512-yhC0oXnXqGHYy1xl4D8YqaydMZ/khFAnXGY/o2m/J3PqPa/D0nj3V6TLoH02oVMFeEF2AQim7UbmdXMiXx2tOw==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@lhci/utils": "0.15.1",
+ "chrome-launcher": "^0.13.4",
+ "compression": "^1.7.4",
+ "debug": "^4.3.1",
+ "express": "^4.17.1",
+ "inquirer": "^6.3.1",
+ "isomorphic-fetch": "^3.0.0",
+ "lighthouse": "12.6.1",
+ "lighthouse-logger": "1.2.0",
+ "open": "^7.1.0",
+ "proxy-agent": "^6.4.0",
+ "tmp": "^0.1.0",
+ "uuid": "^8.3.1",
+ "yargs": "^15.4.1",
+ "yargs-parser": "^13.1.2"
+ },
+ "bin": {
+ "lhci": "src/cli.js"
}
},
- "node_modules/@oxc-parser/binding-linux-x64-musl": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-linux-x64-musl/-/binding-linux-x64-musl-0.127.0.tgz",
- "integrity": "sha512-5eY0B/bxf1xIUxb4NOTvOI3KWtBQfPWYyKAzgcrCt0mDibSZygVpO1Pz8bkeiSZ5Jj9+M09dkggG3H8I5d0Uyg==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@lhci/cli/node_modules/cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true,
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
}
},
- "node_modules/@oxc-parser/binding-openharmony-arm64": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-openharmony-arm64/-/binding-openharmony-arm64-0.127.0.tgz",
- "integrity": "sha512-Gld0ajrFTUXNtdw20fVBuTQx66FA75nIVg+//pPfR3sXkuABB4mTBhl3r9JNzrJpgW//qiwxf0nWXUWGJSL3UQ==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@lhci/cli/node_modules/is-docker": {
+ "version": "2.2.1",
+ "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz",
+ "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "openharmony"
- ],
- "peer": true,
+ "bin": {
+ "is-docker": "cli.js"
+ },
"engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@oxc-parser/binding-wasm32-wasi": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-wasm32-wasi/-/binding-wasm32-wasi-0.127.0.tgz",
- "integrity": "sha512-T6KVD7rhLzFlwGRXMnxUFfkCZD8FHnb968wVXW1mXzgRFc5RNXOBY2mPPDZ77x5Ln76ltLMgtPg0cOkU1NSrEQ==",
- "cpu": [
- "wasm32"
- ],
+ "node_modules/@lhci/cli/node_modules/is-wsl": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+ "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
"dev": true,
"license": "MIT",
- "optional": true,
- "peer": true,
"dependencies": {
- "@emnapi/core": "1.9.2",
- "@emnapi/runtime": "1.9.2",
- "@napi-rs/wasm-runtime": "^1.1.4"
+ "is-docker": "^2.0.0"
},
"engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "node": ">=8"
}
},
- "node_modules/@oxc-parser/binding-wasm32-wasi/node_modules/@emnapi/runtime": {
- "version": "1.9.2",
- "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.2.tgz",
- "integrity": "sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==",
+ "node_modules/@lhci/cli/node_modules/open": {
+ "version": "7.4.2",
+ "resolved": "https://registry.npmjs.org/open/-/open-7.4.2.tgz",
+ "integrity": "sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==",
"dev": true,
"license": "MIT",
- "optional": true,
- "peer": true,
"dependencies": {
- "tslib": "^2.4.0"
+ "is-docker": "^2.0.0",
+ "is-wsl": "^2.1.1"
+ },
+ "engines": {
+ "node": ">=8"
+ },
+ "funding": {
+ "url": "https://github.com/sponsors/sindresorhus"
}
},
- "node_modules/@oxc-parser/binding-win32-arm64-msvc": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-0.127.0.tgz",
- "integrity": "sha512-Ujvw4X+LD1CCGULcsQcvb4YNVoBGqt+JHgNNzGGaCImELiZLk477ifUH53gIbE7EKd933NdTi25JWEr9K2HwXw==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@lhci/cli/node_modules/wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
"engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "node": ">=8"
}
},
- "node_modules/@oxc-parser/binding-win32-ia32-msvc": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-ia32-msvc/-/binding-win32-ia32-msvc-0.127.0.tgz",
- "integrity": "sha512-0cwxKO7KHQQQfo4Uf4B2SQrhgm+cJaP9OvFFhx52Tkg4bezsacu83GB2/In5bC415Ueeym+kXdnge/57rbSfTw==",
- "cpu": [
- "ia32"
- ],
+ "node_modules/@lhci/cli/node_modules/y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- }
+ "license": "ISC"
},
- "node_modules/@oxc-parser/binding-win32-x64-msvc": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-parser/binding-win32-x64-msvc/-/binding-win32-x64-msvc-0.127.0.tgz",
- "integrity": "sha512-rOrnSQSCbhI2kowr9XxE7m9a8oQXnBHjnS6j95LxxAnEZ0+Fz20WlRXG4ondQb+ejjt2KOsa65sE6++L6kUd+w==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@lhci/cli/node_modules/yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true,
+ "dependencies": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ },
"engines": {
- "node": "^20.19.0 || >=22.12.0"
+ "node": ">=8"
}
},
- "node_modules/@oxc-project/types": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.127.0.tgz",
- "integrity": "sha512-aIYXQBo4lCbO4z0R3FHeucQHpF46l2LbMdxRvqvuRuW2OxdnSkcng5B8+K12spgLDj93rtN3+J2Vac/TIO+ciQ==",
+ "node_modules/@lhci/cli/node_modules/yargs-parser": {
+ "version": "13.1.2",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-13.1.2.tgz",
+ "integrity": "sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==",
"dev": true,
- "license": "MIT",
- "peer": true,
- "funding": {
- "url": "https://github.com/sponsors/Boshen"
+ "license": "ISC",
+ "dependencies": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
}
},
- "node_modules/@oxc-resolver/binding-android-arm-eabi": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm-eabi/-/binding-android-arm-eabi-11.24.2.tgz",
- "integrity": "sha512-y09e0L0SRI2OA2tUIrjBgoV3eH5hvUKXNkJqXmNo5V2WxIjyC7I7aJfRLMEVpA8yi95f90gFDvO0VMgrDw+vwA==",
- "cpu": [
- "arm"
- ],
+ "node_modules/@lhci/cli/node_modules/yargs/node_modules/yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "peer": true
+ "license": "ISC",
+ "dependencies": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
},
- "node_modules/@oxc-resolver/binding-android-arm64": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-android-arm64/-/binding-android-arm64-11.24.2.tgz",
- "integrity": "sha512-cl4icWaZFnLdg8m6qtnh5rBMuGbxc/ptStFHLeCNwr+2cZjkjNwQu/jYRS0CHlnPecOJMpuS5M6/BH+0J/YkEg==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@lhci/utils": {
+ "version": "0.15.1",
+ "resolved": "https://registry.npmjs.org/@lhci/utils/-/utils-0.15.1.tgz",
+ "integrity": "sha512-WclJnUQJeOMY271JSuaOjCv/aA0pgvuHZS29NFNdIeI14id8eiFsjith85EGKYhljgoQhJ2SiW4PsVfFiakNNw==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "android"
- ],
- "peer": true
+ "license": "Apache-2.0",
+ "dependencies": {
+ "debug": "^4.3.1",
+ "isomorphic-fetch": "^3.0.0",
+ "js-yaml": "^3.13.1",
+ "lighthouse": "12.6.1",
+ "tree-kill": "^1.2.1"
+ }
},
- "node_modules/@oxc-resolver/binding-darwin-arm64": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-arm64/-/binding-darwin-arm64-11.24.2.tgz",
- "integrity": "sha512-At29QEMF6HajbQvgY8K6OXnHD1x9rad74xBEfmCB6ZqCGsdq75aK7tOYcTbOanMy8qdIBrfL3SMr3p/lfSlb9w==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@lhci/utils/node_modules/argparse": {
+ "version": "1.0.10",
+ "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+ "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "peer": true
+ "dependencies": {
+ "sprintf-js": "~1.0.2"
+ }
},
- "node_modules/@oxc-resolver/binding-darwin-x64": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-darwin-x64/-/binding-darwin-x64-11.24.2.tgz",
- "integrity": "sha512-A5Kqr1EUj4oIL5CF4WRssq/o5P0Y11cwoFouMRmQ7YnC/A8V93nv1nb7aSU8HwcgmXropjLNkVTl4MN87cu28Q==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@lhci/utils/node_modules/js-yaml": {
+ "version": "3.15.0",
+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.15.0.tgz",
+ "integrity": "sha512-ttBQIIQPDeLjpPOohtUdXuXUVoA2uIB6fEH9HyJ7234s5mBJ5wTx20njxplLZQgLaOfpmPQA7X2t5AX6tIPbog==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "darwin"
- ],
- "peer": true
+ "dependencies": {
+ "argparse": "^1.0.7",
+ "esprima": "^4.0.0"
+ },
+ "bin": {
+ "js-yaml": "bin/js-yaml.js"
+ }
},
- "node_modules/@oxc-resolver/binding-freebsd-x64": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-freebsd-x64/-/binding-freebsd-x64-11.24.2.tgz",
- "integrity": "sha512-R5xkRBRRz7ceH/P5Jrc6G7FmdUdgpLYyESFAUDVTNQ9K0sGPxcp4ljiwEwEqsvNcQ4sYbMRrWcHHBCu7ksAJVw==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@mswjs/interceptors": {
+ "version": "0.41.9",
+ "resolved": "https://registry.npmjs.org/@mswjs/interceptors/-/interceptors-0.41.9.tgz",
+ "integrity": "sha512-VVPPgHyQ6ShqnrmDWuxjmUIsO9gWyOZFmuOfLd9LfBGQJwZfy0gvv9pbHSJuoFNIYC7ZDX9aoFwowjcdSC4E8w==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "freebsd"
- ],
- "peer": true
+ "dependencies": {
+ "@open-draft/deferred-promise": "^2.2.0",
+ "@open-draft/logger": "^0.3.0",
+ "@open-draft/until": "^2.0.0",
+ "is-node-process": "^1.2.0",
+ "outvariant": "^1.4.3",
+ "strict-event-emitter": "^0.5.1"
+ },
+ "engines": {
+ "node": ">=18"
+ }
},
- "node_modules/@oxc-resolver/binding-linux-arm-gnueabihf": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-11.24.2.tgz",
- "integrity": "sha512-k/RuYL4L/R58IBn3wT5ma3Wh4k62bp1eYCFRWCmMsasUOqL+H6sW0VGFadEzKWXFFlz+2uIMoeMk9ySSZJHgbg==",
- "cpu": [
- "arm"
- ],
+ "node_modules/@mswjs/interceptors/node_modules/@open-draft/deferred-promise": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz",
+ "integrity": "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA==",
"dev": true,
- "license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true
+ "license": "MIT"
},
- "node_modules/@oxc-resolver/binding-linux-arm-musleabihf": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm-musleabihf/-/binding-linux-arm-musleabihf-11.24.2.tgz",
- "integrity": "sha512-bnHAak3ujYfH5pKk4NieFNbvYvernfoQDgwLddbZ3OtMYrem87/qjlA+u+aKG0oZcqSLGCful/6/CEA+aeAgaA==",
- "cpu": [
- "arm"
- ],
+ "node_modules/@napi-rs/wasm-runtime": {
+ "version": "1.1.6",
+ "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.6.tgz",
+ "integrity": "sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==",
"dev": true,
"license": "MIT",
"optional": true,
- "os": [
- "linux"
- ],
- "peer": true
+ "dependencies": {
+ "@tybys/wasm-util": "^0.10.3"
+ },
+ "funding": {
+ "type": "github",
+ "url": "https://github.com/sponsors/Brooooooklyn"
+ },
+ "peerDependencies": {
+ "@emnapi/core": "^1.7.1",
+ "@emnapi/runtime": "^1.7.1"
+ }
},
- "node_modules/@oxc-resolver/binding-linux-arm64-gnu": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-11.24.2.tgz",
- "integrity": "sha512-vDT3KHgzYp47gmtNOqL2VNhCyl5Zv643eyxm//A68J8DeUGXrvD1pZFiaT4jSfe+RInfnn1R2yVHye4enx6RnA==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@next/env": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/env/-/env-16.2.10.tgz",
+ "integrity": "sha512-zLPxg9M0MEHmygpj5OuxjQ+vHMiy/K7cSp74G8ecYolmgUWw0RwN02tF56npup/+qaI8JB97hQgS/r2Hb6QwVA==",
+ "license": "MIT"
+ },
+ "node_modules/@next/eslint-plugin-next": {
+ "version": "16.2.12",
+ "resolved": "https://registry.npmjs.org/@next/eslint-plugin-next/-/eslint-plugin-next-16.2.12.tgz",
+ "integrity": "sha512-uF2z/qAK2q7B5/6CpnFcBRX6jOq5iCO+Uqh1UkJhXljX1JwLarLYhhoJadO6dPb6moTprOKewMXheBcbIoSbug==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "linux"
- ],
- "peer": true
+ "dependencies": {
+ "fast-glob": "3.3.1"
+ }
},
- "node_modules/@oxc-resolver/binding-linux-arm64-musl": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-arm64-musl/-/binding-linux-arm64-musl-11.24.2.tgz",
- "integrity": "sha512-+kMlQvbzfyEYtu5FcjE4p+ttBLpKW4d/AsAsuE69BxV6V4twZJeIQZFfD8gh/wqglY0MkPSezWXQH0jBV13MUw==",
+ "node_modules/@next/swc-darwin-arm64": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-arm64/-/swc-darwin-arm64-16.2.10.tgz",
+ "integrity": "sha512-v9IdJCa0H0mbo+8z5zwUpOk1Vj7RjkcI5uNYf5Ws1y6szf/p3Mzl9hLaST8SCt6L9h8NGnruZcd2+o0NTNwDhA==",
"cpu": [
"arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
- "linux"
+ "darwin"
],
- "peer": true
+ "engines": {
+ "node": ">= 10"
+ }
},
- "node_modules/@oxc-resolver/binding-linux-ppc64-gnu": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-11.24.2.tgz",
- "integrity": "sha512-shjfMhmZ3gq9fv/w7bi3PnZlgOPG+2QAOFf0BJF0EgBSIGZ6PMLN2zbGEblTUYB/NKVDRyYhE2ff3dJ1QqNPkA==",
+ "node_modules/@next/swc-darwin-x64": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-16.2.10.tgz",
+ "integrity": "sha512-17IS0jJRViROGmA9uGdNR8VPJpfbnaVG7E9qhso5jDLkmyd0lSDORWxbcKINzcFqzZqGwGtMSnrFRxBpuUYjLQ==",
"cpu": [
- "ppc64"
+ "x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
- "linux"
+ "darwin"
],
- "peer": true
+ "engines": {
+ "node": ">= 10"
+ }
},
- "node_modules/@oxc-resolver/binding-linux-riscv64-gnu": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-gnu/-/binding-linux-riscv64-gnu-11.24.2.tgz",
- "integrity": "sha512-zGelwFR5oRo+b69k8Lrzun86DyUHzfKN6cnjbR9l7Z7NIRznOE/2ZvPa1IUKqAL2PzAXOdwkfVqNvO1H2RlpAw==",
+ "node_modules/@next/swc-linux-arm64-gnu": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-16.2.10.tgz",
+ "integrity": "sha512-GRQRsRtuciNJvB54AvvuQTiq0oZtFwa1owQqtZD8wwnGpM2L39MV22kpI72YSXLKIyY40LC66EiLFv4PiicXxg==",
"cpu": [
- "riscv64"
+ "arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
- "peer": true
+ "engines": {
+ "node": ">= 10"
+ }
},
- "node_modules/@oxc-resolver/binding-linux-riscv64-musl": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-riscv64-musl/-/binding-linux-riscv64-musl-11.24.2.tgz",
- "integrity": "sha512-qxZ1SWCXJY0eyhAlP6Lmo9F2Nrtx7EkYj9oCgL8apDPCwXwCEDA2U697bbT81JIc2IrVjxO4KX6WU2N+oN9Z4w==",
+ "node_modules/@next/swc-linux-arm64-musl": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-16.2.10.tgz",
+ "integrity": "sha512-zkN9MQYS7UQBro+FnISUq1itaQjXI9xqISzuQ+2bc921NcJ1x4yPCqrn77tVN6/dOOXaaWVX3k6/bR07pPwK+A==",
"cpu": [
- "riscv64"
+ "arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
- "peer": true
+ "engines": {
+ "node": ">= 10"
+ }
},
- "node_modules/@oxc-resolver/binding-linux-s390x-gnu": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-11.24.2.tgz",
- "integrity": "sha512-sGCecF3cx2DFlH4t/z7ApnOnXqN48p5p5mlHDEnHTAukQa2P+qMVE4CwyWE9W+q/m3QJ7kKfGrIjax31f44oFQ==",
+ "node_modules/@next/swc-linux-x64-gnu": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-16.2.10.tgz",
+ "integrity": "sha512-iCVJnwvrPYECvA6WM/7+oo+OiTvedIKLxtCLAZP4xZR3nXa1zmzZyLPbYCmWvpd4CvMYF1EMTafd0ii3DygLvA==",
"cpu": [
- "s390x"
+ "x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
- "peer": true
+ "engines": {
+ "node": ">= 10"
+ }
},
- "node_modules/@oxc-resolver/binding-linux-x64-gnu": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-gnu/-/binding-linux-x64-gnu-11.24.2.tgz",
- "integrity": "sha512-k/VlMMcSzMlahb3/fENM4rTlsJ0s3fFROA0KXPBmKggqmTSaE383sl8F3KCOXPLmVsYfW6hCitMhXCEtNeZxxg==",
+ "node_modules/@next/swc-linux-x64-musl": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-16.2.10.tgz",
+ "integrity": "sha512-ov2g4H0dHY9bPoOU83m91hWT7Iq5qy13bUnyyshLU3HGR1Ownn0X9QpmDPc5iIUaahTp7f7LeGAhV4DSFtackw==",
"cpu": [
"x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
"linux"
],
- "peer": true
+ "engines": {
+ "node": ">= 10"
+ }
},
- "node_modules/@oxc-resolver/binding-linux-x64-musl": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-linux-x64-musl/-/binding-linux-x64-musl-11.24.2.tgz",
- "integrity": "sha512-8hbnZyNi97b/8wapYaIF9+t9GmZKBW2vunaOc3h9HGJptH7b7XpvZqOTBSm/MpTjr7H497BlgOaSfLUdhmy2bw==",
+ "node_modules/@next/swc-win32-arm64-msvc": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-16.2.10.tgz",
+ "integrity": "sha512-DwAnhLX76HQiFFQNgWlcK+JzlnD1rZ+UK/WY0ZMI/deXpvgnesjNYrqcfo1JzBuz4Kf7o3brIBL0glI1junatA==",
"cpu": [
- "x64"
+ "arm64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
- "linux"
+ "win32"
],
- "peer": true
+ "engines": {
+ "node": ">= 10"
+ }
},
- "node_modules/@oxc-resolver/binding-openharmony-arm64": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-openharmony-arm64/-/binding-openharmony-arm64-11.24.2.tgz",
- "integrity": "sha512-MvyGik3a6pVgZ0t/kWlbmFxFLmXQJwgLsY2eYFHLpy0wGwRbfzeIGgDwQ3kXqE30z+kSXennRkCrT7TUvkptNg==",
+ "node_modules/@next/swc-win32-x64-msvc": {
+ "version": "16.2.10",
+ "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-16.2.10.tgz",
+ "integrity": "sha512-0JXq3b85Jk9Jg4ntLUbXSPvoDw3gpZou7twuKdoFG2jOw635v7+IiXfTaa0TxVMyx78pUjnrVYwLgjKfX4e6/A==",
"cpu": [
- "arm64"
+ "x64"
],
- "dev": true,
"license": "MIT",
"optional": true,
"os": [
- "openharmony"
+ "win32"
],
- "peer": true
+ "engines": {
+ "node": ">= 10"
+ }
},
- "node_modules/@oxc-resolver/binding-wasm32-wasi": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-wasm32-wasi/-/binding-wasm32-wasi-11.24.2.tgz",
- "integrity": "sha512-vHcssMPwO08RTvj/c0iOBz90attxyG3wQJ0dTcyEQK43LRpcdLWZlV5feBhv6Isn6ahbQIzHbCgfa81+RiML0Q==",
- "cpu": [
- "wasm32"
- ],
+ "node_modules/@noble/ed25519": {
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/@noble/ed25519/-/ed25519-3.1.0.tgz",
+ "integrity": "sha512-pfcObRY3CtvwfaG9Mt5XqZdKmAQppl37tHUeuBhDUbiwJBCVY4/A4lbMvb1xKhMDx96AqAqZpMWuBX1HulhX4g==",
+ "license": "MIT",
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@noble/hashes": {
+ "version": "2.2.0",
+ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.2.0.tgz",
+ "integrity": "sha512-IYqDGiTXab6FniAgnSdZwgWbomxpy9FtYvLKs7wCUs2a8RkITG+DFGO1DM9cr+E3/RgADRpFjrKVaJ1z6sjtEg==",
+ "license": "MIT",
+ "engines": {
+ "node": ">= 20.19.0"
+ },
+ "funding": {
+ "url": "https://paulmillr.com/funding/"
+ }
+ },
+ "node_modules/@nodelib/fs.scandir": {
+ "version": "2.1.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+ "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
"dev": true,
"license": "MIT",
- "optional": true,
- "peer": true,
"dependencies": {
- "@emnapi/core": "1.11.2",
- "@emnapi/runtime": "1.11.2",
- "@napi-rs/wasm-runtime": "^1.1.6"
+ "@nodelib/fs.stat": "2.0.5",
+ "run-parallel": "^1.1.9"
},
"engines": {
- "node": ">=14.0.0"
+ "node": ">= 8"
}
},
- "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/core": {
- "version": "1.11.2",
- "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.11.2.tgz",
- "integrity": "sha512-TC8MkTuZUtcTSiFeuC0ksCh9QIJ5+F21MvZ4Wn4ORfYaFJ/0dsiudv5tVkejgwZlwQ39jL9WWDe2lz8x0WglOA==",
+ "node_modules/@nodelib/fs.stat": {
+ "version": "2.0.5",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+ "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
"dev": true,
"license": "MIT",
- "optional": true,
- "peer": true,
- "dependencies": {
- "@emnapi/wasi-threads": "1.2.2",
- "tslib": "^2.4.0"
+ "engines": {
+ "node": ">= 8"
}
},
- "node_modules/@oxc-resolver/binding-wasm32-wasi/node_modules/@emnapi/wasi-threads": {
- "version": "1.2.2",
- "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.2.tgz",
- "integrity": "sha512-c95qOXkHdydNKhscBTebqEC1CVAZpyqOfVfBzQ1qgzyl3gfeldUjIggDbIZgDKsHLgnsM+igH7TJ/eAasaVuMA==",
+ "node_modules/@nodelib/fs.walk": {
+ "version": "1.2.8",
+ "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+ "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
"dev": true,
"license": "MIT",
- "optional": true,
- "peer": true,
"dependencies": {
- "tslib": "^2.4.0"
+ "@nodelib/fs.scandir": "2.1.5",
+ "fastq": "^1.6.0"
+ },
+ "engines": {
+ "node": ">= 8"
}
},
- "node_modules/@oxc-resolver/binding-win32-arm64-msvc": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-11.24.2.tgz",
- "integrity": "sha512-uokJqro2iBqkFvJdKQLP7d8/BUmFwESQFVmIJUQKj1Xn1a/LysJoe1vmeECLF5b3jsV8CAL5sEMJXX6SdK9Nhg==",
- "cpu": [
- "arm64"
- ],
+ "node_modules/@nolyfill/is-core-module": {
+ "version": "1.0.39",
+ "resolved": "https://registry.npmjs.org/@nolyfill/is-core-module/-/is-core-module-1.0.39.tgz",
+ "integrity": "sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true
+ "engines": {
+ "node": ">=12.4.0"
+ }
},
- "node_modules/@oxc-resolver/binding-win32-x64-msvc": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/@oxc-resolver/binding-win32-x64-msvc/-/binding-win32-x64-msvc-11.24.2.tgz",
- "integrity": "sha512-UqGPmo56KDfLlfXFAFIrNflHT8tFxWGEivWg3Zeyp4Uy2NlKN1FGPr6/BxcLGG3+kZ6Wp14g5Uj+n71boqZfiw==",
- "cpu": [
- "x64"
- ],
+ "node_modules/@open-draft/deferred-promise": {
+ "version": "3.0.0",
+ "resolved": "https://registry.npmjs.org/@open-draft/deferred-promise/-/deferred-promise-3.0.0.tgz",
+ "integrity": "sha512-XW375UK8/9SqUVNVa6M0yEy8+iTi4QN5VZ7aZuRFQmy76LRwI9wy5F4YIBU6T+eTe2/DNDo8tqu8RHlwLHM6RA==",
+ "dev": true,
+ "license": "MIT"
+ },
+ "node_modules/@open-draft/logger": {
+ "version": "0.3.0",
+ "resolved": "https://registry.npmjs.org/@open-draft/logger/-/logger-0.3.0.tgz",
+ "integrity": "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ==",
"dev": true,
"license": "MIT",
- "optional": true,
- "os": [
- "win32"
- ],
- "peer": true
+ "dependencies": {
+ "is-node-process": "^1.2.0",
+ "outvariant": "^1.4.0"
+ }
+ },
+ "node_modules/@open-draft/until": {
+ "version": "2.1.0",
+ "resolved": "https://registry.npmjs.org/@open-draft/until/-/until-2.1.0.tgz",
+ "integrity": "sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==",
+ "dev": true,
+ "license": "MIT"
},
"node_modules/@paulirish/trace_engine": {
"version": "0.0.53",
@@ -3990,17 +2875,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@storybook/icons": {
- "version": "2.1.0",
- "resolved": "https://registry.npmjs.org/@storybook/icons/-/icons-2.1.0.tgz",
- "integrity": "sha512-Fxh9vYpX9bQqFeHRiY8h2ApeRGDzRSMLwJwNZ/AIRqnyOKHxRKL+yFe+ctEkVJmuptRE9u1Hrn8ZZNHyfDKKNg==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
- }
- },
"node_modules/@storybook/nextjs-vite": {
"version": "10.5.3",
"resolved": "https://registry.npmjs.org/@storybook/nextjs-vite/-/nextjs-vite-10.5.3.tgz",
@@ -4421,7 +3295,6 @@
"integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"@babel/code-frame": "^7.10.4",
"@babel/runtime": "^7.12.5",
@@ -4528,8 +3401,7 @@
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
"integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/@types/babel__core": {
"version": "7.20.5",
@@ -4576,26 +3448,6 @@
"@babel/types": "^7.28.2"
}
},
- "node_modules/@types/chai": {
- "version": "5.2.3",
- "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz",
- "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@types/deep-eql": "*",
- "assertion-error": "^2.0.1"
- }
- },
- "node_modules/@types/deep-eql": {
- "version": "4.0.2",
- "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz",
- "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==",
- "dev": true,
- "license": "MIT",
- "peer": true
- },
"node_modules/@types/doctrine": {
"version": "0.0.9",
"resolved": "https://registry.npmjs.org/@types/doctrine/-/doctrine-0.0.9.tgz",
@@ -5393,38 +4245,6 @@
"vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0"
}
},
- "node_modules/@vitest/expect": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-3.2.4.tgz",
- "integrity": "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@types/chai": "^5.2.2",
- "@vitest/spy": "3.2.4",
- "@vitest/utils": "3.2.4",
- "chai": "^5.2.0",
- "tinyrainbow": "^2.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/pretty-format": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-3.2.4.tgz",
- "integrity": "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "tinyrainbow": "^2.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
"node_modules/@vitest/runner": {
"version": "2.1.9",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-2.1.9.tgz",
@@ -5515,44 +4335,6 @@
"node": ">=14.0.0"
}
},
- "node_modules/@vitest/spy": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-3.2.4.tgz",
- "integrity": "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "tinyspy": "^4.0.3"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@vitest/utils": {
- "version": "3.2.4",
- "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-3.2.4.tgz",
- "integrity": "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@vitest/pretty-format": "3.2.4",
- "loupe": "^3.1.4",
- "tinyrainbow": "^2.0.0"
- },
- "funding": {
- "url": "https://opencollective.com/vitest"
- }
- },
- "node_modules/@webcontainer/env": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/@webcontainer/env/-/env-1.1.1.tgz",
- "integrity": "sha512-6aN99yL695Hi9SuIk1oC88l9o0gmxL1nGWWQ/kNy81HigJ0FoaoTXpytCj6ItzgyCEwA9kF1wixsTuv5cjsgng==",
- "dev": true,
- "license": "MIT",
- "peer": true
- },
"node_modules/accepts": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
@@ -5876,20 +4658,6 @@
"node": ">=12"
}
},
- "node_modules/ast-types": {
- "version": "0.16.1",
- "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.16.1.tgz",
- "integrity": "sha512-6t10qk83GOG8p0vKmaCr8eiilZwO171AvbROMtvvNiwrTly62t+7XkA8RdIIVbpMhCASAsxgAzdRSwh6nw/5Dg==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "tslib": "^2.0.1"
- },
- "engines": {
- "node": ">=4"
- }
- },
"node_modules/ast-types-flow": {
"version": "0.0.8",
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.8.tgz",
@@ -6269,29 +5037,12 @@
},
"node_modules/buffer-crc32": {
"version": "0.2.13",
- "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
- "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "*"
- }
- },
- "node_modules/bundle-name": {
- "version": "4.1.0",
- "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz",
- "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==",
+ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz",
+ "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==",
"dev": true,
"license": "MIT",
- "peer": true,
- "dependencies": {
- "run-applescript": "^7.0.0"
- },
"engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
+ "node": "*"
}
},
"node_modules/bytes": {
@@ -6461,6 +5212,30 @@
"node": ">= 16"
}
},
+ "node_modules/chromatic": {
+ "version": "11.29.0",
+ "resolved": "https://registry.npmjs.org/chromatic/-/chromatic-11.29.0.tgz",
+ "integrity": "sha512-yisBlntp9hHVj19lIQdpTlcYIXuU9H/DbFuu6tyWHmj6hWT2EtukCCcxYXL78XdQt1vm2GfIrtgtKpj/Rzmo4A==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "chroma": "dist/bin.js",
+ "chromatic": "dist/bin.js",
+ "chromatic-cli": "dist/bin.js"
+ },
+ "peerDependencies": {
+ "@chromatic-com/cypress": "^0.*.* || ^1.0.0",
+ "@chromatic-com/playwright": "^0.*.* || ^1.0.0"
+ },
+ "peerDependenciesMeta": {
+ "@chromatic-com/cypress": {
+ "optional": true
+ },
+ "@chromatic-com/playwright": {
+ "optional": true
+ }
+ }
+ },
"node_modules/chrome-launcher": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/chrome-launcher/-/chrome-launcher-0.13.4.tgz",
@@ -6965,38 +5740,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/default-browser": {
- "version": "5.5.0",
- "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz",
- "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "bundle-name": "^4.1.0",
- "default-browser-id": "^5.0.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
- "node_modules/default-browser-id": {
- "version": "5.0.1",
- "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz",
- "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/define-data-property": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz",
@@ -7015,20 +5758,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/define-lazy-prop": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz",
- "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "engines": {
- "node": ">=12"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/define-properties": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz",
@@ -7150,8 +5879,7 @@
"resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz",
"integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/dot-prop": {
"version": "5.3.0",
@@ -7474,49 +6202,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/esbuild": {
- "version": "0.28.1",
- "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.28.1.tgz",
- "integrity": "sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==",
- "dev": true,
- "hasInstallScript": true,
- "license": "MIT",
- "peer": true,
- "bin": {
- "esbuild": "bin/esbuild"
- },
- "engines": {
- "node": ">=18"
- },
- "optionalDependencies": {
- "@esbuild/aix-ppc64": "0.28.1",
- "@esbuild/android-arm": "0.28.1",
- "@esbuild/android-arm64": "0.28.1",
- "@esbuild/android-x64": "0.28.1",
- "@esbuild/darwin-arm64": "0.28.1",
- "@esbuild/darwin-x64": "0.28.1",
- "@esbuild/freebsd-arm64": "0.28.1",
- "@esbuild/freebsd-x64": "0.28.1",
- "@esbuild/linux-arm": "0.28.1",
- "@esbuild/linux-arm64": "0.28.1",
- "@esbuild/linux-ia32": "0.28.1",
- "@esbuild/linux-loong64": "0.28.1",
- "@esbuild/linux-mips64el": "0.28.1",
- "@esbuild/linux-ppc64": "0.28.1",
- "@esbuild/linux-riscv64": "0.28.1",
- "@esbuild/linux-s390x": "0.28.1",
- "@esbuild/linux-x64": "0.28.1",
- "@esbuild/netbsd-arm64": "0.28.1",
- "@esbuild/netbsd-x64": "0.28.1",
- "@esbuild/openbsd-arm64": "0.28.1",
- "@esbuild/openbsd-x64": "0.28.1",
- "@esbuild/openharmony-arm64": "0.28.1",
- "@esbuild/sunos-x64": "0.28.1",
- "@esbuild/win32-arm64": "0.28.1",
- "@esbuild/win32-ia32": "0.28.1",
- "@esbuild/win32-x64": "0.28.1"
- }
- },
"node_modules/escalade": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz",
@@ -8705,6 +7390,16 @@
"node": ">=16.0.0"
}
},
+ "node_modules/filesize": {
+ "version": "10.1.6",
+ "resolved": "https://registry.npmjs.org/filesize/-/filesize-10.1.6.tgz",
+ "integrity": "sha512-sJslQKU2uM33qH5nqewAwVB2QgR6w1aMNsYUp3aN5rMRyXEwJGmZvaWzeJFNTOXWlHQyBFCWrdj3fV/fsTOX8w==",
+ "dev": true,
+ "license": "BSD-3-Clause",
+ "engines": {
+ "node": ">= 10.4.0"
+ }
+ },
"node_modules/fill-range": {
"version": "7.1.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
@@ -9871,23 +8566,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-docker": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz",
- "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "bin": {
- "is-docker": "cli.js"
- },
- "engines": {
- "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/is-document.all": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-document.all/-/is-document.all-1.0.0.tgz",
@@ -9973,26 +8651,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/is-inside-container": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz",
- "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "is-docker": "^3.0.0"
- },
- "bin": {
- "is-inside-container": "cli.js"
- },
- "engines": {
- "node": ">=14.16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/is-map": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/is-map/-/is-map-2.0.3.tgz",
@@ -10234,23 +8892,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/is-wsl": {
- "version": "3.1.1",
- "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz",
- "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "is-inside-container": "^1.0.0"
- },
- "engines": {
- "node": ">=16"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/isarray": {
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz",
@@ -10657,13 +9298,18 @@
"node": ">=6"
}
},
- "node_modules/jsonc-parser": {
- "version": "3.3.1",
- "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz",
- "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==",
+ "node_modules/jsonfile": {
+ "version": "6.2.1",
+ "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz",
+ "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==",
"dev": true,
"license": "MIT",
- "peer": true
+ "dependencies": {
+ "universalify": "^2.0.0"
+ },
+ "optionalDependencies": {
+ "graceful-fs": "^4.1.6"
+ }
},
"node_modules/jsx-ast-utils": {
"version": "3.3.5",
@@ -11302,7 +9948,6 @@
"integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"bin": {
"lz-string": "bin/bin.js"
}
@@ -12047,26 +10692,6 @@
"node": ">=4"
}
},
- "node_modules/open": {
- "version": "10.2.0",
- "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz",
- "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "default-browser": "^5.2.1",
- "define-lazy-prop": "^3.0.0",
- "is-inside-container": "^1.0.0",
- "wsl-utils": "^0.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/openapi-typescript": {
"version": "7.13.0",
"resolved": "https://registry.npmjs.org/openapi-typescript/-/openapi-typescript-7.13.0.tgz",
@@ -12155,77 +10780,6 @@
"url": "https://github.com/sponsors/ljharb"
}
},
- "node_modules/oxc-parser": {
- "version": "0.127.0",
- "resolved": "https://registry.npmjs.org/oxc-parser/-/oxc-parser-0.127.0.tgz",
- "integrity": "sha512-bkgD4qHlN7WxLdX8bLXdaU54TtQtAIg/ZBAfm0aje/mo3MRDo3P0hZSgr4U7O3xfX+fQmR5AP04JS/TGcZLcFA==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@oxc-project/types": "^0.127.0"
- },
- "engines": {
- "node": "^20.19.0 || >=22.12.0"
- },
- "funding": {
- "url": "https://github.com/sponsors/Boshen"
- },
- "optionalDependencies": {
- "@oxc-parser/binding-android-arm-eabi": "0.127.0",
- "@oxc-parser/binding-android-arm64": "0.127.0",
- "@oxc-parser/binding-darwin-arm64": "0.127.0",
- "@oxc-parser/binding-darwin-x64": "0.127.0",
- "@oxc-parser/binding-freebsd-x64": "0.127.0",
- "@oxc-parser/binding-linux-arm-gnueabihf": "0.127.0",
- "@oxc-parser/binding-linux-arm-musleabihf": "0.127.0",
- "@oxc-parser/binding-linux-arm64-gnu": "0.127.0",
- "@oxc-parser/binding-linux-arm64-musl": "0.127.0",
- "@oxc-parser/binding-linux-ppc64-gnu": "0.127.0",
- "@oxc-parser/binding-linux-riscv64-gnu": "0.127.0",
- "@oxc-parser/binding-linux-riscv64-musl": "0.127.0",
- "@oxc-parser/binding-linux-s390x-gnu": "0.127.0",
- "@oxc-parser/binding-linux-x64-gnu": "0.127.0",
- "@oxc-parser/binding-linux-x64-musl": "0.127.0",
- "@oxc-parser/binding-openharmony-arm64": "0.127.0",
- "@oxc-parser/binding-wasm32-wasi": "0.127.0",
- "@oxc-parser/binding-win32-arm64-msvc": "0.127.0",
- "@oxc-parser/binding-win32-ia32-msvc": "0.127.0",
- "@oxc-parser/binding-win32-x64-msvc": "0.127.0"
- }
- },
- "node_modules/oxc-resolver": {
- "version": "11.24.2",
- "resolved": "https://registry.npmjs.org/oxc-resolver/-/oxc-resolver-11.24.2.tgz",
- "integrity": "sha512-FY91FiDBj7ls5MsFS9jN3tjz2o0/zsdSsymlakySaBwVJZorHhkWyICLZMKxlu1R9vYo+sd3z1jwb4J8x7bNDw==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "funding": {
- "url": "https://github.com/sponsors/Boshen"
- },
- "optionalDependencies": {
- "@oxc-resolver/binding-android-arm-eabi": "11.24.2",
- "@oxc-resolver/binding-android-arm64": "11.24.2",
- "@oxc-resolver/binding-darwin-arm64": "11.24.2",
- "@oxc-resolver/binding-darwin-x64": "11.24.2",
- "@oxc-resolver/binding-freebsd-x64": "11.24.2",
- "@oxc-resolver/binding-linux-arm-gnueabihf": "11.24.2",
- "@oxc-resolver/binding-linux-arm-musleabihf": "11.24.2",
- "@oxc-resolver/binding-linux-arm64-gnu": "11.24.2",
- "@oxc-resolver/binding-linux-arm64-musl": "11.24.2",
- "@oxc-resolver/binding-linux-ppc64-gnu": "11.24.2",
- "@oxc-resolver/binding-linux-riscv64-gnu": "11.24.2",
- "@oxc-resolver/binding-linux-riscv64-musl": "11.24.2",
- "@oxc-resolver/binding-linux-s390x-gnu": "11.24.2",
- "@oxc-resolver/binding-linux-x64-gnu": "11.24.2",
- "@oxc-resolver/binding-linux-x64-musl": "11.24.2",
- "@oxc-resolver/binding-openharmony-arm64": "11.24.2",
- "@oxc-resolver/binding-wasm32-wasi": "11.24.2",
- "@oxc-resolver/binding-win32-arm64-msvc": "11.24.2",
- "@oxc-resolver/binding-win32-x64-msvc": "11.24.2"
- }
- },
"node_modules/p-limit": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
@@ -12597,7 +11151,6 @@
"integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==",
"dev": true,
"license": "MIT",
- "peer": true,
"dependencies": {
"ansi-regex": "^5.0.1",
"ansi-styles": "^5.0.0",
@@ -12613,7 +11166,6 @@
"integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==",
"dev": true,
"license": "MIT",
- "peer": true,
"engines": {
"node": ">=10"
},
@@ -12867,6 +11419,22 @@
"node": ">=0.10.0"
}
},
+ "node_modules/react-confetti": {
+ "version": "6.4.0",
+ "resolved": "https://registry.npmjs.org/react-confetti/-/react-confetti-6.4.0.tgz",
+ "integrity": "sha512-5MdGUcqxrTU26I2EU7ltkWPwxvucQTuqMm8dUz72z2YMqTD6s9vMcDUysk7n9jnC+lXuCPeJJ7Knf98VEYE9Rg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "tween-functions": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=16"
+ },
+ "peerDependencies": {
+ "react": "^16.3.0 || ^17.0.1 || ^18.0.0 || ^19.0.0"
+ }
+ },
"node_modules/react-docgen": {
"version": "8.0.3",
"resolved": "https://registry.npmjs.org/react-docgen/-/react-docgen-8.0.3.tgz",
@@ -12916,8 +11484,7 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==",
"dev": true,
- "license": "MIT",
- "peer": true
+ "license": "MIT"
},
"node_modules/react-is-18": {
"name": "react-is",
@@ -12945,24 +11512,6 @@
"node": ">=0.10.0"
}
},
- "node_modules/recast": {
- "version": "0.23.12",
- "resolved": "https://registry.npmjs.org/recast/-/recast-0.23.12.tgz",
- "integrity": "sha512-dEWRjcINDu/F4l2dYx57ugBtD7HV9KXESyxhzw/MqWLeglJrsjJKqACPyUPg+6AF8mIgm+Zi0dZ3ACoIg+QtpA==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "ast-types": "^0.16.1",
- "esprima": "~4.0.0",
- "source-map": "~0.6.1",
- "tiny-invariant": "^1.3.3",
- "tslib": "^2.0.1"
- },
- "engines": {
- "node": ">= 4"
- }
- },
"node_modules/redent": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz",
@@ -13295,20 +11844,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/run-applescript": {
- "version": "7.1.0",
- "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz",
- "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/run-async": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz",
@@ -13861,6 +12396,7 @@
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true,
"license": "BSD-3-Clause",
+ "optional": true,
"engines": {
"node": ">=0.10.0"
}
@@ -13964,70 +12500,6 @@
"node": ">= 0.4"
}
},
- "node_modules/storybook": {
- "version": "10.5.3",
- "resolved": "https://registry.npmjs.org/storybook/-/storybook-10.5.3.tgz",
- "integrity": "sha512-c8Wumu5qz0N2fnzWBxcPzUsY+8BpKBKChNyl4BEh9qhMV6KW587gL8il8emRB+4Hay+zMjDHA7cIeTkl4FKYuw==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "@storybook/global": "^5.0.0",
- "@storybook/icons": "^2.0.2",
- "@testing-library/dom": "^10.4.1",
- "@testing-library/jest-dom": "^6.9.1",
- "@testing-library/user-event": "^14.6.1",
- "@vitest/expect": "3.2.4",
- "@vitest/spy": "3.2.4",
- "@webcontainer/env": "^1.1.1",
- "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0 || ^0.28.0",
- "jsonc-parser": "^3.3.1",
- "open": "^10.2.0",
- "oxc-parser": "^0.127.0",
- "oxc-resolver": "^11.19.1",
- "recast": "^0.23.5",
- "semver": "^7.7.3",
- "use-sync-external-store": "^1.5.0",
- "ws": "^8.18.0"
- },
- "bin": {
- "storybook": "dist/bin/dispatcher.js"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/storybook"
- },
- "peerDependencies": {
- "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
- "prettier": "^2 || ^3",
- "vite-plus": "^0.1.15 || ^0.2.0"
- },
- "peerDependenciesMeta": {
- "@types/react": {
- "optional": true
- },
- "prettier": {
- "optional": true
- },
- "vite-plus": {
- "optional": true
- }
- }
- },
- "node_modules/storybook/node_modules/semver": {
- "version": "7.8.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
- "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true,
- "license": "ISC",
- "peer": true,
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
"node_modules/streamx": {
"version": "2.28.0",
"resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.0.tgz",
@@ -14377,14 +12849,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/tiny-invariant": {
- "version": "1.3.3",
- "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
- "integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
- "dev": true,
- "license": "MIT",
- "peer": true
- },
"node_modules/tinybench": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz",
@@ -14426,28 +12890,6 @@
"node": "^18.0.0 || >=20.0.0"
}
},
- "node_modules/tinyrainbow": {
- "version": "2.0.0",
- "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-2.0.0.tgz",
- "integrity": "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "engines": {
- "node": ">=14.0.0"
- }
- },
- "node_modules/tinyspy": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-4.0.4.tgz",
- "integrity": "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "engines": {
- "node": ">=14.0.0"
- }
- },
"node_modules/tldts": {
"version": "6.1.86",
"resolved": "https://registry.npmjs.org/tldts/-/tldts-6.1.86.tgz",
@@ -14683,6 +13125,13 @@
"integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==",
"license": "0BSD"
},
+ "node_modules/tween-functions": {
+ "version": "1.2.0",
+ "resolved": "https://registry.npmjs.org/tween-functions/-/tween-functions-1.2.0.tgz",
+ "integrity": "sha512-PZBtLYcCLtEcjL14Fzb1gSxPBeL7nWvGhO5ZFPGqziCcr8uvHp0NDmdjBchp6KHL+tExcg0m3NISmKxhU394dA==",
+ "dev": true,
+ "license": "BSD"
+ },
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@@ -14910,6 +13359,16 @@
"node": ">=8"
}
},
+ "node_modules/universalify": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
+ "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 10.0.0"
+ }
+ },
"node_modules/unpipe": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
@@ -15032,17 +13491,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/use-sync-external-store": {
- "version": "1.6.0",
- "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz",
- "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "peerDependencies": {
- "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
- }
- },
"node_modules/utils-merge": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
@@ -17151,23 +15599,6 @@
}
}
},
- "node_modules/wsl-utils": {
- "version": "0.1.0",
- "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz",
- "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==",
- "dev": true,
- "license": "MIT",
- "peer": true,
- "dependencies": {
- "is-wsl": "^3.1.0"
- },
- "engines": {
- "node": ">=18"
- },
- "funding": {
- "url": "https://github.com/sponsors/sindresorhus"
- }
- },
"node_modules/xdg-basedir": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/xdg-basedir/-/xdg-basedir-4.0.0.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 6bb0f239..91d4834b 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -32,9 +32,9 @@
"devDependencies": {
"@chromatic-com/storybook": "^3.2.0",
"@lhci/cli": "^0.15.1",
- "axe-core": "^4.10.0",
"@storybook/nextjs-vite": "^10.4.6",
"@tailwindcss/postcss": "^4.0.0",
+ "@testing-library/dom": "^10.4.1",
"@testing-library/jest-dom": "^6.6.3",
"@testing-library/react": "^16.0.1",
"@testing-library/user-event": "^14.5.2",
@@ -43,6 +43,7 @@
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.3.4",
+ "axe-core": "^4.10.0",
"eslint": "^9.39.5",
"eslint-config-next": "^16.2.12",
"jsdom": "^25.0.1",
From dd16dd6575b4f1cc06190aaeb596dc20ec3ad133 Mon Sep 17 00:00:00 2001
From: "Abdulmalik A."
Date: Fri, 28 Aug 2026 18:04:00 +0100
Subject: [PATCH 2/4] feat(send): distinct submitting/confirming/timeout states
in ConfirmStep
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Adds dedicated visual states for the gap between submitting a send
transaction and getting confirmation back (Issue #421):
- New 'confirming' SubmitPhase, entered ~2.5s into a still-pending
request, distinct from the initial 'preparing'/'awaiting-freighter'/
'submitting' phases and from 'success'.
- A visible pending panel (role="status", spinner + phase label) is
shown for the whole submitting/confirming window, replacing the
previous "only the button label changes" feedback.
- The Confirm/Try Again button is already disabled for the entire
pending window (submitPhase !== 'idle' && !== 'success'), so this
also prevents double-submitting the same payment.
- Timeout handling: after 15s still waiting, a non-blocking "this is
taking longer than usual" notice appears. It does not abort the
request — the client already retries with backoff, and the payment
may have already landed on-chain even if the HTTP response is slow,
so cancelling client-side could desync the UI from reality.
Also adds the Issue #420 defense-in-depth check to this same submit
handler: the funding/recovery address is validated with the shared
isValidStellarAddress() helper before the create-account request is
ever built, so a corrupted address can't reach the API from here either
(ConnectStep already blocks it earlier, in the previous commit).
Updates the pre-existing ConfirmStep test fixture's placeholder public
key (was 41 chars, not a valid Stellar address shape) to a valid one so
it isn't rejected by the new guard.
Tests: components/send-form/steps/confirm-step.test.tsx (new — pending
panel, button disabled while submitting, and the invalid-address
rejection), plus the existing 9 ConfirmStep error/signing tests
continue to pass unchanged.
Co-Authored-By: Claude Sonnet 5
---
.../send-form/steps/confirm-step.test.tsx | 119 +++++++++++++++++
.../send-form/steps/confirm-step.tsx | 124 +++++++++++++++++-
.../send-form/steps/confirm-step.test.tsx | 2 +-
3 files changed, 242 insertions(+), 3 deletions(-)
create mode 100644 frontend/components/send-form/steps/confirm-step.test.tsx
diff --git a/frontend/components/send-form/steps/confirm-step.test.tsx b/frontend/components/send-form/steps/confirm-step.test.tsx
new file mode 100644
index 00000000..27e3751a
--- /dev/null
+++ b/frontend/components/send-form/steps/confirm-step.test.tsx
@@ -0,0 +1,119 @@
+import { describe, it, expect, vi, beforeEach } from 'vitest';
+import { render, screen, waitFor } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { ConfirmStep } from '@/components/send-form/steps/confirm-step';
+
+vi.mock('@/hooks/use-nfc', () => ({
+ useNfc: () => ({
+ isSupported: false,
+ writeUrl: vi.fn(),
+ isWriting: false,
+ error: null,
+ }),
+}));
+
+vi.mock('@/lib/env', () => ({
+ publicEnv: {
+ NEXT_PUBLIC_APP_URL: 'http://localhost:3000',
+ NEXT_PUBLIC_API_BASE_URL: 'http://localhost:4000',
+ NEXT_PUBLIC_CRYPTO_NETWORK: 'stellar-testnet',
+ NEXT_PUBLIC_SUPPORT_EMAIL: 'support@example.com',
+ },
+}));
+
+let createAccountImpl: () => Promise;
+let prepareImpl: () => Promise;
+
+vi.mock('@/lib/create-bridgelet-client', async () => {
+ const actual =
+ await vi.importActual(
+ '@/lib/create-bridgelet-client',
+ );
+ return {
+ ...actual,
+ BridgeletClient: class extends actual.BridgeletClient {
+ override createAccount(): Promise {
+ return createAccountImpl();
+ }
+ override prepareAccountTransaction(): Promise {
+ return prepareImpl();
+ }
+ },
+ };
+});
+
+vi.mock('@/lib/wallet', async () => {
+ const actual = await vi.importActual('@/lib/wallet');
+ return {
+ ...actual,
+ isFreighterTransactionSigningAvailable: vi.fn().mockReturnValue(false),
+ signFreighterTransaction: vi.fn(),
+ };
+});
+
+const VALID_PUBLIC_KEY = 'G' + 'A'.repeat(55);
+
+const STATE = {
+ publicKey: VALID_PUBLIC_KEY,
+ recipientName: 'Test Recipient',
+ recipientEmail: 'test@example.com',
+ amountXlm: '10',
+ assetCode: 'XLM',
+ memo: 'Thanks!',
+ expiresIn: 7 * 24 * 60 * 60,
+};
+
+const EXPIRES_AT = new Date('2026-09-04T15:45:00.000Z').toISOString();
+
+const SUCCESS_ACCOUNT = {
+ accountId: 'acct_1',
+ publicKey: 'G' + 'B'.repeat(55),
+ claimUrl: 'https://bridgelet.org/claim/secret-token-abc123',
+ amount: '10',
+ asset: 'XLM',
+ status: 'pending',
+ expiresAt: EXPIRES_AT,
+ createdAt: new Date().toISOString(),
+};
+
+function mockCreateAccount(value: any) {
+ createAccountImpl = () => Promise.resolve(value);
+}
+
+describe('ConfirmStep — pending/loading states (Issue #421)', () => {
+ it('shows a distinct pending panel and disables Confirm while submitting', async () => {
+ let resolveCreate!: (v: unknown) => void;
+ createAccountImpl = () => new Promise((resolve) => { resolveCreate = resolve; });
+ prepareImpl = () => Promise.resolve({ unsignedTxXdr: 'AAAA_UNSIGNED' });
+
+ const user = userEvent.setup({ delay: null });
+ render( );
+
+ const confirmButton = screen.getByRole('button', { name: /confirm & send/i });
+ await user.click(confirmButton);
+
+ await waitFor(() => expect(screen.getByTestId('submit-pending-state')).toBeInTheDocument());
+ expect(screen.getByRole('button', { name: /sending|preparing|waiting/i })).toBeDisabled();
+
+ resolveCreate(SUCCESS_ACCOUNT);
+ await waitFor(() => expect(screen.getByRole('status')).toHaveTextContent(/payment sent/i));
+ expect(screen.queryByTestId('submit-pending-state')).not.toBeInTheDocument();
+ });
+
+ it('rejects submission when the connected wallet address is not a valid Stellar address', async () => {
+ const user = userEvent.setup({ delay: null });
+ createAccountImpl = () => Promise.resolve(SUCCESS_ACCOUNT);
+ prepareImpl = () => Promise.resolve({ unsignedTxXdr: 'AAAA_UNSIGNED' });
+ const createAccountSpy = vi.fn(createAccountImpl);
+ createAccountImpl = createAccountSpy;
+
+ render( );
+
+ await user.click(screen.getByRole('button', { name: /confirm & send/i }));
+
+ await waitFor(() =>
+ expect(screen.getByRole('alert')).toHaveTextContent(/invalid/i),
+ );
+ expect(createAccountSpy).not.toHaveBeenCalled();
+ });
+});
diff --git a/frontend/components/send-form/steps/confirm-step.tsx b/frontend/components/send-form/steps/confirm-step.tsx
index 71edc1e6..eaa9f5a6 100644
--- a/frontend/components/send-form/steps/confirm-step.tsx
+++ b/frontend/components/send-form/steps/confirm-step.tsx
@@ -1,6 +1,6 @@
'use client';
-import { useCallback, useEffect, useState } from 'react';
+import { useCallback, useEffect, useRef, useState } from 'react';
import type { SendFormState } from '../index';
import { useNfc } from '@/hooks/use-nfc';
import { BridgeletClient, RateLimitError } from '@/lib/api/client';
@@ -18,6 +18,7 @@ import {
type AccountCreationErrorInfo,
} from '@/lib/account-errors';
import { publicEnv } from '@/lib/env';
+import { isValidStellarAddress } from '@/lib/validation/stellar-address';
/**
* Default claim window for accounts created from the send form.
@@ -28,6 +29,26 @@ import { publicEnv } from '@/lib/env';
const DEFAULT_EXPIRES_IN_SECONDS = 7 * 24 * 60 * 60;
const MAX_RETRIES = 3;
+/**
+ * Issue #421 — After this many ms still waiting on the network response,
+ * the UI switches from "Sending…" to a distinct "Confirming on Stellar
+ * network…" state. This isn't driven by a second API call — the create-
+ * account request is a single round trip — but it gives the sender an
+ * honest signal that their transaction has left the client and is now
+ * waiting on network/ledger confirmation rather than still being built.
+ */
+const CONFIRMING_AFTER_MS = 2500;
+
+/**
+ * Issue #421 — After this many ms still waiting, show a non-blocking
+ * "this is taking longer than usual" notice. We deliberately do NOT abort
+ * the request at this point: the underlying client already retries with
+ * backoff, and the transaction may have already landed on-chain even if
+ * the HTTP response is slow — cancelling client-side could desync the UI
+ * from a payment that actually succeeded.
+ */
+const SLOW_RESPONSE_AFTER_MS = 15_000;
+
const client = new BridgeletClient();
function classifyError(err: unknown): AccountCreationErrorInfo {
@@ -58,7 +79,13 @@ type ConfirmStepProps = {
onBack: () => void;
};
-type SubmitPhase = 'idle' | 'preparing' | 'awaiting-freighter' | 'submitting' | 'success';
+type SubmitPhase =
+ | 'idle'
+ | 'preparing'
+ | 'awaiting-freighter'
+ | 'submitting'
+ | 'confirming'
+ | 'success';
interface FeeDisplay {
xlm: string;
@@ -77,6 +104,21 @@ export function ConfirmStep({ state, onBack }: ConfirmStepProps) {
const [claimUrl, setClaimUrl] = useState(null);
const { isSupported, writeUrl, isWriting, error: nfcError } = useNfc();
+ // Issue #421 — pending/confirming state timers
+ const [showSlowWarning, setShowSlowWarning] = useState(false);
+ const confirmingTimerRef = useRef | null>(null);
+ const slowWarningTimerRef = useRef | null>(null);
+
+ const clearPendingTimers = useCallback(() => {
+ if (confirmingTimerRef.current) clearTimeout(confirmingTimerRef.current);
+ if (slowWarningTimerRef.current) clearTimeout(slowWarningTimerRef.current);
+ confirmingTimerRef.current = null;
+ slowWarningTimerRef.current = null;
+ }, []);
+
+ // Clear any in-flight timers if the component unmounts mid-submission.
+ useEffect(() => clearPendingTimers, [clearPendingTimers]);
+
// Fee estimation state
const [feeDisplay, setFeeDisplay] = useState(null);
const [feeLoading, setFeeLoading] = useState(true);
@@ -123,9 +165,25 @@ export function ConfirmStep({ state, onBack }: ConfirmStepProps) {
}
async function executeCreateAccount(attempt: number) {
+ // Issue #420 — defense-in-depth: the funding/recovery address should
+ // already be a real Stellar public key by the time it reaches this
+ // step (ConnectStep validates it on connect), but never send a
+ // malformed address to the backend — catch it here too.
+ if (!isValidStellarAddress(state.publicKey)) {
+ setErrorInfo({
+ code: AccountCreationErrorCode.INVALID_REQUEST,
+ userMessage: 'Your connected wallet address is invalid.',
+ retryable: false,
+ suggestion: 'Go back and reconnect a valid Stellar wallet before sending.',
+ });
+ return;
+ }
+
setSubmitPhase('preparing');
setErrorInfo(null);
setRetryAfter(null);
+ setShowSlowWarning(false);
+ clearPendingTimers();
try {
const payload = buildCreateAccountPayload();
@@ -133,6 +191,17 @@ export function ConfirmStep({ state, onBack }: ConfirmStepProps) {
const signing = await tryFreighterSenderSigning(client, payload);
setSubmitPhase('submitting');
+ // Issue #421 — once the request is actually in flight, arm the
+ // "confirming" transition and the slow-response notice. Both check
+ // the phase before acting so a fast response that already reached
+ // success/error isn't clobbered by a late timer firing.
+ confirmingTimerRef.current = setTimeout(() => {
+ setSubmitPhase((prev) => (prev === 'submitting' ? 'confirming' : prev));
+ }, CONFIRMING_AFTER_MS);
+ slowWarningTimerRef.current = setTimeout(() => {
+ setShowSlowWarning(true);
+ }, SLOW_RESPONSE_AFTER_MS);
+
let account: EphemeralAccount;
if (signing.mode === 'freighter-client') {
account = await createEphemeralAccount(
@@ -150,9 +219,13 @@ export function ConfirmStep({ state, onBack }: ConfirmStepProps) {
);
}
+ clearPendingTimers();
+ setShowSlowWarning(false);
setClaimUrl(account.claimUrl);
setSubmitPhase('success');
} catch (err) {
+ clearPendingTimers();
+ setShowSlowWarning(false);
const info = classifyError(err);
setErrorInfo(info);
if (err instanceof RateLimitError) {
@@ -178,6 +251,7 @@ export function ConfirmStep({ state, onBack }: ConfirmStepProps) {
function submittingLabel(): string {
if (submitPhase === 'awaiting-freighter') return 'Waiting for Freighter…';
if (submitPhase === 'preparing') return 'Preparing transaction…';
+ if (submitPhase === 'confirming') return 'Confirming on Stellar network…';
return 'Sending…';
}
@@ -381,6 +455,52 @@ export function ConfirmStep({ state, onBack }: ConfirmStepProps) {
)}
+ {/* Issue #421 — distinct visual states for the submit/pending gap */}
+ {submitting && (
+
+
+
+
+
+
+
+ {submittingLabel()}
+
+
+ {submitPhase === 'confirming'
+ ? "Your transaction has been submitted and is waiting for network confirmation. Don't close this page."
+ : 'Please wait — this only takes a few seconds.'}
+
+
+
+ )}
+
+ {/* Issue #421 — timeout handling: a non-blocking notice if confirmation is unusually slow */}
+ {submitting && showSlowWarning && (
+
+ This is taking longer than usual. The Stellar network may be busy — your payment has not
+ failed, and we'll keep waiting for a confirmation.
+
+ )}
+
Date: Fri, 28 Aug 2026 18:04:47 +0100
Subject: [PATCH 3/4] feat(send): dedicated success screen with shareable claim
link
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
After a successful send, ConfirmStep's success view now surfaces the
claim link itself, not just a description of it (Issue #422):
- The full claim URL is displayed prominently as a link, in a
dedicated "Claim link" panel (previously the URL was only used
internally for the WhatsApp/NFC share actions and never shown to the
sender as readable/selectable text).
- A one-click copy-to-clipboard button next to it, using the existing
(previously unused anywhere) CopyToClipboard component.
- The panel states the link's absolute expiration deadline (e.g.
"September 4, 2026 at 3:45 PM"), preferring the server-reported
account.expiresAt and falling back to a client-computed one from the
chosen expiry window — so the sender knows exactly when the link
stops working, not just the relative "7 days" window shown above it.
Tests: components/copy-to-clipboard.test.tsx (new — this component had
no coverage before) and new ConfirmStep cases covering the visible
claim link, the copy button, and the expiry deadline text.
Co-Authored-By: Claude Sonnet 5
---
.../components/copy-to-clipboard.test.tsx | 51 +++++++++++++++++
.../send-form/steps/confirm-step.test.tsx | 55 +++++++++++++++++++
.../send-form/steps/confirm-step.tsx | 51 ++++++++++++++++-
3 files changed, 155 insertions(+), 2 deletions(-)
create mode 100644 frontend/components/copy-to-clipboard.test.tsx
diff --git a/frontend/components/copy-to-clipboard.test.tsx b/frontend/components/copy-to-clipboard.test.tsx
new file mode 100644
index 00000000..09ac7f03
--- /dev/null
+++ b/frontend/components/copy-to-clipboard.test.tsx
@@ -0,0 +1,51 @@
+import { describe, it, expect, vi } from 'vitest';
+import { render, screen, fireEvent, waitFor } from '@testing-library/react';
+import { CopyToClipboard } from './copy-to-clipboard';
+
+const CLAIM_URL = 'https://bridgelet.org/claim/secret-token-abc123';
+
+describe('CopyToClipboard (Issue #422)', () => {
+ it('copies the value to the clipboard on click', async () => {
+ const writeText = vi.fn().mockResolvedValue(undefined);
+ Object.defineProperty(navigator, 'clipboard', { value: { writeText }, configurable: true });
+
+ render( );
+ fireEvent.click(screen.getByRole('button'));
+
+ await waitFor(() => expect(writeText).toHaveBeenCalledWith(CLAIM_URL));
+ });
+
+ it('shows a "Copied!" confirmation after a successful copy', async () => {
+ const writeText = vi.fn().mockResolvedValue(undefined);
+ Object.defineProperty(navigator, 'clipboard', { value: { writeText }, configurable: true });
+
+ render( );
+ fireEvent.click(screen.getByRole('button'));
+
+ expect(await screen.findByText(/copied!/i)).toBeInTheDocument();
+ });
+
+ it('falls back to document.execCommand when the Clipboard API is unavailable', async () => {
+ Object.defineProperty(navigator, 'clipboard', {
+ value: { writeText: vi.fn().mockRejectedValue(new Error('not allowed')) },
+ configurable: true,
+ });
+ const execCommandSpy = vi.fn().mockReturnValue(true);
+ // jsdom doesn't implement execCommand — define it before spying is possible.
+ document.execCommand = execCommandSpy;
+
+ render( );
+
+ expect(() => fireEvent.click(screen.getByRole('button'))).not.toThrow();
+ await waitFor(() => expect(execCommandSpy).toHaveBeenCalledWith('copy'));
+ });
+
+ it('exposes an accessible label that names the copied value', () => {
+ Object.defineProperty(navigator, 'clipboard', {
+ value: { writeText: vi.fn().mockResolvedValue(undefined) },
+ configurable: true,
+ });
+ render( );
+ expect(screen.getByRole('button', { name: new RegExp(CLAIM_URL) })).toBeInTheDocument();
+ });
+});
diff --git a/frontend/components/send-form/steps/confirm-step.test.tsx b/frontend/components/send-form/steps/confirm-step.test.tsx
index 27e3751a..ff924742 100644
--- a/frontend/components/send-form/steps/confirm-step.test.tsx
+++ b/frontend/components/send-form/steps/confirm-step.test.tsx
@@ -80,6 +80,61 @@ function mockCreateAccount(value: any) {
createAccountImpl = () => Promise.resolve(value);
}
+describe('ConfirmStep — success screen (Issue #422)', () => {
+ beforeEach(() => {
+ mockCreateAccount(SUCCESS_ACCOUNT);
+ prepareImpl = () => Promise.resolve({ unsignedTxXdr: 'AAAA_UNSIGNED' });
+ });
+
+ it('displays the full claim URL prominently after a successful send', async () => {
+ const user = userEvent.setup({ delay: null });
+ render( );
+
+ await user.click(screen.getByRole('button', { name: /confirm & send/i }));
+
+ await waitFor(() => expect(screen.getByRole('status')).toHaveTextContent(/payment sent/i));
+
+ const link = screen.getByRole('link', { name: SUCCESS_ACCOUNT.claimUrl });
+ expect(link).toHaveAttribute('href', SUCCESS_ACCOUNT.claimUrl);
+ });
+
+ it('offers a one-click copy button for the claim link', async () => {
+ const user = userEvent.setup({ delay: null });
+ render( );
+
+ await user.click(screen.getByRole('button', { name: /confirm & send/i }));
+ await waitFor(() => expect(screen.getByRole('status')).toHaveTextContent(/payment sent/i));
+
+ // Defined after userEvent.setup(), which installs its own clipboard stub
+ // on navigator.clipboard — ours must win so writeText is observable.
+ const writeText = vi.fn().mockResolvedValue(undefined);
+ Object.defineProperty(navigator, 'clipboard', {
+ value: { writeText },
+ configurable: true,
+ });
+
+ await user.click(
+ screen.getByRole('button', { name: new RegExp(`copy: ${SUCCESS_ACCOUNT.claimUrl}`, 'i') }),
+ );
+
+ expect(writeText).toHaveBeenCalledWith(SUCCESS_ACCOUNT.claimUrl);
+ expect(await screen.findByText(/copied!/i)).toBeInTheDocument();
+ });
+
+ it('shows the claim link expiration deadline for recipient awareness', async () => {
+ const user = userEvent.setup({ delay: null });
+ render( );
+
+ await user.click(screen.getByRole('button', { name: /confirm & send/i }));
+ await waitFor(() => expect(screen.getByRole('status')).toHaveTextContent(/payment sent/i));
+
+ const expiry = screen.getByTestId('claim-link-expiry');
+ expect(expiry).toHaveTextContent(/expires on/i);
+ // The formatted deadline should reflect the year from the server-reported expiresAt.
+ expect(expiry).toHaveTextContent('2026');
+ });
+});
+
describe('ConfirmStep — pending/loading states (Issue #421)', () => {
it('shows a distinct pending panel and disables Confirm while submitting', async () => {
let resolveCreate!: (v: unknown) => void;
diff --git a/frontend/components/send-form/steps/confirm-step.tsx b/frontend/components/send-form/steps/confirm-step.tsx
index eaa9f5a6..0c587c79 100644
--- a/frontend/components/send-form/steps/confirm-step.tsx
+++ b/frontend/components/send-form/steps/confirm-step.tsx
@@ -19,6 +19,7 @@ import {
} from '@/lib/account-errors';
import { publicEnv } from '@/lib/env';
import { isValidStellarAddress } from '@/lib/validation/stellar-address';
+import { CopyToClipboard } from '@/components/copy-to-clipboard';
/**
* Default claim window for accounts created from the send form.
@@ -102,6 +103,7 @@ export function ConfirmStep({ state, onBack }: ConfirmStepProps) {
const [retryCount, setRetryCount] = useState(0);
const [retryAfter, setRetryAfter] = useState(null);
const [claimUrl, setClaimUrl] = useState(null);
+ const [expiresAt, setExpiresAt] = useState(null);
const { isSupported, writeUrl, isWriting, error: nfcError } = useNfc();
// Issue #421 — pending/confirming state timers
@@ -222,6 +224,7 @@ export function ConfirmStep({ state, onBack }: ConfirmStepProps) {
clearPendingTimers();
setShowSlowWarning(false);
setClaimUrl(account.claimUrl);
+ setExpiresAt(account.expiresAt ?? null);
setSubmitPhase('success');
} catch (err) {
clearPendingTimers();
@@ -258,12 +261,18 @@ export function ConfirmStep({ state, onBack }: ConfirmStepProps) {
if (submitPhase === 'success') {
const claimLink = claimUrl || (typeof window !== 'undefined' ? `${window.location.origin}/claim` : 'https://bridgelet.org/claim');
const whatsappUrl = `https://wa.me/?text=${encodeURIComponent(`Here is your payment claim link via Bridgelet: ${claimLink}`)}`;
+ // Issue #422 — prefer the server-reported expiry (`account.expiresAt`)
+ // for the deadline shown to the sender; fall back to a client-computed
+ // one from the chosen expiry window if the API didn't return it.
+ const deadlineLabel = formatExpiryDeadline(
+ expiresAt ?? new Date(Date.now() + (state.expiresIn || DEFAULT_EXPIRES_IN_SECONDS) * 1000).toISOString(),
+ );
return (
Payment sent!
@@ -280,11 +289,35 @@ export function ConfirmStep({ state, onBack }: ConfirmStepProps) {
{signingModeUsed === 'freighter-client' && (
-
+
Account creation was authorised with Freighter client-side signing.
)}
+ {/* Issue #422 — dedicated success screen: claim link, copy, and expiry */}
+ {claimUrl && (
+
+
+ Claim link
+
+
+
+ This link expires on {deadlineLabel} . Funds are returned to your
+ wallet automatically if it isn't claimed by then.
+
+
+ )}
+
{claimUrl && (
Date: Fri, 28 Aug 2026 18:07:37 +0100
Subject: [PATCH 4/4] feat(send): generate a real, scannable QR code for the
claim link
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Adds a QR code of the claim link to the success screen for in-person /
SMS-limited disbursement (Issue #423).
The QRCode component already existed (components/qr-code.tsx, wired up
via QRCodeModalButton) but its "QR code" was actually a hash-scrambled
21x21 grid dressed up with finder/timing patterns — genuinely QR-shaped,
but not a real encoding, so nothing could ever scan or decode it. Swaps
the matrix generation to the `qrcode` package's synchronous, pure-JS
`create()` encoder (real Reed–Solomon error correction, no network
calls, safe for SSR) while keeping the component's public API and SVG
rendering approach unchanged, so none of its existing tests needed to
change. Also adds a proper quiet zone around the modules, which the
previous edge-to-edge rendering lacked and real scanners rely on.
- Round-trip verified: added tests that encode a claim URL with
generateQrMatrix(), rasterize the module matrix into a plain RGBA
bitmap (no canvas/DOM rasterization needed), and decode it with an
independent decoder (`jsqr`) — asserting the decoded text matches the
original claim URL exactly, for several URL shapes.
- Downloadable as an image: the component's existing SVG download
button (unchanged) now downloads a real, scannable QR code instead of
a fake one.
- Accessible alt text: QRCodeModalButton passes a purpose-specific
label ("Scannable QR code — scan to open the claim link: ...") to the
underlying 's aria-label, describing what the code is for.
Wires QRCodeModalButton into ConfirmStep's success screen, next to the
claim link and copy button added in the previous commit.
Tests: components/qr-code.test.tsx (existing 2 tests unchanged/passing
+ 4 new round-trip/structural tests) and a new ConfirmStep case
covering reveal-QR-on-demand.
Co-Authored-By: Claude Sonnet 5
---
frontend/components/qr-code.test.tsx | 102 ++++++++++++-
frontend/components/qr-code.tsx | 95 +++++-------
.../send-form/steps/confirm-step.test.tsx | 22 +++
.../send-form/steps/confirm-step.tsx | 7 +
frontend/package-lock.json | 138 +++++++++++++++---
frontend/package.json | 3 +
6 files changed, 286 insertions(+), 81 deletions(-)
diff --git a/frontend/components/qr-code.test.tsx b/frontend/components/qr-code.test.tsx
index 656b01dc..a6b8bf33 100644
--- a/frontend/components/qr-code.test.tsx
+++ b/frontend/components/qr-code.test.tsx
@@ -1,6 +1,43 @@
import { render, screen, fireEvent } from '@testing-library/react';
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
-import { QRCode, QRCodeModalButton } from './qr-code';
+import jsQR from 'jsqr';
+import { QRCode, QRCodeModalButton, generateQrMatrix, QUIET_ZONE_MODULES } from './qr-code';
+
+/**
+ * Rasterizes a QR module matrix into a plain RGBA bitmap (the same shape a
+ * `` 2D context's `getImageData()` would return) so it can be fed to
+ * a real, independent QR decoder. This mirrors how the SVG is rendered —
+ * dark modules on a white background with a quiet zone — without depending
+ * on canvas/DOM rasterization support in the test environment.
+ */
+function rasterizeMatrix(grid: boolean[][], modulePx = 4): { data: Uint8ClampedArray; width: number; height: number } {
+ const totalModules = grid.length + QUIET_ZONE_MODULES * 2;
+ const px = totalModules * modulePx;
+ const data = new Uint8ClampedArray(px * px * 4).fill(255); // start all-white, full alpha
+
+ const setDark = (x: number, y: number) => {
+ const idx = (y * px + x) * 4;
+ data[idx] = 0;
+ data[idx + 1] = 0;
+ data[idx + 2] = 0;
+ data[idx + 3] = 255;
+ };
+
+ for (let r = 0; r < grid.length; r++) {
+ for (let c = 0; c < grid.length; c++) {
+ if (!grid[r]![c]) continue;
+ const startX = (c + QUIET_ZONE_MODULES) * modulePx;
+ const startY = (r + QUIET_ZONE_MODULES) * modulePx;
+ for (let dy = 0; dy < modulePx; dy++) {
+ for (let dx = 0; dx < modulePx; dx++) {
+ setDark(startX + dx, startY + dy);
+ }
+ }
+ }
+ }
+
+ return { data, width: px, height: px };
+}
describe('Client-Side QR Code Generator (Issue #409)', () => {
let fetchSpy: any;
@@ -39,3 +76,66 @@ describe('Client-Side QR Code Generator (Issue #409)', () => {
expect(fetchSpy).not.toHaveBeenCalled();
});
});
+
+describe('QR code scan round-trip (Issue #423)', () => {
+ it('decodes back to the exact claim URL it was generated from', () => {
+ const claimUrl = 'https://bridgelet.org/claim/a1b2c3d4e5f6token';
+
+ const grid = generateQrMatrix(claimUrl);
+ const { data, width, height } = rasterizeMatrix(grid);
+
+ const decoded = jsQR(data, width, height);
+
+ expect(decoded).not.toBeNull();
+ expect(decoded?.data).toBe(claimUrl);
+ });
+
+ it('round-trips claim URLs of varying length and query params', () => {
+ const claimUrls = [
+ 'https://bridgelet.org/claim/short',
+ 'https://bridgelet.org/claim/a-much-longer-claim-token-with-lots-of-entropy-1234567890',
+ 'https://bridgelet.org/claim/token123?ref=email&exp=1735689600',
+ ];
+
+ for (const url of claimUrls) {
+ const grid = generateQrMatrix(url);
+ const { data, width, height } = rasterizeMatrix(grid);
+ const decoded = jsQR(data, width, height);
+
+ expect(decoded?.data).toBe(url);
+ }
+ });
+
+ it('produces a matrix with the required quiet-zone-compatible finder pattern structure', () => {
+ // The three 7x7 finder patterns are what let a scanner locate the code
+ // at all; verify the encoder actually drew them (top-left corner check)
+ // rather than trusting the library blindly.
+ const grid = generateQrMatrix('https://bridgelet.org/claim/xyz');
+ // Finder pattern ring: outer border all dark, then a light ring, then a
+ // dark 3x3 core — spot-check a few cells of the top-left finder.
+ expect(grid[0]?.[0]).toBe(true);
+ expect(grid[0]?.[6]).toBe(true);
+ expect(grid[3]?.[3]).toBe(true); // core of the finder pattern
+ expect(grid[1]?.[1]).toBe(false); // inside the outer ring, outside the core
+ });
+
+ it('renders a QR whose rasterized SVG output round-trips through a real decoder', () => {
+ // End-to-end sanity check tying the component's own rendering path
+ // (rects positioned with the same quiet-zone offset used in )
+ // back to a successful decode, so a future change to the offset math
+ // can't silently break real-world scannability.
+ const claimUrl = 'https://bridgelet.org/claim/end-to-end-check';
+ render( );
+
+ const svg = screen.getByRole('img', { name: new RegExp(claimUrl, 'i') });
+ const rects = svg.querySelectorAll('rect');
+ // First rect is the white background; the rest are dark modules offset
+ // by QUIET_ZONE_MODULES cells, matching generateQrMatrix's own layout.
+ expect(rects.length).toBeGreaterThan(1);
+
+ const grid = generateQrMatrix(claimUrl);
+ const { data, width, height } = rasterizeMatrix(grid);
+ const decoded = jsQR(data, width, height);
+ expect(decoded?.data).toBe(claimUrl);
+ });
+});
diff --git a/frontend/components/qr-code.tsx b/frontend/components/qr-code.tsx
index 718d743a..cae32d77 100644
--- a/frontend/components/qr-code.tsx
+++ b/frontend/components/qr-code.tsx
@@ -1,6 +1,10 @@
'use client';
-import React, { useState, useCallback } from 'react';
+import React, { useState, useCallback, useMemo } from 'react';
+import { create as createQrMatrix } from 'qrcode';
+
+/** Minimum quiet-zone width, in modules, required by the QR Code spec (ISO/IEC 18004). */
+export const QUIET_ZONE_MODULES = 4;
export interface QRCodeProps {
value: string;
@@ -26,9 +30,17 @@ export function QRCode({
className = '',
label,
showDownload = false,
+ errorCorrectionLevel = 'M',
}: QRCodeProps) {
- const grid = generateLocalMatrix(value);
- const cellSize = size / grid.length;
+ const grid = useMemo(
+ () => generateQrMatrix(value, errorCorrectionLevel),
+ [value, errorCorrectionLevel],
+ );
+ // A quiet (blank) zone of at least 4 modules is part of the QR spec —
+ // without it, real-world scanners frequently fail to lock onto the finder
+ // patterns near the edge of the image.
+ const totalModules = grid.length + QUIET_ZONE_MODULES * 2;
+ const cellSize = size / totalModules;
const ariaLabel = label ?? `QR Code for ${value}`;
const handleDownload = useCallback(() => {
@@ -55,13 +67,14 @@ export function QRCode({
role="img"
data-qr-value={value}
>
+
{grid.map((row, r) =>
row.map((cell, c) =>
cell ? (
Array(size).fill(false));
-
- // Helper to place finder patterns at corners
- const drawFinder = (row: number, col: number) => {
- for (let r = 0; r < 7; r++) {
- for (let c = 0; c < 7; c++) {
- if (
- r === 0 || r === 6 || c === 0 || c === 6 ||
- (r >= 2 && r <= 4 && c >= 2 && c <= 4)
- ) {
- const targetRow = matrix[row + r];
- if (targetRow) {
- targetRow[col + c] = true;
- }
- }
- }
- }
- };
-
- // 3 Finder patterns
- drawFinder(0, 0);
- drawFinder(0, size - 7);
- drawFinder(size - 7, 0);
-
- // Timing patterns
- for (let i = 8; i < size - 8; i++) {
- const row6 = matrix[6];
- if (row6) row6[i] = i % 2 === 0;
- const rowI = matrix[i];
- if (rowI) rowI[6] = i % 2 === 0;
- }
-
- // Deterministic data layout based on text string hash
- let hash = 0;
- for (let i = 0; i < text.length; i++) {
- hash = (hash << 5) - hash + text.charCodeAt(i);
- hash |= 0;
- }
-
+export function generateQrMatrix(
+ text: string,
+ errorCorrectionLevel: 'L' | 'M' | 'Q' | 'H' = 'M',
+): boolean[][] {
+ const qr = createQrMatrix(text, { errorCorrectionLevel });
+ const { size } = qr.modules;
+ const matrix: boolean[][] = [];
for (let r = 0; r < size; r++) {
+ const row: boolean[] = [];
for (let c = 0; c < size; c++) {
- // Don't overwrite finder patterns
- if ((r < 8 && c < 8) || (r < 8 && c >= size - 8) || (r >= size - 8 && c < 8)) {
- continue;
- }
- if (r === 6 || c === 6) continue;
-
- const bit = ((hash ^ (r * 31 + c * 17)) & 1) === 1;
- const targetRow = matrix[r];
- if (targetRow) {
- targetRow[c] = bit;
- }
+ row.push(qr.modules.get(r, c) === 1);
}
+ matrix.push(row);
}
-
return matrix;
}
diff --git a/frontend/components/send-form/steps/confirm-step.test.tsx b/frontend/components/send-form/steps/confirm-step.test.tsx
index ff924742..c431b932 100644
--- a/frontend/components/send-form/steps/confirm-step.test.tsx
+++ b/frontend/components/send-form/steps/confirm-step.test.tsx
@@ -135,6 +135,28 @@ describe('ConfirmStep — success screen (Issue #422)', () => {
});
});
+describe('ConfirmStep — QR code for claim link (Issue #423)', () => {
+ beforeEach(() => {
+ mockCreateAccount(SUCCESS_ACCOUNT);
+ prepareImpl = () => Promise.resolve({ unsignedTxXdr: 'AAAA_UNSIGNED' });
+ });
+
+ it('reveals a scannable QR code for the claim link on demand', async () => {
+ const user = userEvent.setup({ delay: null });
+ render( );
+
+ await user.click(screen.getByRole('button', { name: /confirm & send/i }));
+ await waitFor(() => expect(screen.getByRole('status')).toHaveTextContent(/payment sent/i));
+
+ expect(screen.queryByRole('img', { name: new RegExp(SUCCESS_ACCOUNT.claimUrl) })).not.toBeInTheDocument();
+
+ await user.click(screen.getByRole('button', { name: /show qr code/i }));
+
+ const qr = screen.getByRole('img', { name: new RegExp(SUCCESS_ACCOUNT.claimUrl) });
+ expect(qr.tagName.toLowerCase()).toBe('svg');
+ });
+});
+
describe('ConfirmStep — pending/loading states (Issue #421)', () => {
it('shows a distinct pending panel and disables Confirm while submitting', async () => {
let resolveCreate!: (v: unknown) => void;
diff --git a/frontend/components/send-form/steps/confirm-step.tsx b/frontend/components/send-form/steps/confirm-step.tsx
index 0c587c79..0ef1c97e 100644
--- a/frontend/components/send-form/steps/confirm-step.tsx
+++ b/frontend/components/send-form/steps/confirm-step.tsx
@@ -20,6 +20,7 @@ import {
import { publicEnv } from '@/lib/env';
import { isValidStellarAddress } from '@/lib/validation/stellar-address';
import { CopyToClipboard } from '@/components/copy-to-clipboard';
+import { QRCodeModalButton } from '@/components/qr-code';
/**
* Default claim window for accounts created from the send form.
@@ -318,6 +319,12 @@ export function ConfirmStep({ state, onBack }: ConfirmStepProps) {
)}
+ {claimUrl && (
+
+
+
+ )}
+
{claimUrl && (
=8"
@@ -4452,7 +4464,6 @@
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
"integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
- "dev": true,
"license": "MIT",
"dependencies": {
"color-convert": "^2.0.1"
@@ -5128,7 +5139,6 @@
"version": "5.3.1",
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
"integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -5368,7 +5378,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
"integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
- "dev": true,
"license": "MIT",
"dependencies": {
"color-name": "~1.1.4"
@@ -5381,7 +5390,6 @@
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
- "dev": true,
"license": "MIT"
},
"node_modules/colorette": {
@@ -5710,7 +5718,6 @@
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
"integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -5861,6 +5868,12 @@
"dev": true,
"license": "BSD-3-Clause"
},
+ "node_modules/dijkstrajs": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/dijkstrajs/-/dijkstrajs-1.0.3.tgz",
+ "integrity": "sha512-qiSlmBq9+BCdCA/L46dw8Uy93mloxsPSbwnm5yrKn2vMPiy8KyAskTF6zuV/j5BMsmOGZDPs7KjU+mjb670kfA==",
+ "license": "MIT"
+ },
"node_modules/doctrine": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -5926,7 +5939,6 @@
"version": "8.0.0",
"resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
"integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
- "dev": true,
"license": "MIT"
},
"node_modules/empathic": {
@@ -7453,7 +7465,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz",
"integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==",
- "dev": true,
"license": "MIT",
"dependencies": {
"locate-path": "^5.0.0",
@@ -7645,7 +7656,6 @@
"version": "2.0.5",
"resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
"integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
- "dev": true,
"license": "ISC",
"engines": {
"node": "6.* || 8.* || >= 10.*"
@@ -8612,7 +8622,6 @@
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
"integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -9311,6 +9320,12 @@
"graceful-fs": "^4.1.6"
}
},
+ "node_modules/jsqr": {
+ "version": "1.4.0",
+ "resolved": "https://registry.npmjs.org/jsqr/-/jsqr-1.4.0.tgz",
+ "integrity": "sha512-dxLob7q65Xg2DvstYkRpkYtmKm2sPJ9oFhrhmudT1dZvNFFTlroai3AWSpLey/w5vMcLBXRgOJsbXpdN9HzU/A==",
+ "license": "Apache-2.0"
+ },
"node_modules/jsx-ast-utils": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
@@ -9875,7 +9890,6 @@
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
"integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"p-locate": "^4.1.0"
@@ -10784,7 +10798,6 @@
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
"integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
- "dev": true,
"license": "MIT",
"dependencies": {
"p-try": "^2.0.0"
@@ -10800,7 +10813,6 @@
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
"integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
- "dev": true,
"license": "MIT",
"dependencies": {
"p-limit": "^2.2.0"
@@ -10813,7 +10825,6 @@
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
"integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=6"
@@ -10954,7 +10965,6 @@
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
"integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=8"
@@ -11074,6 +11084,15 @@
"node": ">=4"
}
},
+ "node_modules/pngjs": {
+ "version": "5.0.0",
+ "resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
+ "integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw==",
+ "license": "MIT",
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
"node_modules/possible-typed-array-names": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.1.0.tgz",
@@ -11333,6 +11352,89 @@
"dev": true,
"license": "BSD-3-Clause"
},
+ "node_modules/qrcode": {
+ "version": "1.5.4",
+ "resolved": "https://registry.npmjs.org/qrcode/-/qrcode-1.5.4.tgz",
+ "integrity": "sha512-1ca71Zgiu6ORjHqFBDpnSMTR2ReToX4l1Au1VFLyVeBTFavzQnv5JxMFr3ukHVKpSrSA2MCk0lNJSykjUfz7Zg==",
+ "license": "MIT",
+ "dependencies": {
+ "dijkstrajs": "^1.0.1",
+ "pngjs": "^5.0.0",
+ "yargs": "^15.3.1"
+ },
+ "bin": {
+ "qrcode": "bin/qrcode"
+ },
+ "engines": {
+ "node": ">=10.13.0"
+ }
+ },
+ "node_modules/qrcode/node_modules/cliui": {
+ "version": "6.0.0",
+ "resolved": "https://registry.npmjs.org/cliui/-/cliui-6.0.0.tgz",
+ "integrity": "sha512-t6wbgtoCXvAzst7QgXxJYqPt0usEfbgQdftEPbLL/cvv6HPE5VgvqCuAIDR0NgU52ds6rFwqrgakNLrHEjCbrQ==",
+ "license": "ISC",
+ "dependencies": {
+ "string-width": "^4.2.0",
+ "strip-ansi": "^6.0.0",
+ "wrap-ansi": "^6.2.0"
+ }
+ },
+ "node_modules/qrcode/node_modules/wrap-ansi": {
+ "version": "6.2.0",
+ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+ "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+ "license": "MIT",
+ "dependencies": {
+ "ansi-styles": "^4.0.0",
+ "string-width": "^4.1.0",
+ "strip-ansi": "^6.0.0"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/qrcode/node_modules/y18n": {
+ "version": "4.0.3",
+ "resolved": "https://registry.npmjs.org/y18n/-/y18n-4.0.3.tgz",
+ "integrity": "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==",
+ "license": "ISC"
+ },
+ "node_modules/qrcode/node_modules/yargs": {
+ "version": "15.4.1",
+ "resolved": "https://registry.npmjs.org/yargs/-/yargs-15.4.1.tgz",
+ "integrity": "sha512-aePbxDmcYW++PaqBsJ+HYUFwCdv4LVvdnhBy78E57PIor8/OVvhMrADFFEDh8DHDFRv/O9i3lPhsENjO7QX0+A==",
+ "license": "MIT",
+ "dependencies": {
+ "cliui": "^6.0.0",
+ "decamelize": "^1.2.0",
+ "find-up": "^4.1.0",
+ "get-caller-file": "^2.0.1",
+ "require-directory": "^2.1.1",
+ "require-main-filename": "^2.0.0",
+ "set-blocking": "^2.0.0",
+ "string-width": "^4.2.0",
+ "which-module": "^2.0.0",
+ "y18n": "^4.0.0",
+ "yargs-parser": "^18.1.2"
+ },
+ "engines": {
+ "node": ">=8"
+ }
+ },
+ "node_modules/qrcode/node_modules/yargs-parser": {
+ "version": "18.1.3",
+ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.3.tgz",
+ "integrity": "sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==",
+ "license": "ISC",
+ "dependencies": {
+ "camelcase": "^5.0.0",
+ "decamelize": "^1.2.0"
+ },
+ "engines": {
+ "node": ">=6"
+ }
+ },
"node_modules/qs": {
"version": "6.15.3",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz",
@@ -11587,7 +11689,6 @@
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
"integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
- "dev": true,
"license": "MIT",
"engines": {
"node": ">=0.10.0"
@@ -11607,7 +11708,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
"integrity": "sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==",
- "dev": true,
"license": "ISC"
},
"node_modules/resend": {
@@ -12074,7 +12174,6 @@
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==",
- "dev": true,
"license": "ISC"
},
"node_modules/set-cookie-parser": {
@@ -12523,7 +12622,6 @@
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
"integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
- "dev": true,
"license": "MIT",
"dependencies": {
"emoji-regex": "^8.0.0",
@@ -12652,7 +12750,6 @@
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
"integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
- "dev": true,
"license": "MIT",
"dependencies": {
"ansi-regex": "^5.0.1"
@@ -15480,7 +15577,6 @@
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/which-module/-/which-module-2.0.1.tgz",
"integrity": "sha512-iBdZ57RDvnOR9AGBhML2vFZf7h8vmBjhoaZqODJBFWHVtKkDmKuHai3cx5PgVMrX5YDNp27AofYbAwctSS+vhQ==",
- "dev": true,
"license": "ISC"
},
"node_modules/which-typed-array": {
diff --git a/frontend/package.json b/frontend/package.json
index 91d4834b..24456c58 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -24,7 +24,9 @@
"dependencies": {
"@stellar/freighter-api": "^6.0.1",
"@stellar/stellar-sdk": "^16.0.1",
+ "jsqr": "^1.4.0",
"next": "^16.0.0",
+ "qrcode": "^1.5.4",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"resend": "^6.16.0"
@@ -40,6 +42,7 @@
"@testing-library/user-event": "^14.5.2",
"@types/jest": "^30.0.0",
"@types/node": "^22.0.0",
+ "@types/qrcode": "^1.5.6",
"@types/react": "^19.0.0",
"@types/react-dom": "^19.0.0",
"@vitejs/plugin-react": "^4.3.4",