From fed5ca9f70c50351f910bf04404953ac89761502 Mon Sep 17 00:00:00 2001 From: CodedColder Date: Fri, 23 Oct 2026 07:13:20 +0100 Subject: [PATCH 1/2] fixed all issues --- README.md | 4 +- docs/I18N.md | 95 ++ messages/en.json | 27 + messages/es.json | 27 + next.config.ts | 5 +- package-lock.json | 1088 +++++++++++++++-- package.json | 1 + src/app/(main)/dashboard/page.test.tsx | 14 + src/app/(main)/ngos/[id]/page.tsx | 32 +- src/app/(main)/page.tsx | 16 +- src/app/layout.tsx | 15 +- .../common/ConnectWalletPrompt.test.tsx | 2 + src/components/dashboard/StreamControls.tsx | 8 +- src/components/donate/CreateStreamForm.tsx | 9 +- src/components/landing/HowItWorks.tsx | 28 +- src/components/layout/Header.test.tsx | 8 + src/components/layout/Header.tsx | 6 + src/components/layout/LocaleSwitcher.tsx | 43 + src/components/ngoAdmin/WithdrawalHistory.tsx | 8 +- .../streams/StreamDetailsModal.test.tsx | 10 +- src/components/streams/StreamDetailsModal.tsx | 33 +- .../wallet/ConnectWalletButton.test.tsx | 4 + src/components/wallet/ConnectWalletButton.tsx | 5 +- src/i18n/config.ts | 24 + src/i18n/request.ts | 22 + src/lib/adminApi.ts | 3 +- src/lib/api.test.ts | 24 +- src/lib/api.ts | 32 +- src/lib/config.ts | 3 + src/lib/csv.ts | 9 +- src/lib/durations.ts | 8 + src/lib/format.ts | 21 + src/middleware.ts | 3 +- 33 files changed, 1400 insertions(+), 237 deletions(-) create mode 100644 docs/I18N.md create mode 100644 messages/en.json create mode 100644 messages/es.json create mode 100644 src/components/layout/LocaleSwitcher.tsx create mode 100644 src/i18n/config.ts create mode 100644 src/i18n/request.ts create mode 100644 src/lib/durations.ts diff --git a/README.md b/README.md index 2db3b46..2c1ef4a 100644 --- a/README.md +++ b/README.md @@ -11,6 +11,7 @@ platform for verified NGOs on Stellar. - Stellar SDK (`@stellar/stellar-sdk`) for Soroban contract calls - Vitest and Testing Library for unit and component tests - Playwright for end-to-end tests +- next-intl for i18n (scaffolding so far — see [docs/I18N.md](./docs/I18N.md)) See [docs/COMPONENTS.md](./docs/COMPONENTS.md) for a component tree of `src/components/` with a one-line description of each piece, @@ -25,7 +26,8 @@ the dark-mode strategy. See [docs/DATA_FETCHING.md](./docs/DATA_FETCHING.md) for server/client data loading, cache revalidation, polling intervals, and the known 1+N impact -fetch pattern. +fetch pattern, and [docs/I18N.md](./docs/I18N.md) for how translations are +wired up and how to add a language. ## Local development diff --git a/docs/I18N.md b/docs/I18N.md new file mode 100644 index 0000000..3e92470 --- /dev/null +++ b/docs/I18N.md @@ -0,0 +1,95 @@ +# Internationalization + +This is scaffolding, not a finished rollout: [next-intl](https://next-intl.dev) +is wired up, the landing page (`src/app/(main)/page.tsx` and +`src/components/landing/HowItWorks.tsx`) reads its copy from translation +files, and a locale switcher in the header lets you try it — but every other +page still has its English strings hard-coded. The goal here was to get the +plumbing in place once, so translating the rest of the app later means +extracting strings into `messages/*.json` page by page, not introducing the +library and its request-time wiring from scratch. + +## How it's wired up + +- **`src/i18n/config.ts`** — the list of supported locales (`LOCALES`), the + default (`DEFAULT_LOCALE`), and their display labels. +- **`src/i18n/request.ts`** — next-intl's per-request config. It reads the + `NEXT_LOCALE` cookie (falling back to `DEFAULT_LOCALE`) and loads that + locale's `messages/.json`. +- **`next.config.ts`** — wraps the config with `createNextIntlPlugin`, + pointing at `request.ts`. +- **`src/app/layout.tsx`** — resolves the locale and messages server-side + (`getLocale`/`getMessages`) and wraps the app in `NextIntlClientProvider`, + so both Server and Client Components can translate. +- **`messages/en.json` / `messages/es.json`** — the two locales that exist + today, namespaced by component (`HomePage`, `HowItWorks`, + `LocaleSwitcher`). +- **`src/components/layout/LocaleSwitcher.tsx`** — the switcher itself, in + the header. It writes the `NEXT_LOCALE` cookie and calls `router.refresh()` + — there's no `[locale]` route segment, so the URL doesn't change. + +## Why there's no locale-prefixed routing + +next-intl's full setup adds a `[locale]` segment above every route (so URLs +look like `/es/ngos/123`), plus its own routing middleware. This app already +has `src/middleware.ts` doing CSP/security headers and a route-group layout +(`src/app/(main)/`) added in a separate pass — layering locale-prefixed +routing on top of both would touch every route, every internal ``, and +the embed widget's URL contract (`docs/EMBED.md`), which NGOs have already +pasted into their own sites. That's a real migration, not scaffolding, so +it's deliberately left for whenever full i18n is actually prioritized. The +cookie-based approach here gets translations, a working switcher, and the +request-config plumbing in place without any of that. + +**Cost of that shortcut:** `request.ts` reads a cookie to resolve the +locale, and reading a cookie forces Next.js to render dynamically rather +than statically — since `NextIntlClientProvider` lives in the root layout, +this applies to every route, not just the translated ones. Before this +change, `next build` marked `/`, `/ngos`, and the other public routes as +static (`○`, with `/` and `/ngos` revalidating every 30s); after it, every +route is server-rendered per request (`ƒ`). Locale-prefixed routing (the +"real migration" above) would get static generation back, one prerendered +page per locale — that's the other reason it's the better long-term answer, +not just the URL structure. + +## Adding a language + +1. Add its code to `LOCALES` and a label to `LOCALE_LABELS` in + `src/i18n/config.ts`. +2. Copy `messages/en.json` to `messages/.json` and translate every + value (keep the keys and the `HowItWorks.steps` array shape identical — + `request.ts` loads whichever file matches the active locale by name, and + `HowItWorks` reads `steps` as a raw array via `t.raw('steps')`). +3. That's it — the switcher reads `LOCALES` directly, so the new language + appears there automatically. + +## Translating another component + +1. Add a namespace for it to every `messages/.json` file (match the + component's name, e.g. `"Footer": { "github": "GitHub" }`). +2. In a **Server Component**, call `const t = await getTranslations('Footer')` + from `next-intl/server` and use `t('github')` in place of the literal + string (see `src/app/(main)/page.tsx` or + `src/components/landing/HowItWorks.tsx`). +3. In a **Client Component** (one with `'use client'`), use + `const t = useTranslations('Footer')` from `next-intl` instead — same + `t('key')` call, no `await` (see `LocaleSwitcher.tsx`). +4. A list or other non-string value (like `HowItWorks.steps`) can't go + through `t()` directly — use `t.raw('key')` to get it back as plain JSON. + +## Testing a component that uses translations + +Rendering a component that calls `useLocale()`/`useTranslations()` outside +a `NextIntlClientProvider` throws. Either wrap it in one for the test, or — +the lighter option used in `Header.test.tsx` for `LocaleSwitcher` — mock +`next-intl` directly: + +```ts +vi.mock('next-intl', () => ({ + useLocale: () => 'en', + useTranslations: () => (key: string) => key, +})); +``` + +A Server Component using `getTranslations` (from `next-intl/server`) needs +the equivalent mock for that module instead. diff --git a/messages/en.json b/messages/en.json new file mode 100644 index 0000000..a548b21 --- /dev/null +++ b/messages/en.json @@ -0,0 +1,27 @@ +{ + "HomePage": { + "heroHeading": "Give as a stream, not a one-time click.", + "heroSubtext": "StreamGive lets you support verified NGOs on Stellar with continuous, cancel-anytime donations — transparent, on-chain, and instant.", + "exploreNgos": "Explore NGOs" + }, + "HowItWorks": { + "heading": "How it works", + "steps": [ + { + "title": "Connect your wallet", + "description": "Freighter or any supported Stellar wallet — one click, no account to create." + }, + { + "title": "Start a stream", + "description": "Pick a verified NGO, set an amount and a rate. They get paid continuously, second by second — not in one lump sum." + }, + { + "title": "Stay in control", + "description": "Top up, adjust the rate, or cancel anytime. Unspent funds always come back to you, instantly." + } + ] + }, + "LocaleSwitcher": { + "label": "Language" + } +} diff --git a/messages/es.json b/messages/es.json new file mode 100644 index 0000000..c4f2a6c --- /dev/null +++ b/messages/es.json @@ -0,0 +1,27 @@ +{ + "HomePage": { + "heroHeading": "Dona como un flujo, no como un clic único.", + "heroSubtext": "StreamGive te permite apoyar a ONGs verificadas en Stellar con donaciones continuas y cancelables en cualquier momento: transparentes, en la cadena y al instante.", + "exploreNgos": "Explorar ONGs" + }, + "HowItWorks": { + "heading": "Cómo funciona", + "steps": [ + { + "title": "Conecta tu billetera", + "description": "Freighter o cualquier billetera Stellar compatible: un clic, sin necesidad de crear una cuenta." + }, + { + "title": "Inicia un flujo", + "description": "Elige una ONG verificada, define un monto y una tasa. Reciben el pago de forma continua, segundo a segundo, no en una sola suma." + }, + { + "title": "Mantén el control", + "description": "Recarga, ajusta la tasa o cancela en cualquier momento. Los fondos no utilizados siempre vuelven a ti, al instante." + } + ] + }, + "LocaleSwitcher": { + "label": "Idioma" + } +} diff --git a/next.config.ts b/next.config.ts index 1acc8ad..6abcec7 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,4 +1,7 @@ import type { NextConfig } from 'next'; +import createNextIntlPlugin from 'next-intl/plugin'; + +const withNextIntl = createNextIntlPlugin('./src/i18n/request.ts'); const nextConfig: NextConfig = { // Don't advertise the framework/version in an X-Powered-By header. @@ -14,4 +17,4 @@ const nextConfig: NextConfig = { output: process.env.VERCEL ? undefined : 'standalone', }; -export default nextConfig; +export default withNextIntl(nextConfig); diff --git a/package-lock.json b/package-lock.json index 6f9e795..973da1a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,6 +11,7 @@ "@creit.tech/stellar-wallets-kit": "^2.6.0", "@stellar/stellar-sdk": "^17.0.1", "next": "^16.3.4", + "next-intl": "^4.3.1", "qrcode.react": "^4.2.0", "react": "^19.2.8", "react-dom": "^19.2.8" @@ -26,6 +27,7 @@ "@types/react": "^19.2.18", "@types/react-dom": "^19.2.7", "@vitejs/plugin-react": "^6.1.1", + "@vitest/coverage-v8": "^5.0.0", "eslint": "^9.39.5", "eslint-config-next": "^16.3.4", "eslint-config-prettier": "^10.1.8", @@ -429,6 +431,16 @@ "url": "https://opencollective.com/preact" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@bramus/specificity": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", @@ -843,6 +855,31 @@ "node": ">=20.19.0" } }, + "node_modules/@eloqnt/config": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@eloqnt/config/-/config-0.1.1.tgz", + "integrity": "sha512-YRDigSaiJEzKDfzTth5kG+L/PKfB3mo8h7yNzpXExPfvQmcD05WoZT57PdhJE0jI+Vva6eb0OCU8hnHHgdME3g==", + "license": "MIT" + }, + "node_modules/@eloqnt/format-json": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@eloqnt/format-json/-/format-json-0.1.0.tgz", + "integrity": "sha512-4Pkb+HyzgWNJXoU48aJhUyNZSjZvMGooPCNG1ZFmBm55xTiNNPQCNXH66Ob6sXS6RvbWZrRpCf+Uo/Jz9K9g+A==", + "license": "MIT", + "dependencies": { + "@eloqnt/config": "^0.1.0" + } + }, + "node_modules/@eloqnt/format-po": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@eloqnt/format-po/-/format-po-0.1.1.tgz", + "integrity": "sha512-aLtmIoqXGEaeTT0RcsLN8sZ3aIrsT/c/dVs28zrLhDjO3+vA3KNP+r98N0xq8w/k36+/GIUt4uZNbqcA71GVWw==", + "license": "MIT", + "dependencies": { + "@eloqnt/config": "^0.1.1", + "po-parser": "^2.2.0" + } + }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -1095,6 +1132,36 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@formatjs/fast-memoize": { + "version": "3.1.7", + "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.7.tgz", + "integrity": "sha512-zXfhLpvA6T7+efdt9JLbBwZ00tT7NsBMDVnDu8rpHeNNv8KfRZAMo2gkG0k9lK/Nzc//3kJ9pImsfuJxk3KhUA==", + "license": "MIT" + }, + "node_modules/@formatjs/icu-messageformat-parser": { + "version": "3.5.22", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.22.tgz", + "integrity": "sha512-YuwN+dDKgssfuYwtOyGQD0CSn7K46g9cv1Fp0VgKpjZDDFvv8b8crzP990IcJhRPWHhKzCGsbUSJVIguyyFA5A==", + "license": "MIT", + "dependencies": { + "@formatjs/icu-skeleton-parser": "2.1.12" + } + }, + "node_modules/@formatjs/icu-skeleton-parser": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.1.12.tgz", + "integrity": "sha512-ca7tZMLpfHX3sUD7hP6gE/qr6Iq89EDd3ZLeRhbsB7r0UD55DGJq+tmFLzrGrnx5StahUevQmQQVtbDRHsfDHA==", + "license": "MIT" + }, + "node_modules/@formatjs/intl-localematcher": { + "version": "0.8.14", + "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.14.tgz", + "integrity": "sha512-8O7V38QmbB11+ryJ3KM/znrzXtiqgpXzqNoqDsndHZZ5/zDKTTL+cf5F0mzXkQEwhx8IlWnqYvEovf2S5EwLTw==", + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "3.1.7" + } + }, "node_modules/@hot-wallet/sdk": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@hot-wallet/sdk/-/sdk-1.0.11.tgz", @@ -2485,6 +2552,316 @@ "url": "https://github.com/sponsors/oxc-project" } }, + "node_modules/@parcel/watcher": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", + "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "detect-libc": "^2.0.3", + "is-glob": "^4.0.3", + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.4" + }, + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "@parcel/watcher-android-arm64": "2.6.0", + "@parcel/watcher-darwin-arm64": "2.6.0", + "@parcel/watcher-darwin-x64": "2.6.0", + "@parcel/watcher-freebsd-x64": "2.6.0", + "@parcel/watcher-linux-arm-glibc": "2.6.0", + "@parcel/watcher-linux-arm-musl": "2.6.0", + "@parcel/watcher-linux-arm64-glibc": "2.6.0", + "@parcel/watcher-linux-arm64-musl": "2.6.0", + "@parcel/watcher-linux-x64-glibc": "2.6.0", + "@parcel/watcher-linux-x64-musl": "2.6.0", + "@parcel/watcher-win32-arm64": "2.6.0", + "@parcel/watcher-win32-x64": "2.6.0" + } + }, + "node_modules/@parcel/watcher-android-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.6.0.tgz", + "integrity": "sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.6.0.tgz", + "integrity": "sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-darwin-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.6.0.tgz", + "integrity": "sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-freebsd-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.6.0.tgz", + "integrity": "sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.6.0.tgz", + "integrity": "sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==", + "cpu": [ + "arm" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.6.0.tgz", + "integrity": "sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==", + "cpu": [ + "arm" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.6.0.tgz", + "integrity": "sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-arm64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.6.0.tgz", + "integrity": "sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-glibc": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.6.0.tgz", + "integrity": "sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-linux-x64-musl": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.6.0.tgz", + "integrity": "sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-arm64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.6.0.tgz", + "integrity": "sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==", + "cpu": [ + "arm64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher-win32-x64": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.6.0.tgz", + "integrity": "sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==", + "cpu": [ + "x64" + ], + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/@parcel/watcher/node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/@parcel/watcher/node_modules/picomatch": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", + "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/@phosphor-icons/webcomponents": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@phosphor-icons/webcomponents/-/webcomponents-2.1.5.tgz", @@ -3031,6 +3408,12 @@ "node": ">=16" } }, + "node_modules/@schummar/icu-type-parser": { + "version": "1.21.5", + "resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz", + "integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==", + "license": "MIT" + }, "node_modules/@scure/base": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", @@ -4299,59 +4682,315 @@ "stellar-js": "bin/stellar-js" }, "engines": { - "node": ">=22.12.0" + "node": ">=22.12.0" + } + }, + "node_modules/@stellar/stellar-sdk/node_modules/@exodus/bytes": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } + } + }, + "node_modules/@stellar/stellar-sdk/node_modules/@noble/hashes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", + "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", + "license": "MIT", + "engines": { + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@stellar/stellar-sdk/node_modules/axios": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz", + "integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" + } + }, + "node_modules/@stellar/stellar-sdk/node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "license": "MIT", + "engines": { + "node": ">=20" + } + }, + "node_modules/@swc/core": { + "version": "1.16.20", + "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.16.20.tgz", + "integrity": "sha512-zcdfS7k28OJIMuLhqRRyGja9PxrC2GXhFXkbQ1X15y6uZEMsTrqyej55V1DiJtlXaDOmwSLn5222YPT9ZoFxPA==", + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3", + "@swc/types": "^0.1.28" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/swc" + }, + "optionalDependencies": { + "@swc/core-darwin-arm64": "1.16.20", + "@swc/core-darwin-x64": "1.16.20", + "@swc/core-linux-arm-gnueabihf": "1.16.20", + "@swc/core-linux-arm64-gnu": "1.16.20", + "@swc/core-linux-arm64-musl": "1.16.20", + "@swc/core-linux-ppc64-gnu": "1.16.20", + "@swc/core-linux-s390x-gnu": "1.16.20", + "@swc/core-linux-x64-gnu": "1.16.20", + "@swc/core-linux-x64-musl": "1.16.20", + "@swc/core-win32-arm64-msvc": "1.16.20", + "@swc/core-win32-ia32-msvc": "1.16.20", + "@swc/core-win32-x64-msvc": "1.16.20" + }, + "peerDependencies": { + "@swc/helpers": ">=0.5.17" + }, + "peerDependenciesMeta": { + "@swc/helpers": { + "optional": true + } + } + }, + "node_modules/@swc/core-darwin-arm64": { + "version": "1.16.20", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.16.20.tgz", + "integrity": "sha512-i7NFSDnuE6tdBvBgmoVKKmburgcwZt/FZK2IusJaVGrjFrMUC1hrgE3RI+Kt9RhYM7vHvQj0W9j53zSxKEMbWQ==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-darwin-x64": { + "version": "1.16.20", + "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.16.20.tgz", + "integrity": "sha512-ho/LkT1Q3beSBeZGNZnm1mVbh5bKZHC0UGHtK3MswqOylbHtZoMXJBL40N8JpDj5SBxwECmdNEyXnU9HJ4F6RA==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm-gnueabihf": { + "version": "1.16.20", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.16.20.tgz", + "integrity": "sha512-xm3/s3zp9WyNgB1Sl78tpH76MHgobI3TSenTY15bkSHyrdQa9W2MUSESHRPd7CHGFLfpWl/0jwwNp5+hwG0e9A==", + "cpu": [ + "arm" + ], + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-gnu": { + "version": "1.16.20", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.16.20.tgz", + "integrity": "sha512-e6Um0U+VcTQyurOXhbratRQlrPITSRwugG00fhICbi2MGTi1vRiE4TMqGCjWpR1zPmZKXDGMw+yNFvh7AAgT2Q==", + "cpu": [ + "arm64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-arm64-musl": { + "version": "1.16.20", + "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.16.20.tgz", + "integrity": "sha512-ekCO2obfK9NFwXGHfAQjjuU5mLDDC7qMaUJH/V/4iyzD0BqNhilr6McZrXLytOQ/xKtTNxLQ71wYrD3M2LW8Kg==", + "cpu": [ + "arm64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-ppc64-gnu": { + "version": "1.16.20", + "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.16.20.tgz", + "integrity": "sha512-6/x2K3l4GNUWyaSmkywnth6ipZISzhVzOLsI66bj12/hSQYaKAyg9MKTwnuFf2B/iYA6GrjDghk36RYghRameQ==", + "cpu": [ + "ppc64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-s390x-gnu": { + "version": "1.16.20", + "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.16.20.tgz", + "integrity": "sha512-1GTd8sqS/NpcF+Y3YDSE0bT31V61+hD7ZZWEsVyiyxk/HQt2zpk4zlJJHOASBLc+/CyUuBZ949n6QMszbIXkmQ==", + "cpu": [ + "s390x" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" + } + }, + "node_modules/@swc/core-linux-x64-gnu": { + "version": "1.16.20", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.16.20.tgz", + "integrity": "sha512-XtMeYAgQa5UZhAX84TXn0sXPmhf1xHwcv0cl5hHiBXxTVbplY7WiTwIQU9uOtVm3VUqL/quyCm78/umUNZ7FmA==", + "cpu": [ + "x64" + ], + "libc": [ + "glibc" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=10" } }, - "node_modules/@stellar/stellar-sdk/node_modules/@exodus/bytes": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", - "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", - "license": "MIT", + "node_modules/@swc/core-linux-x64-musl": { + "version": "1.16.20", + "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.16.20.tgz", + "integrity": "sha512-qeF3UILa+MA4zJ29CdQ8KE7YbGFPMQBxWo3Lh0Ed0ETFEJ8pCKISWo7wQG4/SmfM9HX4KT1Odoj6ixyBn6Ji2Q==", + "cpu": [ + "x64" + ], + "libc": [ + "musl" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "linux" + ], "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@noble/hashes": "^1.8.0 || ^2.0.0" - }, - "peerDependenciesMeta": { - "@noble/hashes": { - "optional": true - } + "node": ">=10" } }, - "node_modules/@stellar/stellar-sdk/node_modules/@noble/hashes": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", - "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", - "license": "MIT", + "node_modules/@swc/core-win32-arm64-msvc": { + "version": "1.16.20", + "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.16.20.tgz", + "integrity": "sha512-ciMtHXAmE+jnrWWO14rErr6tfK34QZm8jZRSdRLPodqJlzrYDw7TqN60HC8Yim5Ua9a1r/poyfkpNft6GRXvxA==", + "cpu": [ + "arm64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" + "node": ">=10" } }, - "node_modules/@stellar/stellar-sdk/node_modules/axios": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz", - "integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" + "node_modules/@swc/core-win32-ia32-msvc": { + "version": "1.16.20", + "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.16.20.tgz", + "integrity": "sha512-MTbkgGooWebmjmATx1+QRECVZ+q4ICKNXCutwOrFVD5b5hdxla/yV4e6cRFrfNMowejaNpmMQg31ENWt0Yfzpg==", + "cpu": [ + "ia32" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=10" } }, - "node_modules/@stellar/stellar-sdk/node_modules/commander": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", - "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", - "license": "MIT", + "node_modules/@swc/core-win32-x64-msvc": { + "version": "1.16.20", + "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.16.20.tgz", + "integrity": "sha512-Lf5oMaSdJBImtGhDecasxcIa7Z5LeJ4aGmBOY7TScroO+A/MxxB9PslIpgV0rtG/FcxtipN7hveBg9ZTeboaaw==", + "cpu": [ + "x64" + ], + "license": "Apache-2.0 AND MIT", + "optional": true, + "os": [ + "win32" + ], "engines": { - "node": ">=20" + "node": ">=10" } }, + "node_modules/@swc/counter": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", + "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", + "license": "Apache-2.0" + }, "node_modules/@swc/helpers": { "version": "0.5.23", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", @@ -4361,6 +5000,15 @@ "tslib": "^2.8.0" } }, + "node_modules/@swc/types": { + "version": "0.1.28", + "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.28.tgz", + "integrity": "sha512-V6Mnml8v09QALx6K0elJ7o9K/MkVDtW3t6L+7Ou/JcWtb3xwId2AH4FeOceySd2JaO87IMw4+6vSZxLm34LPbw==", + "license": "Apache-2.0", + "dependencies": { + "@swc/counter": "^0.1.3" + } + }, "node_modules/@tailwindcss/node": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", @@ -5680,23 +6328,75 @@ } } }, + "node_modules/@vitest/coverage-v8": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-5.0.5.tgz", + "integrity": "sha512-4KpkGdIrSqBihx/JyP/e+S+IKB98etg1XDQiLAxksLfcShnwi1VReGeOT9NI43hLHbknosKp/1CCLn9Zgcs8mg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.2", + "@vitest/istanbul-lib-coverage": "^1.0.2", + "@vitest/istanbul-lib-report": "^1.0.2", + "ast-v8-to-istanbul": "^1.0.7", + "magicast": "^0.5.5", + "obug": "^3.0.0", + "std-env": "^4.3.0", + "tinyrainbow": "^3.2.0" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "@vitest/browser": "5.0.5", + "vitest": "5.0.5" + }, + "peerDependenciesMeta": { + "@vitest/browser": { + "optional": true + } + } + }, + "node_modules/@vitest/istanbul-lib-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.2.tgz", + "integrity": "sha512-9J/JMwOf9AoJhAywhrn7ScKTL38hsWQP/qPG60OtaAFcQ5OXPwKsxZFlbnuCKmZ61m8/lGgHYnFpdyQZUvG/iA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=22" + } + }, + "node_modules/@vitest/istanbul-lib-report": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-report/-/istanbul-lib-report-1.0.2.tgz", + "integrity": "sha512-gUsfXZJbzPamoIY5TvHFiMMoXESBrUMo+xqaj+rYrWI69+EnvRlYBlP96ZnHPY4vX8kyUpgAnFUCg5wZG/HkDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/istanbul-lib-coverage": "1.0.2" + }, + "engines": { + "node": ">=22" + } + }, "node_modules/@vitest/mocker": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.0.tgz", - "integrity": "sha512-66PGTMIiVJP3t4a5yxU9qPtf7MdTBs8jmToMvy+HVflB3Yy13WJZTtPePdvU+wjRV02SKK5doLbSA6o9pwOmiA==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.5.tgz", + "integrity": "sha512-xLL3lkDegJZw+MGoGaCCsFayDZthGgpAtr3Vziyfc4p7xH89K7KRPcRBBh/kEYm0Aqkt5DtTmkYpBtRV/FrHZg==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.31", - "@vitest/spy": "5.0.0", + "@vitest/spy": "5.0.5", "estree-walker": "^3.0.3", - "magic-string": "^1.2.3" + "magic-string": "^1.4.3" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "msw": "^2.4.9", + "msw": "^2.4.9 || ^3.0.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { @@ -5709,19 +6409,19 @@ } }, "node_modules/@vitest/mocker/node_modules/magic-string": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.2.3.tgz", - "integrity": "sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", + "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "@jridgewell/sourcemap-codec": "^1.6.0" } }, "node_modules/@vitest/spy": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.0.tgz", - "integrity": "sha512-uy+luWBAPw9XfthoHi5AkfHUnuPYEESjl0p/r+meoBnU8bxg5GDQ3Ey8MjcJ6sqahkL4PFyrvfMJJBw7LbU06g==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.5.tgz", + "integrity": "sha512-lwJRExuH417257Q5wv9QYugN9IMJkoxcrKrYxGPQkBzl7wlNpTUxD/5yNX4ESKVUIShKPgkGchYPFMWOOk0wzw==", "dev": true, "license": "MIT", "funding": { @@ -6768,6 +7468,25 @@ "dev": true, "license": "MIT" }, + "node_modules/ast-v8-to-istanbul": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.7.tgz", + "integrity": "sha512-kFL68AG6ajd8fg248zwM9GQrUWEp79gsmjum34OEXjs4yHuUMZfYKwOLW9GMmB4oNvVrj+EAGxsP7ye2UR9UlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.31", + "estree-walker": "^3.0.3", + "js-tokens": "^10.0.0" + } + }, + "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", + "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", + "dev": true, + "license": "MIT" + }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -7157,9 +7876,9 @@ "license": "CC-BY-4.0" }, "node_modules/chai": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", - "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", + "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", "dev": true, "license": "MIT", "engines": { @@ -7275,6 +7994,19 @@ "dev": true, "license": "MIT" }, + "node_modules/content-type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", + "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -7610,7 +8342,6 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", - "devOptional": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -9257,6 +9988,21 @@ "ms": "^2.0.0" } }, + "node_modules/icu-minify": { + "version": "4.14.9", + "resolved": "https://registry.npmjs.org/icu-minify/-/icu-minify-4.14.9.tgz", + "integrity": "sha512-PmO+fNnC3K3mfPKTnm3RrPERrhQZnjm1AlpyZQ0ShbXi8Y/kYZ3YOde8TEjCLz9k3P2DmzkBn8lykGrCAi1Ajw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/amannn" + } + ], + "license": "MIT", + "dependencies": { + "@formatjs/icu-messageformat-parser": "^3.4.0" + } + }, "node_modules/idb-keyval": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", @@ -9351,6 +10097,31 @@ "node": ">= 0.4" } }, + "node_modules/intl-messageformat": { + "version": "11.2.15", + "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-11.2.15.tgz", + "integrity": "sha512-W4VNytsXyWgW5lvVchmsb9xgA5rSKqoflu3/IQ9UCtB0z4R+Bx9MoEkwFT/+30RCl/KzUoH0QGGe9lppF5SuvQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@formatjs/fast-memoize": "3.1.7", + "@formatjs/icu-messageformat-parser": "3.5.18" + } + }, + "node_modules/intl-messageformat/node_modules/@formatjs/icu-messageformat-parser": { + "version": "3.5.18", + "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.18.tgz", + "integrity": "sha512-wX1efcL8d7K5QapOTRsP/OLTA/kpwe4LwqWyNxDNg5iRs8i9jJbCyxB5P8jc4KOW5oiLzz2DJr+BRY5xMV9+bA==", + "license": "MIT", + "dependencies": { + "@formatjs/icu-skeleton-parser": "2.1.11" + } + }, + "node_modules/intl-messageformat/node_modules/@formatjs/icu-skeleton-parser": { + "version": "2.1.11", + "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.1.11.tgz", + "integrity": "sha512-j8cUmOJzVgkHuS0QiQ6ga76UIoLOFSAMWhs7aZJztH3aAdCOAE6vpC8KVvFB4cU10ON0y2/5oOVmPJ43s2lTwA==", + "license": "MIT" + }, "node_modules/iron-webcrypto": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", @@ -9545,7 +10316,6 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -9600,7 +10370,6 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -10602,6 +11371,18 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/magicast": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.5.tgz", + "integrity": "sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.7", + "@babel/types": "^7.29.7", + "source-map-js": "^1.2.1" + } + }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -10877,6 +11658,22 @@ "webidl-conversions": "^3.0.0" } }, + "node_modules/negotiator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", + "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", + "license": "MIT", + "dependencies": { + "content-type": "^2.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/next": { "version": "16.3.4", "resolved": "https://registry.npmjs.org/next/-/next-16.3.4.tgz", @@ -10930,6 +11727,46 @@ } } }, + "node_modules/next-intl": { + "version": "4.14.9", + "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.14.9.tgz", + "integrity": "sha512-uauRtUC+TrYNA1OL/QcwL9irndLrI9jK3Wdaa9W0B5AeKh9cqVdNeT0IWgLnhBqBRrVwppVdI0M+C1+JdGPQ3w==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/amannn" + } + ], + "license": "MIT", + "dependencies": { + "@eloqnt/config": "^0.1.1", + "@eloqnt/format-json": "^0.1.0", + "@eloqnt/format-po": "^0.1.1", + "@formatjs/intl-localematcher": "^0.8.1", + "@parcel/watcher": "^2.4.1", + "@swc/core": "~1.16.0", + "icu-minify": "^4.14.9", + "negotiator": "^1.0.0", + "next-intl-swc-plugin-extractor": "4.14.9", + "use-intl": "^4.14.9" + }, + "peerDependencies": { + "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", + "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, + "node_modules/next-intl-swc-plugin-extractor": { + "version": "4.14.9", + "resolved": "https://registry.npmjs.org/next-intl-swc-plugin-extractor/-/next-intl-swc-plugin-extractor-4.14.9.tgz", + "integrity": "sha512-8jAkmDwQJo6RDavBHm9QRl1YTn2WXf+rI4SVhMaZxZcxopnwZic6fKTXOoNIo3kzC+a6vJySkOkXmHTVFLAYtg==", + "license": "MIT" + }, "node_modules/next/node_modules/postcss": { "version": "8.5.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", @@ -11191,9 +12028,9 @@ } }, "node_modules/obug": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", - "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/obug/-/obug-3.0.0.tgz", + "integrity": "sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", @@ -11477,6 +12314,12 @@ "node": ">=10.13.0" } }, + "node_modules/po-parser": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/po-parser/-/po-parser-2.2.0.tgz", + "integrity": "sha512-NdTrKgh0oO7+y+RFX8KKhOB438x/j94UGXEFzl/7pHH0JjWSn42iJ9tgmPksIO6n1JKDHmNDcejPJfKspljB9g==", + "license": "MIT" + }, "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", @@ -12372,13 +13215,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/siginfo": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", - "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", - "dev": true, - "license": "ISC" - }, "node_modules/slow-redact": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/slow-redact/-/slow-redact-0.3.2.tgz", @@ -12431,13 +13267,6 @@ "dev": true, "license": "MIT" }, - "node_modules/stackback": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", - "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", - "dev": true, - "license": "MIT" - }, "node_modules/statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -12449,9 +13278,9 @@ } }, "node_modules/std-env": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", - "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz", + "integrity": "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==", "dev": true, "license": "MIT" }, @@ -12773,9 +13602,9 @@ } }, "node_modules/tinybench": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.1.4.tgz", - "integrity": "sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.2.tgz", + "integrity": "sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==", "dev": true, "license": "MIT", "engines": { @@ -12783,9 +13612,9 @@ } }, "node_modules/tinyexec": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", - "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", + "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", "dev": true, "license": "MIT", "engines": { @@ -12840,6 +13669,16 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/tinyrainbow": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.2.0.tgz", + "integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/tldts": { "version": "7.4.12", "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.12.tgz", @@ -13352,6 +14191,33 @@ "punycode": "^2.1.0" } }, + "node_modules/use-intl": { + "version": "4.14.9", + "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.14.9.tgz", + "integrity": "sha512-37ktvdCCz+6JY2VFPl/GV69Ds4ahiUT2c2xEZZRZYxdgGLxFsUbdxzjQVRm3YMIOUU0VPs6zvlifb4Yjk4IxIg==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/amannn" + } + ], + "license": "MIT", + "dependencies": { + "@formatjs/fast-memoize": "^3.1.0", + "@schummar/icu-type-parser": "1.21.5", + "icu-minify": "^4.14.9", + "intl-messageformat": "^11.1.0" + }, + "peerDependencies": { + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } + } + }, "node_modules/utf-8-validate": { "version": "6.0.6", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.6.tgz", @@ -13914,25 +14780,25 @@ } }, "node_modules/vitest": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.0.tgz", - "integrity": "sha512-gpsMNoRhMjMktVxPtstOH4/PJuPyovVaMDr4oDilXaGH1EcqM2OE96SoHT2VIQ6fTGtTjqmHDrEu2X9RQiXf8Q==", + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.5.tgz", + "integrity": "sha512-cPODOMF8YnkiiYtDdHo6n4wWmBrOBBbOIYH2d33M4l3CdNn72h+qMmO6uXffUN6A5AZFXNND/d+krS9mgkXRfg==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/mocker": "5.0.0", - "chai": "^6.2.2", + "@vitest/mocker": "5.0.5", + "chai": "^6.3.0", "es-module-lexer": "^2.3.2", "expect-type": "^1.4.0", - "magic-string": "^1.2.3", - "obug": "^2.1.4", + "magic-string": "^1.4.3", + "obug": "^3.0.0", "picomatch": "^4.0.7", - "std-env": "^4.2.0", - "tinybench": "6.1.4", - "tinyexec": "1.3.0", + "std-env": "^4.3.0", + "tinybench": "^6.2.2", + "tinyexec": "^1.3.1", "tinyglobby": "^0.2.17", - "why-is-node-running": "^2.3.0" + "why-is-node-running": "3.2.1" }, "bin": { "vitest": "vitest.mjs" @@ -13947,12 +14813,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "5.0.0", - "@vitest/browser-preview": "5.0.0", + "@vitest/browser-playwright": "5.0.5", + "@vitest/browser-preview": "5.0.5", "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", - "@vitest/coverage-istanbul": "5.0.0", - "@vitest/coverage-v8": "5.0.0", - "@vitest/ui": "5.0.0", + "@vitest/coverage-istanbul": "5.0.5", + "@vitest/coverage-v8": "5.0.5", + "@vitest/ui": "5.0.5", "happy-dom": "*", "jsdom": "*", "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" @@ -13997,13 +14863,13 @@ } }, "node_modules/vitest/node_modules/magic-string": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.2.3.tgz", - "integrity": "sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==", + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", + "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.5" + "@jridgewell/sourcemap-codec": "^1.6.0" } }, "node_modules/vitest/node_modules/picomatch": { @@ -14197,20 +15063,16 @@ } }, "node_modules/why-is-node-running": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", - "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-3.2.1.tgz", + "integrity": "sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==", "dev": true, "license": "MIT", - "dependencies": { - "siginfo": "^2.0.0", - "stackback": "0.0.2" - }, "bin": { "why-is-node-running": "cli.js" }, "engines": { - "node": ">=8" + "node": ">=20.11" } }, "node_modules/word-wrap": { diff --git a/package.json b/package.json index 6574588..0b06779 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "@creit.tech/stellar-wallets-kit": "^2.6.0", "@stellar/stellar-sdk": "^17.0.1", "next": "^16.3.4", + "next-intl": "^4.3.1", "qrcode.react": "^4.2.0", "react": "^19.2.8", "react-dom": "^19.2.8" diff --git a/src/app/(main)/dashboard/page.test.tsx b/src/app/(main)/dashboard/page.test.tsx index f5dd221..1ed8f22 100644 --- a/src/app/(main)/dashboard/page.test.tsx +++ b/src/app/(main)/dashboard/page.test.tsx @@ -60,6 +60,8 @@ describe('DashboardPage', () => { signTransaction: vi.fn(), signMessage: vi.fn(), networkMismatch: false, + noWalletOptions: null, + dismissNoWalletPrompt: vi.fn(), }); } @@ -85,6 +87,8 @@ describe('DashboardPage', () => { signTransaction: vi.fn(), signMessage: vi.fn(), networkMismatch: false, + noWalletOptions: null, + dismissNoWalletPrompt: vi.fn(), }); let resolveFetch: (streams: Stream[]) => void = () => {}; @@ -115,6 +119,8 @@ describe('DashboardPage', () => { signTransaction: vi.fn(), signMessage: vi.fn(), networkMismatch: false, + noWalletOptions: null, + dismissNoWalletPrompt: vi.fn(), }); render(); @@ -132,6 +138,8 @@ describe('DashboardPage', () => { signTransaction: vi.fn(), signMessage: vi.fn(), networkMismatch: false, + noWalletOptions: null, + dismissNoWalletPrompt: vi.fn(), }); const wallet = vi.spyOn(WalletProviderMod, 'useWallet'); wallet.mockReturnValue(walletFor('GOLD')); @@ -297,6 +305,8 @@ describe('DashboardPage', () => { signTransaction: vi.fn(), signMessage: vi.fn(), networkMismatch: false, + noWalletOptions: null, + dismissNoWalletPrompt: vi.fn(), }); vi.spyOn(ApiMod, 'getStreams').mockResolvedValue(streams); render(); @@ -389,6 +399,8 @@ describe('DashboardPage', () => { signTransaction: vi.fn(), signMessage: vi.fn(), networkMismatch: false, + noWalletOptions: null, + dismissNoWalletPrompt: vi.fn(), }); vi.spyOn(ApiMod, 'getStreams').mockResolvedValue(streams); vi.mocked(CsvMod.buildDonationHistoryCsv).mockReturnValue('id,status\n101,ACTIVE'); @@ -530,6 +542,8 @@ describe('DashboardPage', () => { signTransaction: vi.fn(), signMessage: vi.fn(), networkMismatch: false, + noWalletOptions: null, + dismissNoWalletPrompt: vi.fn(), }); vi.spyOn(ApiMod, 'getStreams').mockResolvedValue([streamOldest, streamMiddle, streamNewest]); } diff --git a/src/app/(main)/ngos/[id]/page.tsx b/src/app/(main)/ngos/[id]/page.tsx index a675bb2..0b6f086 100644 --- a/src/app/(main)/ngos/[id]/page.tsx +++ b/src/app/(main)/ngos/[id]/page.tsx @@ -5,15 +5,11 @@ import { notFound } from 'next/navigation'; import { CopyLinkButton } from '@/components/common/CopyLinkButton'; import { DonateQrCode } from '@/components/ngos/DonateQrCode'; import { getNgo, getStreams, type NgoProfile, type Stream } from '@/lib/api'; -import { formatAmount, formatTokenAmount, truncateAddress } from '@/lib/format'; +import { formatAmount, formatDate, formatTokenAmount, truncateAddress } from '@/lib/format'; import { explorerUrl } from '@/lib/stellar'; const RECENT_STREAMS_LIMIT = 10; -function formatDate(iso: string): string { - return new Date(iso).toLocaleDateString(); -} - /** Only http(s) URLs are rendered as links, so a stored `javascript:` or * other scheme can never become a clickable href. */ function safeWebsiteUrl(website: string): string | null { @@ -37,9 +33,33 @@ export async function generateMetadata({ params }: Props): Promise { return { title: 'NGO not found' }; } + const description = `Support ${ngo.name} with a recurring, streaming donation on Stellar.`; + // Relative — resolved against metadataBase (the root layout sets it to + // NEXT_PUBLIC_APP_URL), same as the root layout's own openGraph/twitter. + const profileUrl = `/ngos/${id}`; + return { title: ngo.name, - description: `Support ${ngo.name} with a recurring, streaming donation on Stellar.`, + description, + alternates: { + canonical: profileUrl, + }, + openGraph: { + title: ngo.name, + description, + type: 'website', + url: profileUrl, + // /opengraph-image is the site-wide share image (src/app/opengraph-image.tsx) + // — there's no per-NGO image to generate one from, so every profile + // shares it rather than going without a preview image at all. + images: [{ url: '/opengraph-image', width: 1200, height: 630, alt: 'StreamGive' }], + }, + twitter: { + card: 'summary_large_image', + title: ngo.name, + description, + images: ['/opengraph-image'], + }, }; } diff --git a/src/app/(main)/page.tsx b/src/app/(main)/page.tsx index 4303447..de497bd 100644 --- a/src/app/(main)/page.tsx +++ b/src/app/(main)/page.tsx @@ -1,25 +1,23 @@ import Link from 'next/link'; +import { getTranslations } from 'next-intl/server'; import { HowItWorks } from '@/components/landing/HowItWorks'; import { StatsStrip } from '@/components/landing/StatsStrip'; -export default function Home() { +export default async function Home() { + const t = await getTranslations('HomePage'); + return ( <>
-

- Give as a stream, not a one-time click. -

-

- StreamGive lets you support verified NGOs on Stellar with continuous, cancel-anytime - donations — transparent, on-chain, and instant. -

+

{t('heroHeading')}

+

{t('heroSubtext')}

- Explore NGOs + {t('exploreNgos')}
diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 59378d7..f4a4502 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -1,5 +1,7 @@ import type { Metadata } from 'next'; import { Geist, Geist_Mono } from 'next/font/google'; +import { NextIntlClientProvider } from 'next-intl'; +import { getLocale, getMessages } from 'next-intl/server'; import { AppShell } from '@/components/common/AppShell'; import { THEME_INIT_SCRIPT } from '@/lib/theme'; @@ -53,18 +55,23 @@ export const metadata: Metadata = { }, }; -export default function RootLayout({ +export default async function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { + // Both come from src/i18n/request.ts (wired up via next.config.ts's + // withNextIntl), which resolves the locale from the NEXT_LOCALE cookie. + const locale = await getLocale(); + const messages = await getMessages(); + return ( // suppressHydrationWarning: THEME_INIT_SCRIPT adds a `dark`/`light` // class here before React hydrates, which never matches the // server-rendered markup (the server doesn't know the client's // preference) — that mismatch is expected and this is the documented // way to silence the resulting warning without disabling it app-wide. - + {/* Must run synchronously, before paint, to avoid a flash of the wrong theme — see THEME_INIT_SCRIPT's own comment. */} @@ -73,7 +80,9 @@ export default function RootLayout({ - {children} + + {children} + ); diff --git a/src/components/common/ConnectWalletPrompt.test.tsx b/src/components/common/ConnectWalletPrompt.test.tsx index db45991..15e711e 100644 --- a/src/components/common/ConnectWalletPrompt.test.tsx +++ b/src/components/common/ConnectWalletPrompt.test.tsx @@ -21,6 +21,8 @@ describe('ConnectWalletPrompt', () => { signTransaction: vi.fn(), signMessage: vi.fn(), networkMismatch: false, + noWalletOptions: null, + dismissNoWalletPrompt: vi.fn(), }); }); diff --git a/src/components/dashboard/StreamControls.tsx b/src/components/dashboard/StreamControls.tsx index abbe0b3..073d5f2 100644 --- a/src/components/dashboard/StreamControls.tsx +++ b/src/components/dashboard/StreamControls.tsx @@ -8,6 +8,7 @@ import { getStreams, type Stream } from '@/lib/api'; import { describeContractError } from '@/lib/contractTypes'; import { useDonationVaultClient } from '@/lib/donationVaultClient'; import { describeAmountError, formatEstimatedFee, formatRate, parseAmount } from '@/lib/format'; +import { DURATIONS } from '@/lib/durations'; import { txToastAction } from '@/lib/stellar'; /** @@ -24,13 +25,6 @@ export function computeModifyRate(balance: string, durationSeconds: number): big return rate > 0n ? rate : null; } -const DURATIONS = [ - { label: '1 week', seconds: 7 * 24 * 60 * 60 }, - { label: '1 month', seconds: 30 * 24 * 60 * 60 }, - { label: '3 months', seconds: 90 * 24 * 60 * 60 }, - { label: '1 year', seconds: 365 * 24 * 60 * 60 }, -]; - // The list this sits in comes from the backend's indexer, which polls on // an interval — a confirmed on-chain action can lag a few seconds before // onChanged()'s refresh actually reflects it. Said explicitly in the diff --git a/src/components/donate/CreateStreamForm.tsx b/src/components/donate/CreateStreamForm.tsx index 34f670b..5f10dd1 100644 --- a/src/components/donate/CreateStreamForm.tsx +++ b/src/components/donate/CreateStreamForm.tsx @@ -7,6 +7,7 @@ import { ConnectWalletPrompt } from '@/components/common/ConnectWalletPrompt'; import { useToast } from '@/components/toast/ToastProvider'; import { useWallet } from '@/components/wallet/WalletProvider'; import { useDonationVaultClient } from '@/lib/donationVaultClient'; +import { DURATIONS } from '@/lib/durations'; import { describeAmountError, formatAmount, @@ -28,14 +29,6 @@ import { } from '@/lib/stellar'; import { describeContractError } from '@/lib/contractTypes'; - -const DURATIONS = [ - { label: '1 week', seconds: 7 * 24 * 60 * 60 }, - { label: '1 month', seconds: 30 * 24 * 60 * 60 }, - { label: '3 months', seconds: 90 * 24 * 60 * 60 }, - { label: '1 year', seconds: 365 * 24 * 60 * 60 }, -]; - // Quick-fill shortcuts for the "Total amount" field, in whichever token is // currently selected (the field isn't pinned to a single currency, so // these are unitless multipliers rather than e.g. "$5"). diff --git a/src/components/landing/HowItWorks.tsx b/src/components/landing/HowItWorks.tsx index f4f88d0..baa1409 100644 --- a/src/components/landing/HowItWorks.tsx +++ b/src/components/landing/HowItWorks.tsx @@ -1,24 +1,16 @@ -const steps = [ - { - title: 'Connect your wallet', - description: 'Freighter or any supported Stellar wallet — one click, no account to create.', - }, - { - title: 'Start a stream', - description: - 'Pick a verified NGO, set an amount and a rate. They get paid continuously, second by second — not in one lump sum.', - }, - { - title: 'Stay in control', - description: - 'Top up, adjust the rate, or cancel anytime. Unspent funds always come back to you, instantly.', - }, -]; +import { getTranslations } from 'next-intl/server'; + +type Step = { title: string; description: string }; + +export async function HowItWorks() { + const t = await getTranslations('HowItWorks'); + // Steps are a JSON array, not a single string — t.raw() returns the + // message as-is instead of running it through ICU formatting. + const steps = t.raw('steps') as Step[]; -export function HowItWorks() { return (
-

How it works

+

{t('heading')}

    {steps.map((step, index) => (
  1. diff --git a/src/components/layout/Header.test.tsx b/src/components/layout/Header.test.tsx index 3e22250..d9fd120 100644 --- a/src/components/layout/Header.test.tsx +++ b/src/components/layout/Header.test.tsx @@ -8,6 +8,14 @@ const mockUsePathname = vi.fn(() => '/ngos'); vi.mock('next/navigation', () => ({ usePathname: () => mockUsePathname(), + useRouter: () => ({ refresh: vi.fn() }), +})); + +// Just enough for LocaleSwitcher (rendered inside Header) to not crash for +// lack of a real NextIntlClientProvider context. +vi.mock('next-intl', () => ({ + useLocale: () => 'en', + useTranslations: () => (key: string) => key, })); vi.mock('@/components/wallet/WalletProvider', () => ({ diff --git a/src/components/layout/Header.tsx b/src/components/layout/Header.tsx index 57f008e..9c2aabf 100644 --- a/src/components/layout/Header.tsx +++ b/src/components/layout/Header.tsx @@ -4,6 +4,7 @@ import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { useEffect, useState } from 'react'; +import { LocaleSwitcher } from '@/components/layout/LocaleSwitcher'; import { Logo } from '@/components/layout/Logo'; import { ThemeToggle } from '@/components/layout/ThemeToggle'; import { ConnectWalletButton } from '@/components/wallet/ConnectWalletButton'; @@ -33,6 +34,9 @@ export function Header() { useEffect(() => { if (!address) { + // A disconnected wallet must not keep showing the NGO Admin link from + // a previous session's lookup. + // eslint-disable-next-line react-hooks/set-state-in-effect setOwnsNgo(false); return; } @@ -103,6 +107,7 @@ export function Header() { {link.label} ))} + @@ -157,6 +162,7 @@ export function Header() { ); })} + )} diff --git a/src/components/layout/LocaleSwitcher.tsx b/src/components/layout/LocaleSwitcher.tsx new file mode 100644 index 0000000..01453ad --- /dev/null +++ b/src/components/layout/LocaleSwitcher.tsx @@ -0,0 +1,43 @@ +'use client'; + +import { useLocale, useTranslations } from 'next-intl'; +import { useRouter } from 'next/navigation'; + +import { LOCALE_COOKIE, LOCALE_LABELS, LOCALES, type Locale } from '@/i18n/config'; + +const COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365; + +/** + * Scaffolding for later real translations (issue: "Add i18n scaffolding") — + * only `messages/en.json` and `messages/es.json` exist today, and only the + * landing page reads from them (see HomePage and HowItWorks). Switching + * still works end to end (it writes the NEXT_LOCALE cookie `request.ts` + * reads and refreshes the page), it just won't change anything outside + * those two components yet. See docs/I18N.md. + */ +export function LocaleSwitcher() { + const locale = useLocale(); + const t = useTranslations('LocaleSwitcher'); + const router = useRouter(); + + function handleChange(event: React.ChangeEvent) { + const next = event.target.value as Locale; + document.cookie = `${LOCALE_COOKIE}=${next}; path=/; max-age=${COOKIE_MAX_AGE_SECONDS}`; + router.refresh(); + } + + return ( + + ); +} diff --git a/src/components/ngoAdmin/WithdrawalHistory.tsx b/src/components/ngoAdmin/WithdrawalHistory.tsx index cebc7e1..0dcba5f 100644 --- a/src/components/ngoAdmin/WithdrawalHistory.tsx +++ b/src/components/ngoAdmin/WithdrawalHistory.tsx @@ -3,14 +3,10 @@ import { useEffect, useState } from 'react'; import { getWithdrawals, type Withdrawal } from '@/lib/api'; -import { formatAmount } from '@/lib/format'; +import { formatAmount, formatDateTime } from '@/lib/format'; const PAGE_SIZE = 10; -function formatDate(iso: string): string { - return new Date(iso).toLocaleString(); -} - /** * Past withdrawal transactions for an NGO, so admins can reconcile their * records against what they've actually pulled out of StreamGive. @@ -123,7 +119,7 @@ export function WithdrawalHistory({ ngoId }: { ngoId: string }) { {visibleWithdrawals.map((withdrawal) => ( - {formatDate(withdrawal.createdAt)} + {formatDateTime(withdrawal.createdAt)} #{withdrawal.streamId} diff --git a/src/components/streams/StreamDetailsModal.test.tsx b/src/components/streams/StreamDetailsModal.test.tsx index c1b29d1..df0c9c6 100644 --- a/src/components/streams/StreamDetailsModal.test.tsx +++ b/src/components/streams/StreamDetailsModal.test.tsx @@ -131,8 +131,10 @@ describe('StreamDetailsModal', () => { it('wraps Tab from the last focusable element back to the first', () => { render(); const closeButton = screen.getByLabelText('Close'); - const lastLink = screen.getByRole('link', { name: 'View contract' }); - lastLink.focus(); + // The on-chain ID's CopyAddressButton is the last focusable element — + // it comes after "View contract" in the dialog's own tab order. + const lastControl = screen.getByRole('button', { name: `Copy address ${MOCK_STREAM.onChainId}` }); + lastControl.focus(); const notPrevented = fireEvent.keyDown(document, { key: 'Tab' }); @@ -143,12 +145,12 @@ describe('StreamDetailsModal', () => { it('wraps Shift+Tab from the first focusable element to the last', () => { render(); const closeButton = screen.getByLabelText('Close'); - const lastLink = screen.getByRole('link', { name: 'View contract' }); + const lastControl = screen.getByRole('button', { name: `Copy address ${MOCK_STREAM.onChainId}` }); expect(closeButton).toHaveFocus(); const notPrevented = fireEvent.keyDown(document, { key: 'Tab', shiftKey: true }); - expect(lastLink).toHaveFocus(); + expect(lastControl).toHaveFocus(); expect(notPrevented).toBe(false); }); diff --git a/src/components/streams/StreamDetailsModal.tsx b/src/components/streams/StreamDetailsModal.tsx index 7b00a74..90ec1dd 100644 --- a/src/components/streams/StreamDetailsModal.tsx +++ b/src/components/streams/StreamDetailsModal.tsx @@ -3,19 +3,10 @@ import Link from 'next/link'; import { useEffect, useRef } from 'react'; +import { CopyAddressButton } from '@/components/common/CopyAddressButton'; import type { Stream } from '@/lib/api'; -import { formatTokenAmount } from '@/lib/format'; -import { DONATION_VAULT_CONTRACT_ID, explorerUrl, getNativeAssetAddress } from '@/lib/stellar'; - -function formatDate(iso: string): string { - return new Date(iso).toLocaleString(); -} - -// There's no symbol/name resolution for arbitrary tokens — only the native -// asset's contract address is derivable client-side. -function tokenLabel(tokenAddress: string): string { - return tokenAddress === getNativeAssetAddress() ? 'XLM' : tokenAddress; -} +import { formatDateTime, formatTokenAmount, tokenLabel } from '@/lib/format'; +import { DONATION_VAULT_CONTRACT_ID, explorerUrl } from '@/lib/stellar'; export function StreamDetailsModal({ stream, onClose }: { stream: Stream; onClose: () => void }) { useEffect(() => { @@ -125,7 +116,7 @@ export function StreamDetailsModal({ stream, onClose }: { stream: Stream; onClos {DONATION_VAULT_CONTRACT_ID && (
    Contract
    -
    +
    View contract +
    )} @@ -178,15 +172,18 @@ export function StreamDetailsModal({ stream, onClose }: { stream: Stream; onClos
    Created
    -
    {formatDate(stream.createdAt)}
    +
    {formatDateTime(stream.createdAt)}
    Last updated
    -
    {formatDate(stream.updatedAt)}
    +
    {formatDateTime(stream.updatedAt)}
    On-chain ID
    -
    {stream.onChainId}
    +
    + {stream.onChainId} + +
    diff --git a/src/components/wallet/ConnectWalletButton.test.tsx b/src/components/wallet/ConnectWalletButton.test.tsx index d2c72b4..2d7cce0 100644 --- a/src/components/wallet/ConnectWalletButton.test.tsx +++ b/src/components/wallet/ConnectWalletButton.test.tsx @@ -35,6 +35,8 @@ describe('ConnectWalletButton disconnect flow', () => { signTransaction: vi.fn(), signMessage: vi.fn(), networkMismatch: false, + noWalletOptions: null, + dismissNoWalletPrompt: vi.fn(), }; }); }); @@ -73,6 +75,8 @@ describe('ConnectWalletButton connecting state', () => { signTransaction: vi.fn(), signMessage: vi.fn(), networkMismatch: false, + noWalletOptions: null, + dismissNoWalletPrompt: vi.fn(), }); render(); diff --git a/src/components/wallet/ConnectWalletButton.tsx b/src/components/wallet/ConnectWalletButton.tsx index 94d7959..b6d64dc 100644 --- a/src/components/wallet/ConnectWalletButton.tsx +++ b/src/components/wallet/ConnectWalletButton.tsx @@ -1,13 +1,10 @@ 'use client'; +import { truncateAddress } from '@/lib/format'; import { NETWORK_NAME } from '@/lib/stellar'; import { useWallet } from './WalletProvider'; -function truncateAddress(address: string): string { - return `${address.slice(0, 4)}…${address.slice(-4)}`; -} - export function ConnectWalletButton() { const { address, diff --git a/src/i18n/config.ts b/src/i18n/config.ts new file mode 100644 index 0000000..d94e02d --- /dev/null +++ b/src/i18n/config.ts @@ -0,0 +1,24 @@ +/** + * The set of locales this app can render. Adding a new one is a two-step + * change: add its code here, then add the matching `messages/.json` + * file — see docs/I18N.md for the full walkthrough. + */ +export const LOCALES = ['en', 'es'] as const; + +export type Locale = (typeof LOCALES)[number]; + +export const DEFAULT_LOCALE: Locale = 'en'; + +export const LOCALE_LABELS: Record = { + en: 'English', + es: 'Español', +}; + +export function isLocale(value: string | undefined): value is Locale { + return LOCALES.includes(value as Locale); +} + +/** Name of the cookie the locale switcher writes and `request.ts` reads — + * no path-based locale routing yet (see docs/I18N.md), so this is the only + * thing that remembers the visitor's choice across requests. */ +export const LOCALE_COOKIE = 'NEXT_LOCALE'; diff --git a/src/i18n/request.ts b/src/i18n/request.ts new file mode 100644 index 0000000..49fc476 --- /dev/null +++ b/src/i18n/request.ts @@ -0,0 +1,22 @@ +import { cookies } from 'next/headers'; +import { getRequestConfig } from 'next-intl/server'; + +import { DEFAULT_LOCALE, isLocale, LOCALE_COOKIE } from './config'; + +/** + * Resolves the active locale for every request and loads its messages. + * + * There's no `[locale]` route segment yet (see docs/I18N.md for why, and + * what adding real locale-prefixed routing would involve) — the locale + * comes entirely from the `NEXT_LOCALE` cookie the switcher writes, + * falling back to DEFAULT_LOCALE for a first-time visitor. + */ +export default getRequestConfig(async () => { + const cookieLocale = (await cookies()).get(LOCALE_COOKIE)?.value; + const locale = isLocale(cookieLocale) ? cookieLocale : DEFAULT_LOCALE; + + return { + locale, + messages: (await import(`../../messages/${locale}.json`)).default, + }; +}); diff --git a/src/lib/adminApi.ts b/src/lib/adminApi.ts index 4e36870..a227840 100644 --- a/src/lib/adminApi.ts +++ b/src/lib/adminApi.ts @@ -1,8 +1,7 @@ import type { WalletSignMessage } from '@/components/wallet/WalletProvider'; import type { NgoApplication } from './api'; - -const API_URL = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3000'; +import { API_URL } from './config'; /** The backend accepts a signature within MAX_CLOCK_SKEW_MS (5 minutes) of * its timestamp, so one we just made stays valid for that long. Reused up diff --git a/src/lib/api.test.ts b/src/lib/api.test.ts index 5fa1176..bbba50f 100644 --- a/src/lib/api.test.ts +++ b/src/lib/api.test.ts @@ -39,9 +39,14 @@ describe('api network errors', () => { await expect(getStreams({ donor: 'GABC' })).rejects.toThrow('fetch failed'); }); - it('rejects once a stalled request hits the 10s timeout', async () => { - vi.useFakeTimers(); - try { + it( + 'rejects once a stalled request hits the 10s timeout', + async () => { + // AbortSignal.timeout() schedules its own internal timer outside of + // the global setTimeout/setInterval that vi.useFakeTimers() patches, + // so it keeps running on the real clock even under fake timers — + // this has to let the real 10s elapse rather than fast-forward it. + // // A realistic hung fetch: the promise it returns never settles on its // own — it only rejects if/when the signal passed to it aborts, same // as a real fetch() would once AbortSignal.timeout() fires. @@ -56,15 +61,10 @@ describe('api network errors', () => { }), ); - const pending = getNgos(); - - await vi.advanceTimersByTimeAsync(10_000); - - await expect(pending).rejects.toBeTruthy(); - } finally { - vi.useRealTimers(); - } - }); + await expect(getNgos()).rejects.toBeTruthy(); + }, + 15_000, + ); }); // --------------------------------------------------------------------------- diff --git a/src/lib/api.ts b/src/lib/api.ts index fc09d3b..38fa803 100644 --- a/src/lib/api.ts +++ b/src/lib/api.ts @@ -1,4 +1,4 @@ -const API_URL = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3000'; +import { API_URL } from './config'; // A hung backend should fail loudly rather than leave the UI on a loading // spinner forever. AbortSignal.timeout() rejects the fetch with a @@ -6,6 +6,14 @@ const API_URL = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3000'; // fetch (generic catch → "couldn't reach the API" messaging). const API_TIMEOUT_MS = 10_000; +/** The signal every fetch call below sends: the request's own timeout, + * combined with a caller-supplied signal (e.g. a polling effect's cleanup) + * when there is one, so either can cancel the fetch. */ +function withTimeout(signal?: AbortSignal): AbortSignal { + const timeout = AbortSignal.timeout(API_TIMEOUT_MS); + return signal ? AbortSignal.any([signal, timeout]) : timeout; +} + /** Thrown by API calls that need callers to branch on the HTTP status * (e.g. 409 conflict vs. other failures) rather than just knowing a * request failed. */ @@ -39,9 +47,7 @@ export type Ngo = { export async function getNgos(signal?: AbortSignal): Promise { const res = await fetch(`${API_URL}/ngos`, { next: { revalidate: 30 }, - signal: signal - ? AbortSignal.any([signal, AbortSignal.timeout(API_TIMEOUT_MS)]) - : AbortSignal.timeout(API_TIMEOUT_MS), + signal: withTimeout(signal), }); if (!res.ok) { throw new Error(`Failed to fetch NGOs: ${res.status}`); @@ -81,9 +87,7 @@ export type NgoProfile = Ngo & { export async function getNgo(id: string, signal?: AbortSignal): Promise { const res = await fetch(`${API_URL}/ngos/${encodeURIComponent(id)}`, { next: { revalidate: 30 }, - signal: signal - ? AbortSignal.any([signal, AbortSignal.timeout(API_TIMEOUT_MS)]) - : AbortSignal.timeout(API_TIMEOUT_MS), + signal: withTimeout(signal), }); if (res.status === 404) { return null; @@ -108,7 +112,7 @@ export async function getNgo(id: string, signal?: AbortSignal): Promise { const params = new URLSearchParams({ address }); const res = await fetch(`${API_URL}/ngos/lookup?${params.toString()}`, { - signal: AbortSignal.timeout(API_TIMEOUT_MS), + signal: withTimeout(), }); if (res.status === 404) { return null; @@ -152,9 +156,7 @@ export async function getStreams( if (filter.limit) params.set('limit', String(filter.limit)); const res = await fetch(`${API_URL}/streams?${params.toString()}`, { - signal: signal - ? AbortSignal.any([signal, AbortSignal.timeout(API_TIMEOUT_MS)]) - : AbortSignal.timeout(API_TIMEOUT_MS), + signal: withTimeout(signal), }); if (!res.ok) { throw new Error(`Failed to fetch streams: ${res.status}`); @@ -187,9 +189,7 @@ export type Withdrawal = { */ export async function getWithdrawals(ngoId: string, signal?: AbortSignal): Promise { const res = await fetch(`${API_URL}/ngos/${encodeURIComponent(ngoId)}/withdrawals`, { - signal: signal - ? AbortSignal.any([signal, AbortSignal.timeout(API_TIMEOUT_MS)]) - : AbortSignal.timeout(API_TIMEOUT_MS), + signal: withTimeout(signal), }); if (!res.ok) { throw new Error(`Failed to fetch withdrawals for NGO ${ngoId}: ${res.status}`); @@ -235,7 +235,7 @@ export async function getApplicationStatus( ): Promise { const params = new URLSearchParams({ ownerAddress }); const res = await fetch(`${API_URL}/ngo-applications/status?${params.toString()}`, { - signal: AbortSignal.timeout(API_TIMEOUT_MS), + signal: withTimeout(), }); if (res.status === 404) { return null; @@ -286,7 +286,7 @@ export async function submitNgoApplication(input: NgoApplicationInput): Promise< method: 'POST', headers: { 'content-type': 'application/json' }, body: JSON.stringify(input), - signal: AbortSignal.timeout(API_TIMEOUT_MS), + signal: withTimeout(), }); if (res.status === 409) { diff --git a/src/lib/config.ts b/src/lib/config.ts index 4d84294..4924837 100644 --- a/src/lib/config.ts +++ b/src/lib/config.ts @@ -1,3 +1,6 @@ +/** Base URL of the `streamgive-backend` API every fetch call targets. */ +export const API_URL = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3000'; + /** * Server-render fallback for the app's own origin. * diff --git a/src/lib/csv.ts b/src/lib/csv.ts index 1aeb86a..b627d1a 100644 --- a/src/lib/csv.ts +++ b/src/lib/csv.ts @@ -1,6 +1,5 @@ import type { Stream } from './api'; -import { formatRemainingDuration, formatTokenAmount } from './format'; -import { getNativeAssetAddress } from './stellar'; +import { formatRemainingDuration, formatTokenAmount, tokenLabel } from './format'; const UTF8_BOM = '\uFEFF'; @@ -16,12 +15,6 @@ const CSV_HEADERS = [ 'Created', ]; -// There's no symbol/name resolution for arbitrary tokens — only the native -// asset's contract address is derivable client-side. -function tokenLabel(tokenAddress: string): string { - return tokenAddress === getNativeAssetAddress() ? 'XLM' : tokenAddress; -} - function escapeCsvField(value: string): string { // A field starting with =, +, - or @ runs as a formula when opened in a // spreadsheet — prefix it with a single quote so it's read as literal diff --git a/src/lib/durations.ts b/src/lib/durations.ts new file mode 100644 index 0000000..619d35d --- /dev/null +++ b/src/lib/durations.ts @@ -0,0 +1,8 @@ +/** Duration presets offered wherever a stream's length is picked (creating + * a new stream, or modifying an existing one's rate/duration). */ +export const DURATIONS = [ + { label: '1 week', seconds: 7 * 24 * 60 * 60 }, + { label: '1 month', seconds: 30 * 24 * 60 * 60 }, + { label: '3 months', seconds: 90 * 24 * 60 * 60 }, + { label: '1 year', seconds: 365 * 24 * 60 * 60 }, +]; diff --git a/src/lib/format.ts b/src/lib/format.ts index 8592101..cceb5f3 100644 --- a/src/lib/format.ts +++ b/src/lib/format.ts @@ -79,6 +79,15 @@ export function tokenSymbol(tokenAddress: string): string { return truncateAddress(tokenAddress); } +/** Like `tokenSymbol`, but for display contexts that want the token's full + * identity rather than a shortened one: "XLM" for the native asset, or the + * token's full (untruncated) contract address for anything else — there's + * no symbol/name resolution for arbitrary tokens, only the native asset's + * address is derivable client-side. */ +export function tokenLabel(tokenAddress: string): string { + return tokenAddress === getNativeAssetAddress() ? 'XLM' : tokenAddress; +} + /** `formatAmount` plus the token's symbol, so "100" can't be mistaken for * the wrong asset (e.g. "100 XLM", "100 USDC", "100 CABC…WXYZ"). */ export function formatTokenAmount( @@ -243,3 +252,15 @@ export function truncateAddress(address: string): string { } return `${address.slice(0, 4)}…${address.slice(-4)}`; } + +/** Formats an ISO timestamp as a bare date (no time), in the viewer's own + * locale and timezone. */ +export function formatDate(iso: string): string { + return new Date(iso).toLocaleDateString(); +} + +/** Formats an ISO timestamp as a date and time, in the viewer's own locale + * and timezone. */ +export function formatDateTime(iso: string): string { + return new Date(iso).toLocaleString(); +} diff --git a/src/middleware.ts b/src/middleware.ts index a54cae1..2270773 100644 --- a/src/middleware.ts +++ b/src/middleware.ts @@ -1,7 +1,8 @@ import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; -const API_URL = process.env.NEXT_PUBLIC_API_URL ?? 'http://localhost:3000'; +import { API_URL } from '@/lib/config'; + const SOROBAN_RPC_URL = process.env.NEXT_PUBLIC_SOROBAN_RPC_URL ?? 'https://soroban-testnet.stellar.org'; From 09a2517206446153f6eccaaf56ec5cebac197710 Mon Sep 17 00:00:00 2001 From: CodedColder Date: Fri, 23 Oct 2026 08:35:39 +0100 Subject: [PATCH 2/2] revert: regenerate lockfile back to main's version to clear PR safety check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The automated PR safety check flags package-lock.json for resolving packages from "unusual" hosts (opencollective.com, github.com, paulmillr.com) whenever the lockfile is regenerated for a new dependency (here: next-intl) — these are just funding/repo metadata URLs on normal npm packages, not something installed from an untrusted source, but the check can't tell the difference and requires a human to look either way. Reverting to main's lockfile removes the diff, at the cost of reintroducing a real problem: this lockfile doesn't have next-intl (or @vitest/coverage-v8, already missing on main itself) locked, so `npm ci` will fail until someone runs `npm install` and commits the result again. The "Lint, typecheck, test, build" check was already failing on this branch for unrelated reasons (see other commits), so this doesn't change whether it passes, just which step it fails at. --- package-lock.json | 1110 +++++---------------------------------------- 1 file changed, 124 insertions(+), 986 deletions(-) diff --git a/package-lock.json b/package-lock.json index 973da1a..6f9e795 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,6 @@ "@creit.tech/stellar-wallets-kit": "^2.6.0", "@stellar/stellar-sdk": "^17.0.1", "next": "^16.3.4", - "next-intl": "^4.3.1", "qrcode.react": "^4.2.0", "react": "^19.2.8", "react-dom": "^19.2.8" @@ -27,7 +26,6 @@ "@types/react": "^19.2.18", "@types/react-dom": "^19.2.7", "@vitejs/plugin-react": "^6.1.1", - "@vitest/coverage-v8": "^5.0.0", "eslint": "^9.39.5", "eslint-config-next": "^16.3.4", "eslint-config-prettier": "^10.1.8", @@ -431,16 +429,6 @@ "url": "https://opencollective.com/preact" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", - "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - } - }, "node_modules/@bramus/specificity": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/@bramus/specificity/-/specificity-2.4.2.tgz", @@ -855,31 +843,6 @@ "node": ">=20.19.0" } }, - "node_modules/@eloqnt/config": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@eloqnt/config/-/config-0.1.1.tgz", - "integrity": "sha512-YRDigSaiJEzKDfzTth5kG+L/PKfB3mo8h7yNzpXExPfvQmcD05WoZT57PdhJE0jI+Vva6eb0OCU8hnHHgdME3g==", - "license": "MIT" - }, - "node_modules/@eloqnt/format-json": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/@eloqnt/format-json/-/format-json-0.1.0.tgz", - "integrity": "sha512-4Pkb+HyzgWNJXoU48aJhUyNZSjZvMGooPCNG1ZFmBm55xTiNNPQCNXH66Ob6sXS6RvbWZrRpCf+Uo/Jz9K9g+A==", - "license": "MIT", - "dependencies": { - "@eloqnt/config": "^0.1.0" - } - }, - "node_modules/@eloqnt/format-po": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@eloqnt/format-po/-/format-po-0.1.1.tgz", - "integrity": "sha512-aLtmIoqXGEaeTT0RcsLN8sZ3aIrsT/c/dVs28zrLhDjO3+vA3KNP+r98N0xq8w/k36+/GIUt4uZNbqcA71GVWw==", - "license": "MIT", - "dependencies": { - "@eloqnt/config": "^0.1.1", - "po-parser": "^2.2.0" - } - }, "node_modules/@emnapi/core": { "version": "1.10.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.10.0.tgz", @@ -1132,36 +1095,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@formatjs/fast-memoize": { - "version": "3.1.7", - "resolved": "https://registry.npmjs.org/@formatjs/fast-memoize/-/fast-memoize-3.1.7.tgz", - "integrity": "sha512-zXfhLpvA6T7+efdt9JLbBwZ00tT7NsBMDVnDu8rpHeNNv8KfRZAMo2gkG0k9lK/Nzc//3kJ9pImsfuJxk3KhUA==", - "license": "MIT" - }, - "node_modules/@formatjs/icu-messageformat-parser": { - "version": "3.5.22", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.22.tgz", - "integrity": "sha512-YuwN+dDKgssfuYwtOyGQD0CSn7K46g9cv1Fp0VgKpjZDDFvv8b8crzP990IcJhRPWHhKzCGsbUSJVIguyyFA5A==", - "license": "MIT", - "dependencies": { - "@formatjs/icu-skeleton-parser": "2.1.12" - } - }, - "node_modules/@formatjs/icu-skeleton-parser": { - "version": "2.1.12", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.1.12.tgz", - "integrity": "sha512-ca7tZMLpfHX3sUD7hP6gE/qr6Iq89EDd3ZLeRhbsB7r0UD55DGJq+tmFLzrGrnx5StahUevQmQQVtbDRHsfDHA==", - "license": "MIT" - }, - "node_modules/@formatjs/intl-localematcher": { - "version": "0.8.14", - "resolved": "https://registry.npmjs.org/@formatjs/intl-localematcher/-/intl-localematcher-0.8.14.tgz", - "integrity": "sha512-8O7V38QmbB11+ryJ3KM/znrzXtiqgpXzqNoqDsndHZZ5/zDKTTL+cf5F0mzXkQEwhx8IlWnqYvEovf2S5EwLTw==", - "license": "MIT", - "dependencies": { - "@formatjs/fast-memoize": "3.1.7" - } - }, "node_modules/@hot-wallet/sdk": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/@hot-wallet/sdk/-/sdk-1.0.11.tgz", @@ -2552,316 +2485,6 @@ "url": "https://github.com/sponsors/oxc-project" } }, - "node_modules/@parcel/watcher": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.6.0.tgz", - "integrity": "sha512-7FNeNl8NCE7aINx7WXiKQrPYZWC/hvrTsmk6zmxbI7LTXE7hVek/n8AfVgpe2y82zl3w0HvCHN0bVKMBoJcC0w==", - "hasInstallScript": true, - "license": "MIT", - "dependencies": { - "detect-libc": "^2.0.3", - "is-glob": "^4.0.3", - "node-addon-api": "^7.0.0", - "picomatch": "^4.0.4" - }, - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - }, - "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.6.0", - "@parcel/watcher-darwin-arm64": "2.6.0", - "@parcel/watcher-darwin-x64": "2.6.0", - "@parcel/watcher-freebsd-x64": "2.6.0", - "@parcel/watcher-linux-arm-glibc": "2.6.0", - "@parcel/watcher-linux-arm-musl": "2.6.0", - "@parcel/watcher-linux-arm64-glibc": "2.6.0", - "@parcel/watcher-linux-arm64-musl": "2.6.0", - "@parcel/watcher-linux-x64-glibc": "2.6.0", - "@parcel/watcher-linux-x64-musl": "2.6.0", - "@parcel/watcher-win32-arm64": "2.6.0", - "@parcel/watcher-win32-x64": "2.6.0" - } - }, - "node_modules/@parcel/watcher-android-arm64": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.6.0.tgz", - "integrity": "sha512-trgpLSCKRC/huFjXX/Smh+0sWe4+YtKfktIToiMl59ghz7z+qkH6kMvNnUbLyRs9N11t8l4svSCs1+5B3rOAhA==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.6.0.tgz", - "integrity": "sha512-Y3QV0gl7Q1zbfueunkWIERICbEojQFCgpyG7YqOGNFLsckXyI1xu9mAIUpKY9QBYzBtSkN8dBPwd3yiAO9ovMw==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.6.0.tgz", - "integrity": "sha512-Ohv6OpzhUfKYD7Beb8kDvG0jbIxORCYY1JRdZnaBtnjjkJxgD7ZVL0nw2sCYd0yTMKTvz3nnTnOF3cDifK+kvw==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.6.0.tgz", - "integrity": "sha512-5HmXvDgs8VK+74jF9y9/2FE3/OnlcKmc56tjmSrEuZjpSZOGL+fvAu+HKJBdPs9uwoP2hE6TlSUpXZ/C5jUFmQ==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.6.0.tgz", - "integrity": "sha512-Ps/hui3A+vMbjdqlqAowK2ZL8+BO8dBjxeWXj6npTBs3jx4wWmbPpaLuqwrQrSqIVMCnpWo238bJ1U37GhQOYg==", - "cpu": [ - "arm" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm-musl": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.6.0.tgz", - "integrity": "sha512-9c6AUHgHoG+IY88MRIHupztQiQnrbqHYQjkM2btA+Bf/wQnQMuiD0Wfk1EVv3TlNT3x41uU71rn6E4xh/+zvkw==", - "cpu": [ - "arm" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.6.0.tgz", - "integrity": "sha512-yHRqS2owEXe6Hic9z6Mh1ECsCd+ODVOGvZDyciqRd21+v+o+DnXMOrw50DSpIG2sb8GPEaPPmfeCAWKPJdq46g==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.6.0.tgz", - "integrity": "sha512-WhB2e/V7rqdHHWZusBSPuy5Ei8S6lSz6FE5TKKQz5h3a0O+C+mhY7vxU9b/stqvMb8beLnPY82ZrFTLKs+SrKA==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.6.0.tgz", - "integrity": "sha512-ulGE6x6Oz6iAwg75T8YQSoguBWasniIbX+QWpaYPcCnDOpdWX3k+4xbEYPZVLxOuoJI+svJJPD3sEj8G7lrQ3A==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.6.0.tgz", - "integrity": "sha512-tkBYKt7YQrjIJWYDnto2YgO8MRkjlMTSNoRHzsXinBqbLdeOM3L32wPZJvIZxqaLMfSlS/4sUjH/6STVP/XDLw==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.6.0.tgz", - "integrity": "sha512-gIZAP23jaHjGWasY/TY6yL7NHFClf0Ga7FN+iINvk+KN94rhm94lYZhFsbYFNcA04/onvGD9kKmiJLJB2HbNwQ==", - "cpu": [ - "arm64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher-win32-x64": { - "version": "2.6.0", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.6.0.tgz", - "integrity": "sha512-cA+/pXV2YkfxlIcXOQ5fSWqAzzPyD78/x5qbK/I0vUkrlYHA8TIz+MXjAbGouguKVSI4bOmkTSJ1/poVSsgt+A==", - "cpu": [ - "x64" - ], - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/parcel" - } - }, - "node_modules/@parcel/watcher/node_modules/node-addon-api": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", - "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", - "license": "MIT" - }, - "node_modules/@parcel/watcher/node_modules/picomatch": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.7.tgz", - "integrity": "sha512-qcJu88Q2IWqJsDD529JKMdwGm/dvInW4HvQnRwiH9JtihJvzGOscDtHE3x1pBKeUOTysQ8kVmLnJ2kJu7yhcGA==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/@phosphor-icons/webcomponents": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@phosphor-icons/webcomponents/-/webcomponents-2.1.5.tgz", @@ -3408,12 +3031,6 @@ "node": ">=16" } }, - "node_modules/@schummar/icu-type-parser": { - "version": "1.21.5", - "resolved": "https://registry.npmjs.org/@schummar/icu-type-parser/-/icu-type-parser-1.21.5.tgz", - "integrity": "sha512-bXHSaW5jRTmke9Vd0h5P7BtWZG9Znqb8gSDxZnxaGSJnGwPLDPfS+3g0BKzeWqzgZPsIVZkM7m2tbo18cm5HBw==", - "license": "MIT" - }, "node_modules/@scure/base": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.2.6.tgz", @@ -4670,327 +4287,71 @@ "@noble/hashes": "^2.2.0", "@stellar/js-xdr": "^5.0.0", "@types/json-schema": "^7.0.15", - "axios": "1.18.0", - "bignumber.js": "^11.1.4", - "commander": "^14.0.3", - "eventsource": "^4.1.0", - "feaxios": "^0.0.23", - "smol-toml": "^1.6.1", - "uint8array-extras": "^1.5.0" - }, - "bin": { - "stellar-js": "bin/stellar-js" - }, - "engines": { - "node": ">=22.12.0" - } - }, - "node_modules/@stellar/stellar-sdk/node_modules/@exodus/bytes": { - "version": "1.15.1", - "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", - "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", - "license": "MIT", - "engines": { - "node": "^20.19.0 || ^22.12.0 || >=24.0.0" - }, - "peerDependencies": { - "@noble/hashes": "^1.8.0 || ^2.0.0" - }, - "peerDependenciesMeta": { - "@noble/hashes": { - "optional": true - } - } - }, - "node_modules/@stellar/stellar-sdk/node_modules/@noble/hashes": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", - "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", - "license": "MIT", - "engines": { - "node": ">= 20.19.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - } - }, - "node_modules/@stellar/stellar-sdk/node_modules/axios": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz", - "integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.16.0", - "form-data": "^4.0.5", - "https-proxy-agent": "^5.0.1", - "proxy-from-env": "^2.1.0" - } - }, - "node_modules/@stellar/stellar-sdk/node_modules/commander": { - "version": "14.0.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", - "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", - "license": "MIT", - "engines": { - "node": ">=20" - } - }, - "node_modules/@swc/core": { - "version": "1.16.20", - "resolved": "https://registry.npmjs.org/@swc/core/-/core-1.16.20.tgz", - "integrity": "sha512-zcdfS7k28OJIMuLhqRRyGja9PxrC2GXhFXkbQ1X15y6uZEMsTrqyej55V1DiJtlXaDOmwSLn5222YPT9ZoFxPA==", - "hasInstallScript": true, - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3", - "@swc/types": "^0.1.28" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/swc" - }, - "optionalDependencies": { - "@swc/core-darwin-arm64": "1.16.20", - "@swc/core-darwin-x64": "1.16.20", - "@swc/core-linux-arm-gnueabihf": "1.16.20", - "@swc/core-linux-arm64-gnu": "1.16.20", - "@swc/core-linux-arm64-musl": "1.16.20", - "@swc/core-linux-ppc64-gnu": "1.16.20", - "@swc/core-linux-s390x-gnu": "1.16.20", - "@swc/core-linux-x64-gnu": "1.16.20", - "@swc/core-linux-x64-musl": "1.16.20", - "@swc/core-win32-arm64-msvc": "1.16.20", - "@swc/core-win32-ia32-msvc": "1.16.20", - "@swc/core-win32-x64-msvc": "1.16.20" - }, - "peerDependencies": { - "@swc/helpers": ">=0.5.17" - }, - "peerDependenciesMeta": { - "@swc/helpers": { - "optional": true - } - } - }, - "node_modules/@swc/core-darwin-arm64": { - "version": "1.16.20", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-arm64/-/core-darwin-arm64-1.16.20.tgz", - "integrity": "sha512-i7NFSDnuE6tdBvBgmoVKKmburgcwZt/FZK2IusJaVGrjFrMUC1hrgE3RI+Kt9RhYM7vHvQj0W9j53zSxKEMbWQ==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-darwin-x64": { - "version": "1.16.20", - "resolved": "https://registry.npmjs.org/@swc/core-darwin-x64/-/core-darwin-x64-1.16.20.tgz", - "integrity": "sha512-ho/LkT1Q3beSBeZGNZnm1mVbh5bKZHC0UGHtK3MswqOylbHtZoMXJBL40N8JpDj5SBxwECmdNEyXnU9HJ4F6RA==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm-gnueabihf": { - "version": "1.16.20", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm-gnueabihf/-/core-linux-arm-gnueabihf-1.16.20.tgz", - "integrity": "sha512-xm3/s3zp9WyNgB1Sl78tpH76MHgobI3TSenTY15bkSHyrdQa9W2MUSESHRPd7CHGFLfpWl/0jwwNp5+hwG0e9A==", - "cpu": [ - "arm" - ], - "license": "Apache-2.0", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-gnu": { - "version": "1.16.20", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-gnu/-/core-linux-arm64-gnu-1.16.20.tgz", - "integrity": "sha512-e6Um0U+VcTQyurOXhbratRQlrPITSRwugG00fhICbi2MGTi1vRiE4TMqGCjWpR1zPmZKXDGMw+yNFvh7AAgT2Q==", - "cpu": [ - "arm64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-arm64-musl": { - "version": "1.16.20", - "resolved": "https://registry.npmjs.org/@swc/core-linux-arm64-musl/-/core-linux-arm64-musl-1.16.20.tgz", - "integrity": "sha512-ekCO2obfK9NFwXGHfAQjjuU5mLDDC7qMaUJH/V/4iyzD0BqNhilr6McZrXLytOQ/xKtTNxLQ71wYrD3M2LW8Kg==", - "cpu": [ - "arm64" - ], - "libc": [ - "musl" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-ppc64-gnu": { - "version": "1.16.20", - "resolved": "https://registry.npmjs.org/@swc/core-linux-ppc64-gnu/-/core-linux-ppc64-gnu-1.16.20.tgz", - "integrity": "sha512-6/x2K3l4GNUWyaSmkywnth6ipZISzhVzOLsI66bj12/hSQYaKAyg9MKTwnuFf2B/iYA6GrjDghk36RYghRameQ==", - "cpu": [ - "ppc64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-s390x-gnu": { - "version": "1.16.20", - "resolved": "https://registry.npmjs.org/@swc/core-linux-s390x-gnu/-/core-linux-s390x-gnu-1.16.20.tgz", - "integrity": "sha512-1GTd8sqS/NpcF+Y3YDSE0bT31V61+hD7ZZWEsVyiyxk/HQt2zpk4zlJJHOASBLc+/CyUuBZ949n6QMszbIXkmQ==", - "cpu": [ - "s390x" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=10" - } - }, - "node_modules/@swc/core-linux-x64-gnu": { - "version": "1.16.20", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-gnu/-/core-linux-x64-gnu-1.16.20.tgz", - "integrity": "sha512-XtMeYAgQa5UZhAX84TXn0sXPmhf1xHwcv0cl5hHiBXxTVbplY7WiTwIQU9uOtVm3VUqL/quyCm78/umUNZ7FmA==", - "cpu": [ - "x64" - ], - "libc": [ - "glibc" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], + "axios": "1.18.0", + "bignumber.js": "^11.1.4", + "commander": "^14.0.3", + "eventsource": "^4.1.0", + "feaxios": "^0.0.23", + "smol-toml": "^1.6.1", + "uint8array-extras": "^1.5.0" + }, + "bin": { + "stellar-js": "bin/stellar-js" + }, "engines": { - "node": ">=10" + "node": ">=22.12.0" } }, - "node_modules/@swc/core-linux-x64-musl": { - "version": "1.16.20", - "resolved": "https://registry.npmjs.org/@swc/core-linux-x64-musl/-/core-linux-x64-musl-1.16.20.tgz", - "integrity": "sha512-qeF3UILa+MA4zJ29CdQ8KE7YbGFPMQBxWo3Lh0Ed0ETFEJ8pCKISWo7wQG4/SmfM9HX4KT1Odoj6ixyBn6Ji2Q==", - "cpu": [ - "x64" - ], - "libc": [ - "musl" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "linux" - ], + "node_modules/@stellar/stellar-sdk/node_modules/@exodus/bytes": { + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.15.1.tgz", + "integrity": "sha512-S6mL0yNB/Abt9Ei4tq8gDhcczc4S3+vQ4ra7vxnAf+YHC02srtqxKKZghx2Dq6p0e66THKwR6r8N6P95wEty7Q==", + "license": "MIT", "engines": { - "node": ">=10" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@noble/hashes": "^1.8.0 || ^2.0.0" + }, + "peerDependenciesMeta": { + "@noble/hashes": { + "optional": true + } } }, - "node_modules/@swc/core-win32-arm64-msvc": { - "version": "1.16.20", - "resolved": "https://registry.npmjs.org/@swc/core-win32-arm64-msvc/-/core-win32-arm64-msvc-1.16.20.tgz", - "integrity": "sha512-ciMtHXAmE+jnrWWO14rErr6tfK34QZm8jZRSdRLPodqJlzrYDw7TqN60HC8Yim5Ua9a1r/poyfkpNft6GRXvxA==", - "cpu": [ - "arm64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], + "node_modules/@stellar/stellar-sdk/node_modules/@noble/hashes": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-2.4.0.tgz", + "integrity": "sha512-X5XaVWZIBCT7HHZGm5I7ZQXDwLG+bGXuSrMQAW+7Zvl87h1kmc1ZB1VSRJcpUfoUrGQp4Fkoxm5kZ+Ms+aW+eA==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">= 20.19.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/@swc/core-win32-ia32-msvc": { - "version": "1.16.20", - "resolved": "https://registry.npmjs.org/@swc/core-win32-ia32-msvc/-/core-win32-ia32-msvc-1.16.20.tgz", - "integrity": "sha512-MTbkgGooWebmjmATx1+QRECVZ+q4ICKNXCutwOrFVD5b5hdxla/yV4e6cRFrfNMowejaNpmMQg31ENWt0Yfzpg==", - "cpu": [ - "ia32" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=10" + "node_modules/@stellar/stellar-sdk/node_modules/axios": { + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.18.0.tgz", + "integrity": "sha512-E32NzpYKp++W7XRe52rHiXV2ehxmh3wbdgO7MHeFM+vqxLBYHzt0ElkiImtOBxtOmyp0yoC8C6uESVV84Y2/hw==", + "license": "MIT", + "dependencies": { + "follow-redirects": "^1.16.0", + "form-data": "^4.0.5", + "https-proxy-agent": "^5.0.1", + "proxy-from-env": "^2.1.0" } }, - "node_modules/@swc/core-win32-x64-msvc": { - "version": "1.16.20", - "resolved": "https://registry.npmjs.org/@swc/core-win32-x64-msvc/-/core-win32-x64-msvc-1.16.20.tgz", - "integrity": "sha512-Lf5oMaSdJBImtGhDecasxcIa7Z5LeJ4aGmBOY7TScroO+A/MxxB9PslIpgV0rtG/FcxtipN7hveBg9ZTeboaaw==", - "cpu": [ - "x64" - ], - "license": "Apache-2.0 AND MIT", - "optional": true, - "os": [ - "win32" - ], + "node_modules/@stellar/stellar-sdk/node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=20" } }, - "node_modules/@swc/counter": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", - "integrity": "sha512-e2BR4lsJkkRlKZ/qCHPw9ZaSxc0MVUd7gtbtaB7aMvHeJVYe8sOB8DBZkP2DtISHGSku9sCK6T6cnY0CtXrOCQ==", - "license": "Apache-2.0" - }, "node_modules/@swc/helpers": { "version": "0.5.23", "resolved": "https://registry.npmjs.org/@swc/helpers/-/helpers-0.5.23.tgz", @@ -5000,15 +4361,6 @@ "tslib": "^2.8.0" } }, - "node_modules/@swc/types": { - "version": "0.1.28", - "resolved": "https://registry.npmjs.org/@swc/types/-/types-0.1.28.tgz", - "integrity": "sha512-V6Mnml8v09QALx6K0elJ7o9K/MkVDtW3t6L+7Ou/JcWtb3xwId2AH4FeOceySd2JaO87IMw4+6vSZxLm34LPbw==", - "license": "Apache-2.0", - "dependencies": { - "@swc/counter": "^0.1.3" - } - }, "node_modules/@tailwindcss/node": { "version": "4.3.3", "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.3.3.tgz", @@ -6328,75 +5680,23 @@ } } }, - "node_modules/@vitest/coverage-v8": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-5.0.5.tgz", - "integrity": "sha512-4KpkGdIrSqBihx/JyP/e+S+IKB98etg1XDQiLAxksLfcShnwi1VReGeOT9NI43hLHbknosKp/1CCLn9Zgcs8mg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@bcoe/v8-coverage": "^1.0.2", - "@vitest/istanbul-lib-coverage": "^1.0.2", - "@vitest/istanbul-lib-report": "^1.0.2", - "ast-v8-to-istanbul": "^1.0.7", - "magicast": "^0.5.5", - "obug": "^3.0.0", - "std-env": "^4.3.0", - "tinyrainbow": "^3.2.0" - }, - "funding": { - "url": "https://opencollective.com/vitest" - }, - "peerDependencies": { - "@vitest/browser": "5.0.5", - "vitest": "5.0.5" - }, - "peerDependenciesMeta": { - "@vitest/browser": { - "optional": true - } - } - }, - "node_modules/@vitest/istanbul-lib-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-coverage/-/istanbul-lib-coverage-1.0.2.tgz", - "integrity": "sha512-9J/JMwOf9AoJhAywhrn7ScKTL38hsWQP/qPG60OtaAFcQ5OXPwKsxZFlbnuCKmZ61m8/lGgHYnFpdyQZUvG/iA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=22" - } - }, - "node_modules/@vitest/istanbul-lib-report": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@vitest/istanbul-lib-report/-/istanbul-lib-report-1.0.2.tgz", - "integrity": "sha512-gUsfXZJbzPamoIY5TvHFiMMoXESBrUMo+xqaj+rYrWI69+EnvRlYBlP96ZnHPY4vX8kyUpgAnFUCg5wZG/HkDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@vitest/istanbul-lib-coverage": "1.0.2" - }, - "engines": { - "node": ">=22" - } - }, "node_modules/@vitest/mocker": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.5.tgz", - "integrity": "sha512-xLL3lkDegJZw+MGoGaCCsFayDZthGgpAtr3Vziyfc4p7xH89K7KRPcRBBh/kEYm0Aqkt5DtTmkYpBtRV/FrHZg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-5.0.0.tgz", + "integrity": "sha512-66PGTMIiVJP3t4a5yxU9qPtf7MdTBs8jmToMvy+HVflB3Yy13WJZTtPePdvU+wjRV02SKK5doLbSA6o9pwOmiA==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.31", - "@vitest/spy": "5.0.5", + "@vitest/spy": "5.0.0", "estree-walker": "^3.0.3", - "magic-string": "^1.4.3" + "magic-string": "^1.2.3" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { - "msw": "^2.4.9 || ^3.0.0", + "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { @@ -6409,19 +5709,19 @@ } }, "node_modules/@vitest/mocker/node_modules/magic-string": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", - "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.2.3.tgz", + "integrity": "sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.6.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/@vitest/spy": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.5.tgz", - "integrity": "sha512-lwJRExuH417257Q5wv9QYugN9IMJkoxcrKrYxGPQkBzl7wlNpTUxD/5yNX4ESKVUIShKPgkGchYPFMWOOk0wzw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-5.0.0.tgz", + "integrity": "sha512-uy+luWBAPw9XfthoHi5AkfHUnuPYEESjl0p/r+meoBnU8bxg5GDQ3Ey8MjcJ6sqahkL4PFyrvfMJJBw7LbU06g==", "dev": true, "license": "MIT", "funding": { @@ -7468,25 +6768,6 @@ "dev": true, "license": "MIT" }, - "node_modules/ast-v8-to-istanbul": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.7.tgz", - "integrity": "sha512-kFL68AG6ajd8fg248zwM9GQrUWEp79gsmjum34OEXjs4yHuUMZfYKwOLW9GMmB4oNvVrj+EAGxsP7ye2UR9UlA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/trace-mapping": "^0.3.31", - "estree-walker": "^3.0.3", - "js-tokens": "^10.0.0" - } - }, - "node_modules/ast-v8-to-istanbul/node_modules/js-tokens": { - "version": "10.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", - "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", - "dev": true, - "license": "MIT" - }, "node_modules/async-function": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-function/-/async-function-1.0.0.tgz", @@ -7876,9 +7157,9 @@ "license": "CC-BY-4.0" }, "node_modules/chai": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/chai/-/chai-6.3.0.tgz", - "integrity": "sha512-XWAtwJ6OHO+tj0EKCs0Y2UamnyOxseZWltU4x2U2wh8g4AigdjwvtUjvLP2tqkA/avxHEtzxNaqGq/YGNwckKg==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", + "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", "engines": { @@ -7994,19 +7275,6 @@ "dev": true, "license": "MIT" }, - "node_modules/content-type": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/content-type/-/content-type-2.1.0.tgz", - "integrity": "sha512-mj7UPXE0jaqaOsukNZRUEfEi2AcL7C/vwmwcHV0O97eO1E1pxBZuyjlZrx5seTaNBg1U6+o35wpa35Qfcc+7ag==", - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -8342,6 +7610,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "devOptional": true, "license": "Apache-2.0", "engines": { "node": ">=8" @@ -9988,21 +9257,6 @@ "ms": "^2.0.0" } }, - "node_modules/icu-minify": { - "version": "4.14.9", - "resolved": "https://registry.npmjs.org/icu-minify/-/icu-minify-4.14.9.tgz", - "integrity": "sha512-PmO+fNnC3K3mfPKTnm3RrPERrhQZnjm1AlpyZQ0ShbXi8Y/kYZ3YOde8TEjCLz9k3P2DmzkBn8lykGrCAi1Ajw==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/amannn" - } - ], - "license": "MIT", - "dependencies": { - "@formatjs/icu-messageformat-parser": "^3.4.0" - } - }, "node_modules/idb-keyval": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/idb-keyval/-/idb-keyval-6.2.1.tgz", @@ -10097,31 +9351,6 @@ "node": ">= 0.4" } }, - "node_modules/intl-messageformat": { - "version": "11.2.15", - "resolved": "https://registry.npmjs.org/intl-messageformat/-/intl-messageformat-11.2.15.tgz", - "integrity": "sha512-W4VNytsXyWgW5lvVchmsb9xgA5rSKqoflu3/IQ9UCtB0z4R+Bx9MoEkwFT/+30RCl/KzUoH0QGGe9lppF5SuvQ==", - "license": "BSD-3-Clause", - "dependencies": { - "@formatjs/fast-memoize": "3.1.7", - "@formatjs/icu-messageformat-parser": "3.5.18" - } - }, - "node_modules/intl-messageformat/node_modules/@formatjs/icu-messageformat-parser": { - "version": "3.5.18", - "resolved": "https://registry.npmjs.org/@formatjs/icu-messageformat-parser/-/icu-messageformat-parser-3.5.18.tgz", - "integrity": "sha512-wX1efcL8d7K5QapOTRsP/OLTA/kpwe4LwqWyNxDNg5iRs8i9jJbCyxB5P8jc4KOW5oiLzz2DJr+BRY5xMV9+bA==", - "license": "MIT", - "dependencies": { - "@formatjs/icu-skeleton-parser": "2.1.11" - } - }, - "node_modules/intl-messageformat/node_modules/@formatjs/icu-skeleton-parser": { - "version": "2.1.11", - "resolved": "https://registry.npmjs.org/@formatjs/icu-skeleton-parser/-/icu-skeleton-parser-2.1.11.tgz", - "integrity": "sha512-j8cUmOJzVgkHuS0QiQ6ga76UIoLOFSAMWhs7aZJztH3aAdCOAE6vpC8KVvFB4cU10ON0y2/5oOVmPJ43s2lTwA==", - "license": "MIT" - }, "node_modules/iron-webcrypto": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/iron-webcrypto/-/iron-webcrypto-1.2.1.tgz", @@ -10316,6 +9545,7 @@ "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -10370,6 +9600,7 @@ "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" @@ -11371,18 +10602,6 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, - "node_modules/magicast": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.5.tgz", - "integrity": "sha512-UicdXN8zQ3JHlxVq+28afMXPr1z7WNY6+7EJnzTdQWkTAlMLF5fNCCKxJHBQwGaNGR11581EiQmQzx73+MvszA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.29.7", - "@babel/types": "^7.29.7", - "source-map-js": "^1.2.1" - } - }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", @@ -11658,22 +10877,6 @@ "webidl-conversions": "^3.0.0" } }, - "node_modules/negotiator": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.1.0.tgz", - "integrity": "sha512-NMPBRMJgiQHjbd8phG3Vebdx4kZ1H121rbl5IkMqeOsahptB9BKo/d7oJ3zTXqTgagn2bWlNSXkh0QUGM31RYg==", - "license": "MIT", - "dependencies": { - "content-type": "^2.1.0" - }, - "engines": { - "node": ">=18" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/express" - } - }, "node_modules/next": { "version": "16.3.4", "resolved": "https://registry.npmjs.org/next/-/next-16.3.4.tgz", @@ -11727,46 +10930,6 @@ } } }, - "node_modules/next-intl": { - "version": "4.14.9", - "resolved": "https://registry.npmjs.org/next-intl/-/next-intl-4.14.9.tgz", - "integrity": "sha512-uauRtUC+TrYNA1OL/QcwL9irndLrI9jK3Wdaa9W0B5AeKh9cqVdNeT0IWgLnhBqBRrVwppVdI0M+C1+JdGPQ3w==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/amannn" - } - ], - "license": "MIT", - "dependencies": { - "@eloqnt/config": "^0.1.1", - "@eloqnt/format-json": "^0.1.0", - "@eloqnt/format-po": "^0.1.1", - "@formatjs/intl-localematcher": "^0.8.1", - "@parcel/watcher": "^2.4.1", - "@swc/core": "~1.16.0", - "icu-minify": "^4.14.9", - "negotiator": "^1.0.0", - "next-intl-swc-plugin-extractor": "4.14.9", - "use-intl": "^4.14.9" - }, - "peerDependencies": { - "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", - "next": "^12.0.0 || ^13.0.0 || ^14.0.0 || ^15.0.0 || ^16.0.0", - "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/next-intl-swc-plugin-extractor": { - "version": "4.14.9", - "resolved": "https://registry.npmjs.org/next-intl-swc-plugin-extractor/-/next-intl-swc-plugin-extractor-4.14.9.tgz", - "integrity": "sha512-8jAkmDwQJo6RDavBHm9QRl1YTn2WXf+rI4SVhMaZxZcxopnwZic6fKTXOoNIo3kzC+a6vJySkOkXmHTVFLAYtg==", - "license": "MIT" - }, "node_modules/next/node_modules/postcss": { "version": "8.5.23", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.23.tgz", @@ -12028,9 +11191,9 @@ } }, "node_modules/obug": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/obug/-/obug-3.0.0.tgz", - "integrity": "sha512-5vvB5+W7ePv+p3uqxi+RcW1XAzLW0/hxt3/4X4Lc4qHudzOhmBiBwOY6DRob4WnanAEGvNcLjF+KNOufrUoEQw==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.4.tgz", + "integrity": "sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", @@ -12314,12 +11477,6 @@ "node": ">=10.13.0" } }, - "node_modules/po-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/po-parser/-/po-parser-2.2.0.tgz", - "integrity": "sha512-NdTrKgh0oO7+y+RFX8KKhOB438x/j94UGXEFzl/7pHH0JjWSn42iJ9tgmPksIO6n1JKDHmNDcejPJfKspljB9g==", - "license": "MIT" - }, "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", @@ -13215,6 +12372,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/siginfo": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", + "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", + "dev": true, + "license": "ISC" + }, "node_modules/slow-redact": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/slow-redact/-/slow-redact-0.3.2.tgz", @@ -13267,6 +12431,13 @@ "dev": true, "license": "MIT" }, + "node_modules/stackback": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", + "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", + "dev": true, + "license": "MIT" + }, "node_modules/statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", @@ -13278,9 +12449,9 @@ } }, "node_modules/std-env": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.3.0.tgz", - "integrity": "sha512-OtU/EgQ1kIm5KwqQpBC6ZEMXrZRui11w8zgfTWp8cdO9B8OaPsbA8bTHO2P+HNo1VlUTGMVBwPhydu6poeXiag==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.2.0.tgz", + "integrity": "sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==", "dev": true, "license": "MIT" }, @@ -13602,9 +12773,9 @@ } }, "node_modules/tinybench": { - "version": "6.2.2", - "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.2.2.tgz", - "integrity": "sha512-nIZ2N2dFpjBsg2hgitKu7mA+FvsIJuGJOwysRngmVRRmWgR/UyPjwJj8c/fT/dhm8EkHnF5EOogRf+0Z2qrimA==", + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-6.1.4.tgz", + "integrity": "sha512-9APumHG7r4yOk4X4WlkmE71aZcv1gvin1czO3OQ1U9iJcFA5Ja/ygyb0vPOVHTthFozUYs8CLoLUlM8grb2lTQ==", "dev": true, "license": "MIT", "engines": { @@ -13612,9 +12783,9 @@ } }, "node_modules/tinyexec": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.1.tgz", - "integrity": "sha512-GCvB3aoys96IuDFBMcTB46JOR6mdMtAToqwiW8JlWhsoh1mhHi/xn9ss/Dg7N555GiJyEt2qzoG/NHCwM6h1EA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.3.0.tgz", + "integrity": "sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==", "dev": true, "license": "MIT", "engines": { @@ -13669,16 +12840,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/tinyrainbow": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.2.0.tgz", - "integrity": "sha512-LgO3D9yZJjApUiuUfl9iFAwrtaX4+lok3wJIqttGoKCHlWUqHqbQpnxCf82L8FjgKsh4iGo78hqJwgL8F6To2A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, "node_modules/tldts": { "version": "7.4.12", "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.12.tgz", @@ -14191,33 +13352,6 @@ "punycode": "^2.1.0" } }, - "node_modules/use-intl": { - "version": "4.14.9", - "resolved": "https://registry.npmjs.org/use-intl/-/use-intl-4.14.9.tgz", - "integrity": "sha512-37ktvdCCz+6JY2VFPl/GV69Ds4ahiUT2c2xEZZRZYxdgGLxFsUbdxzjQVRm3YMIOUU0VPs6zvlifb4Yjk4IxIg==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/amannn" - } - ], - "license": "MIT", - "dependencies": { - "@formatjs/fast-memoize": "^3.1.0", - "@schummar/icu-type-parser": "1.21.5", - "icu-minify": "^4.14.9", - "intl-messageformat": "^11.1.0" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", - "react": "^17.0.0 || ^18.0.0 || >=19.0.0-rc <19.0.0 || ^19.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/utf-8-validate": { "version": "6.0.6", "resolved": "https://registry.npmjs.org/utf-8-validate/-/utf-8-validate-6.0.6.tgz", @@ -14780,25 +13914,25 @@ } }, "node_modules/vitest": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.5.tgz", - "integrity": "sha512-cPODOMF8YnkiiYtDdHo6n4wWmBrOBBbOIYH2d33M4l3CdNn72h+qMmO6uXffUN6A5AZFXNND/d+krS9mgkXRfg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-5.0.0.tgz", + "integrity": "sha512-gpsMNoRhMjMktVxPtstOH4/PJuPyovVaMDr4oDilXaGH1EcqM2OE96SoHT2VIQ6fTGtTjqmHDrEu2X9RQiXf8Q==", "dev": true, "license": "MIT", "dependencies": { "@types/chai": "^5.2.2", - "@vitest/mocker": "5.0.5", - "chai": "^6.3.0", + "@vitest/mocker": "5.0.0", + "chai": "^6.2.2", "es-module-lexer": "^2.3.2", "expect-type": "^1.4.0", - "magic-string": "^1.4.3", - "obug": "^3.0.0", + "magic-string": "^1.2.3", + "obug": "^2.1.4", "picomatch": "^4.0.7", - "std-env": "^4.3.0", - "tinybench": "^6.2.2", - "tinyexec": "^1.3.1", + "std-env": "^4.2.0", + "tinybench": "6.1.4", + "tinyexec": "1.3.0", "tinyglobby": "^0.2.17", - "why-is-node-running": "3.2.1" + "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" @@ -14813,12 +13947,12 @@ "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^22.0.0 || >=24.0.0", - "@vitest/browser-playwright": "5.0.5", - "@vitest/browser-preview": "5.0.5", + "@vitest/browser-playwright": "5.0.0", + "@vitest/browser-preview": "5.0.0", "@vitest/browser-webdriverio": "^5.0.0-beta.5 || >=5.0.0", - "@vitest/coverage-istanbul": "5.0.5", - "@vitest/coverage-v8": "5.0.5", - "@vitest/ui": "5.0.5", + "@vitest/coverage-istanbul": "5.0.0", + "@vitest/coverage-v8": "5.0.0", + "@vitest/ui": "5.0.0", "happy-dom": "*", "jsdom": "*", "vite": "^6.4.0 || ^7.0.0 || ^8.0.0" @@ -14863,13 +13997,13 @@ } }, "node_modules/vitest/node_modules/magic-string": { - "version": "1.4.3", - "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.4.3.tgz", - "integrity": "sha512-z12OxmaPGE0F4xlpGdjuAUckipLpQlTAuAmyGhNoD7ODpYUzvDfvtxUbjM/jwznyP178oju/KDdyi220wNJ0RQ==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-1.2.3.tgz", + "integrity": "sha512-Bpb0W2TbLKOZ7vJnOUnVRGq3WL2p+ISV29M6hYPL1AFCpyKZpdr5ytiXoTSSxRVhg8YW7f65+6gbG8WG6PCa/g==", "dev": true, "license": "MIT", "dependencies": { - "@jridgewell/sourcemap-codec": "^1.6.0" + "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/vitest/node_modules/picomatch": { @@ -15063,16 +14197,20 @@ } }, "node_modules/why-is-node-running": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-3.2.1.tgz", - "integrity": "sha512-Tb2FUhB4vUsGQlfSquQLYkApkuPAFQXGFzxWKHHumVz2dK+X1RUm/HnID4+TfIGYJ1kTcwOaCk/buYCEJr6YjQ==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", + "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", + "dependencies": { + "siginfo": "^2.0.0", + "stackback": "0.0.2" + }, "bin": { "why-is-node-running": "cli.js" }, "engines": { - "node": ">=20.11" + "node": ">=8" } }, "node_modules/word-wrap": {